diff --git a/scarf/Packages/ScarfIOS/Package.swift b/scarf/Packages/ScarfIOS/Package.swift index e656975..e2a22fa 100644 --- a/scarf/Packages/ScarfIOS/Package.swift +++ b/scarf/Packages/ScarfIOS/Package.swift @@ -32,11 +32,15 @@ let package = Package( ], dependencies: [ .package(path: "../ScarfCore"), - // Pinned to the 0.7 minor line until the API stabilizes at 1.0. - // When we bump, re-run onboarding smoke tests against at least: + // Pinned tight to the 0.12 minor line. Citadel pre-1.0 has + // changed its authentication-method variant names between + // minor versions (0.7 → 0.9 → 0.12) — letting the version + // float to 0.13+ without a code review risks a silent build + // break in `CitadelSSHService.buildClientSettings(...)`. When + // bumping the minor, smoke test onboarding against: // (a) a real host with 1Password SSH agent // (b) a real host with a hand-edited `authorized_keys` - .package(url: "https://github.com/orlandos-nl/Citadel", from: "0.7.0"), + .package(url: "https://github.com/orlandos-nl/Citadel", .upToNextMinor(from: "0.12.0")), ], targets: [ .target( diff --git a/scarf/Packages/ScarfIOS/Sources/ScarfIOS/CitadelSSHService.swift b/scarf/Packages/ScarfIOS/Sources/ScarfIOS/CitadelSSHService.swift index 02dd17a..4f93d00 100644 --- a/scarf/Packages/ScarfIOS/Sources/ScarfIOS/CitadelSSHService.swift +++ b/scarf/Packages/ScarfIOS/Sources/ScarfIOS/CitadelSSHService.swift @@ -20,14 +20,15 @@ import ScarfCore /// (file transport, SQLite snapshot pulls, ACP channel) will /// layer on top. /// -/// **Citadel API disclaimer.** Citadel 0.9's exact authentication- -/// method spelling for Ed25519 private keys is evolving across -/// 0.7 → 0.9. The `runOneShotProbe(...)` helper below is written -/// against the documented `SSHClientSettings` + `.privateKey(...)` -/// pattern; if Citadel has renamed or refactored that variant, -/// adjust `buildClientSettings(...)` — everything else (the retry -/// loop, the error classification, the exit-code handling) is -/// Citadel-version-independent. +/// **Citadel 0.12.1 API verified.** Every call below (`SSHAuthentication +/// Method.ed25519(username:privateKey:)`, `SSHClientSettings(host: +/// authenticationMethod:hostKeyValidator:)`, `SSHHostKeyValidator. +/// acceptAnything()`, `SSHClient.connect(to:)`, `client.executeCommand +/// (_:)`, `client.close()`) was cross-checked against the 0.12.1 tag in +/// April 2026. If Citadel's package pin is bumped to a new minor +/// (0.13+), re-verify these against +/// `Sources/Citadel/SSHAuthenticationMethod.swift` and +/// `Sources/Citadel/ClientSession.swift` in the new release. public struct CitadelSSHService: SSHConnectionTester { /// Seconds to wait for the probe exec. Set tight so onboarding /// doesn't hang on a silently-dropped connection. @@ -116,15 +117,10 @@ public struct CitadelSSHService: SSHConnectionTester { // MARK: - Citadel glue /// Translate our in-house `SSHKeyBundle` (raw 32+32 byte Ed25519) - /// into Citadel's authentication method. - /// - /// **FIXME when updating Citadel.** The exact function name below - /// is my best read of Citadel 0.7–0.9's API surface — private-key - /// auth has gone through several iterations. If the build fails - /// here with "no member `ed25519`" or similar, check the current - /// `SSHAuthenticationMethod.swift` in the pinned Citadel version - /// and adjust. Everything else (key decode, error classification, - /// timeout) is independent. + /// into Citadel's authentication method. Verified against Citadel + /// 0.12.1 — see `Sources/Citadel/SSHAuthenticationMethod.swift` + /// for the full set of `.passwordBased(...)` / `.ed25519(...)` / + /// `.p256(...)` / etc. variants. private func buildClientSettings( config: IOSServerConfig, key: SSHKeyBundle @@ -138,9 +134,6 @@ public struct CitadelSSHService: SSHConnectionTester { throw SSHConnectionTestError.other("Stored private key is malformed") } let username = config.user ?? "root" - // See FIXME above — the `.ed25519(...)` method name is the - // shape I expect based on Citadel 0.7–0.9 docs; double-check - // on Mac once the pod is resolved. let auth: SSHAuthenticationMethod = .ed25519( username: username, privateKey: ck diff --git a/scarf/docs/IOS_PORT_PLAN.md b/scarf/docs/IOS_PORT_PLAN.md index da0b112..659be80 100644 --- a/scarf/docs/IOS_PORT_PLAN.md +++ b/scarf/docs/IOS_PORT_PLAN.md @@ -487,7 +487,10 @@ minutes. **Shipped — new `Packages/ScarfIOS` local SPM package:** -- Depends on local ScarfCore + remote Citadel (`from: "0.7.0"`). +- Depends on local ScarfCore + remote Citadel + (`.upToNextMinor(from: "0.12.0")` — tight pin because Citadel's + pre-1.0 authentication-method variant names have changed between + minors; explicit bump → review → smoke-test is the flow). - `KeychainSSHKeyStore.swift` — real iOS Keychain impl (`kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`, no iCloud sync). - `UserDefaultsIOSServerConfigStore.swift` — JSON in UserDefaults. @@ -498,10 +501,11 @@ minutes. `Curve25519.Signing.PrivateKey`. - `CitadelSSHService.swift` — `SSHConnectionTester` conformance + key-generation wrapper. Runs a one-shot SSH exec (`echo scarf-ok`) - for the onboarding probe. Clearly-marked FIXME on the Citadel - authentication-method call site because 0.7→0.9 has shifted the - variant name; other than that one line, everything is - Citadel-version-independent. + for the onboarding probe. Every Citadel API call in the file was + cross-checked against the `0.12.1` tag (SSHAuthenticationMethod. + ed25519, SSHClientSettings init, SSHHostKeyValidator.acceptAnything, + SSHClient.connect, executeCommand, close) — should build clean on + first try. **Shipped — `scarf/scarf-ios/` iOS app source tree:** @@ -535,11 +539,14 @@ the 3 ScarfIOS tests. **Manual validation needed on Mac:** -1. Xcode project creation per SETUP.md. -2. Citadel 0.9.x `SSHAuthenticationMethod.ed25519(...)` variant name - — verify and fix if it's been renamed. -3. Onboarding end-to-end: simulator → physical iPhone via TestFlight +1. Xcode project creation per SETUP.md. The `Assets.xcassets/` is + pre-built (1024×1024 icon copied from the Mac app's set; + Scarf-teal AccentColor with light + dark variants) so the target + should ship with a real icon on first archive. +2. Onboarding end-to-end: simulator → physical iPhone via TestFlight → real SSH host with the public key added to `authorized_keys`. + Citadel 0.12.1 APIs were verified in source; no expected Citadel + drift. **Rules next phases can rely on:** diff --git a/scarf/scarf-ios/Assets.xcassets/AccentColor.colorset/Contents.json b/scarf/scarf-ios/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..b60ec08 --- /dev/null +++ b/scarf/scarf-ios/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.722", + "green" : "0.525", + "red" : "0.227" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.902", + "green" : "0.690", + "red" : "0.400" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/scarf/scarf-ios/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png b/scarf/scarf-ios/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png new file mode 100644 index 0000000..844aecd Binary files /dev/null and b/scarf/scarf-ios/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png differ diff --git a/scarf/scarf-ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/scarf/scarf-ios/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..f22e10c --- /dev/null +++ b/scarf/scarf-ios/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "AppIcon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/scarf/scarf-ios/Assets.xcassets/Contents.json b/scarf/scarf-ios/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/scarf/scarf-ios/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/scarf/scarf-ios/SETUP.md b/scarf/scarf-ios/SETUP.md index 5c7ffad..a82cded 100644 --- a/scarf/scarf-ios/SETUP.md +++ b/scarf/scarf-ios/SETUP.md @@ -61,22 +61,36 @@ In the **scarf-ios** project (target of the same name): Xcode created for you: - `scarf_iosApp.swift` (or `scarf-iosApp.swift`) - `ContentView.swift` - - `Assets.xcassets` (keep this one — we'll reuse) -2. In Finder, open `/scarf/scarf-ios/`. Drag the **App/**, - **Onboarding/**, and **Dashboard/** folders onto the `scarf-ios` - target in Xcode's navigator. + - **`Assets.xcassets`** — yes, delete this one too. We ship a + pre-built `Assets.xcassets/` with the app icon + accent + color inside `/scarf/scarf-ios/` that we'll add in the + next step. +2. In Finder, open `/scarf/scarf-ios/`. Drag **App/**, + **Onboarding/**, **Dashboard/**, and **`Assets.xcassets/`** onto + the `scarf-ios` target in Xcode's navigator. - In the import sheet: **Create groups**, **Add to target: scarf-ios**. -3. Build (`⌘B`). It should compile cleanly. If Citadel's - authentication-method variant has changed since I wrote - `CitadelSSHService`, adjust `buildClientSettings(...)` — see the - FIXME comment in that file. +3. Build (`⌘B`). It should compile cleanly against Citadel 0.12.1 + — every API call in `CitadelSSHService` was cross-checked + against the 0.12.1 tag in April 2026. If you've bumped the pin + to 0.13+ and something fails here, the likeliest culprit is + `SSHAuthenticationMethod.ed25519(username:privateKey:)` being + renamed or refactored; check the current + `Sources/Citadel/SSHAuthenticationMethod.swift` in the new + release. -## One-time: app icon + accent color +## App icon + accent color -The `Assets.xcassets` that Xcode scaffolded already has a blank -`AppIcon` and `AccentColor`. Drop your icon asset + pick an accent -color in the Inspector. Nothing else to configure. +The `Assets.xcassets/` inside `/scarf/scarf-ios/` ships with: + +- **`AppIcon.appiconset/AppIcon-1024.png`** — the 1024×1024 Scarf + icon from the Mac app's icon set. iOS 14+ renders all smaller + sizes automatically from the single 1024 image. +- **`AccentColor.colorset`** — a custom Scarf teal (`RGB + 0.227 / 0.525 / 0.722` in light mode; lighter `0.400 / 0.690 + / 0.902` in dark mode). If you want a different accent, swap + the sRGB components in `Contents.json` or edit in Xcode's + color picker. ## Info.plist additions for M2 @@ -135,9 +149,11 @@ If you want to publish to TestFlight in M2, add these under **Citadel fails to resolve.** Delete derived data (`~/Library/Developer/Xcode/DerivedData/scarf-ios-*`) and `File → Packages → Reset Package Caches`, then rebuild. -**`SSHAuthenticationMethod` has no member `ed25519`.** Citadel's -private-key auth has changed variant names between 0.7 and 0.9. See -`CitadelSSHService.buildClientSettings(...)` — there's one line to +**`SSHAuthenticationMethod` has no member `ed25519`.** Shouldn't +happen against Citadel 0.12.1 (verified), but historically the +private-key variant names have changed between minor versions +(0.7 → 0.9 → 0.12). See `CitadelSSHService.buildClientSettings(...)` +— there's one line to update. Keep the protocol conformance intact. **Keychain reads empty after relaunch.** Check that you haven't