M2 follow-up: Citadel 0.12.1 (current), pre-built Assets.xcassets

Two follow-ups per review:

## Citadel: current stable

Citadel is at 0.12.1, not 0.9.x as I'd been writing against. Bumped
the pin from `from: "0.7.0"` to `.upToNextMinor(from: "0.12.0")`
— tight because Citadel's pre-1.0 authentication-method variants
have shifted between minor releases (0.7 → 0.9 → 0.12), so
explicit bump-and-review is safer than letting the version float.

Downloaded Citadel 0.12.1's source and verified every API call in
CitadelSSHService against it:
  - SSHAuthenticationMethod.ed25519(username:, privateKey:) ✓
  - SSHClientSettings(host:, authenticationMethod:, hostKeyValidator:) ✓
  - SSHHostKeyValidator.acceptAnything() ✓
  - SSHClient.connect(to: settings) ✓
  - client.executeCommand(_:) -> ByteBuffer ✓
  - client.close() async throws ✓

Dropped the "FIXME — may need adjustment" disclaimer in the file
header; replaced with a "verified against 0.12.1" note that says
re-verify if the pin bumps to 0.13+. Same change in SETUP.md
troubleshooting.

## Assets.xcassets (app icon + accent color)

scarf/scarf-ios/Assets.xcassets/ now exists with:

  - AppIcon.appiconset/
      AppIcon-1024.png    (1024×1024, copied from the Mac app's
                           icon set — same art)
      Contents.json       (idiom: universal, platform: ios,
                           size: 1024x1024 — iOS 14+ renders all
                           smaller sizes from this automatically)
  - AccentColor.colorset/
      Contents.json       (Scarf teal: sRGB 0.227/0.525/0.722
                           light, 0.400/0.690/0.902 dark)
  - Contents.json         (root, empty — just version metadata)

SETUP.md updated:
  - Instructs Alan to delete Xcode's scaffolded Assets.xcassets AND
    import ours, not the other way around.
  - Notes the accent color values so a different palette choice is
    a one-file edit.
  - Removes the obsolete "drop your icon asset" step.

No functional code changes; tests still 88/88 on Linux.

https://claude.ai/code/session_019yMRP6mwZWfzVrPTqevx2y
This commit is contained in:
Claude
2026-04-22 23:17:07 +00:00
parent ba368d2f6d
commit 3420abae74
8 changed files with 125 additions and 47 deletions
+7 -3
View File
@@ -32,11 +32,15 @@ let package = Package(
], ],
dependencies: [ dependencies: [
.package(path: "../ScarfCore"), .package(path: "../ScarfCore"),
// Pinned to the 0.7 minor line until the API stabilizes at 1.0. // Pinned tight to the 0.12 minor line. Citadel pre-1.0 has
// When we bump, re-run onboarding smoke tests against at least: // 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 // (a) a real host with 1Password SSH agent
// (b) a real host with a hand-edited `authorized_keys` // (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: [ targets: [
.target( .target(
@@ -20,14 +20,15 @@ import ScarfCore
/// (file transport, SQLite snapshot pulls, ACP channel) will /// (file transport, SQLite snapshot pulls, ACP channel) will
/// layer on top. /// layer on top.
/// ///
/// **Citadel API disclaimer.** Citadel 0.9's exact authentication- /// **Citadel 0.12.1 API verified.** Every call below (`SSHAuthentication
/// method spelling for Ed25519 private keys is evolving across /// Method.ed25519(username:privateKey:)`, `SSHClientSettings(host:
/// 0.7 0.9. The `runOneShotProbe(...)` helper below is written /// authenticationMethod:hostKeyValidator:)`, `SSHHostKeyValidator.
/// against the documented `SSHClientSettings` + `.privateKey(...)` /// acceptAnything()`, `SSHClient.connect(to:)`, `client.executeCommand
/// pattern; if Citadel has renamed or refactored that variant, /// (_:)`, `client.close()`) was cross-checked against the 0.12.1 tag in
/// adjust `buildClientSettings(...)` everything else (the retry /// April 2026. If Citadel's package pin is bumped to a new minor
/// loop, the error classification, the exit-code handling) is /// (0.13+), re-verify these against
/// Citadel-version-independent. /// `Sources/Citadel/SSHAuthenticationMethod.swift` and
/// `Sources/Citadel/ClientSession.swift` in the new release.
public struct CitadelSSHService: SSHConnectionTester { public struct CitadelSSHService: SSHConnectionTester {
/// Seconds to wait for the probe exec. Set tight so onboarding /// Seconds to wait for the probe exec. Set tight so onboarding
/// doesn't hang on a silently-dropped connection. /// doesn't hang on a silently-dropped connection.
@@ -116,15 +117,10 @@ public struct CitadelSSHService: SSHConnectionTester {
// MARK: - Citadel glue // MARK: - Citadel glue
/// Translate our in-house `SSHKeyBundle` (raw 32+32 byte Ed25519) /// Translate our in-house `SSHKeyBundle` (raw 32+32 byte Ed25519)
/// into Citadel's authentication method. /// into Citadel's authentication method. Verified against Citadel
/// /// 0.12.1 see `Sources/Citadel/SSHAuthenticationMethod.swift`
/// **FIXME when updating Citadel.** The exact function name below /// for the full set of `.passwordBased(...)` / `.ed25519(...)` /
/// is my best read of Citadel 0.70.9's API surface private-key /// `.p256(...)` / etc. variants.
/// 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.
private func buildClientSettings( private func buildClientSettings(
config: IOSServerConfig, config: IOSServerConfig,
key: SSHKeyBundle key: SSHKeyBundle
@@ -138,9 +134,6 @@ public struct CitadelSSHService: SSHConnectionTester {
throw SSHConnectionTestError.other("Stored private key is malformed") throw SSHConnectionTestError.other("Stored private key is malformed")
} }
let username = config.user ?? "root" let username = config.user ?? "root"
// See FIXME above the `.ed25519(...)` method name is the
// shape I expect based on Citadel 0.70.9 docs; double-check
// on Mac once the pod is resolved.
let auth: SSHAuthenticationMethod = .ed25519( let auth: SSHAuthenticationMethod = .ed25519(
username: username, username: username,
privateKey: ck privateKey: ck
+16 -9
View File
@@ -487,7 +487,10 @@ minutes.
**Shipped — new `Packages/ScarfIOS` local SPM package:** **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 - `KeychainSSHKeyStore.swift` — real iOS Keychain impl
(`kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`, no iCloud sync). (`kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly`, no iCloud sync).
- `UserDefaultsIOSServerConfigStore.swift` — JSON in UserDefaults. - `UserDefaultsIOSServerConfigStore.swift` — JSON in UserDefaults.
@@ -498,10 +501,11 @@ minutes.
`Curve25519.Signing.PrivateKey`. `Curve25519.Signing.PrivateKey`.
- `CitadelSSHService.swift``SSHConnectionTester` conformance + - `CitadelSSHService.swift``SSHConnectionTester` conformance +
key-generation wrapper. Runs a one-shot SSH exec (`echo scarf-ok`) key-generation wrapper. Runs a one-shot SSH exec (`echo scarf-ok`)
for the onboarding probe. Clearly-marked FIXME on the Citadel for the onboarding probe. Every Citadel API call in the file was
authentication-method call site because 0.7→0.9 has shifted the cross-checked against the `0.12.1` tag (SSHAuthenticationMethod.
variant name; other than that one line, everything is ed25519, SSHClientSettings init, SSHHostKeyValidator.acceptAnything,
Citadel-version-independent. SSHClient.connect, executeCommand, close) — should build clean on
first try.
**Shipped — `scarf/scarf-ios/` iOS app source tree:** **Shipped — `scarf/scarf-ios/` iOS app source tree:**
@@ -535,11 +539,14 @@ the 3 ScarfIOS tests.
**Manual validation needed on Mac:** **Manual validation needed on Mac:**
1. Xcode project creation per SETUP.md. 1. Xcode project creation per SETUP.md. The `Assets.xcassets/` is
2. Citadel 0.9.x `SSHAuthenticationMethod.ed25519(...)` variant name pre-built (1024×1024 icon copied from the Mac app's set;
— verify and fix if it's been renamed. Scarf-teal AccentColor with light + dark variants) so the target
3. Onboarding end-to-end: simulator → physical iPhone via TestFlight 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`. → 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:** **Rules next phases can rely on:**
@@ -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
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

@@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "AppIcon-1024.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
+31 -15
View File
@@ -61,22 +61,36 @@ In the **scarf-ios** project (target of the same name):
Xcode created for you: Xcode created for you:
- `scarf_iosApp.swift` (or `scarf-iosApp.swift`) - `scarf_iosApp.swift` (or `scarf-iosApp.swift`)
- `ContentView.swift` - `ContentView.swift`
- `Assets.xcassets` (keep this one — we'll reuse) - **`Assets.xcassets`** — yes, delete this one too. We ship a
2. In Finder, open `<repo>/scarf/scarf-ios/`. Drag the **App/**, pre-built `Assets.xcassets/` with the app icon + accent
**Onboarding/**, and **Dashboard/** folders onto the `scarf-ios` color inside `<repo>/scarf/scarf-ios/` that we'll add in the
target in Xcode's navigator. next step.
2. In Finder, open `<repo>/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: - In the import sheet: **Create groups**, **Add to target:
scarf-ios**. scarf-ios**.
3. Build (`⌘B`). It should compile cleanly. If Citadel's 3. Build (`⌘B`). It should compile cleanly against Citadel 0.12.1
authentication-method variant has changed since I wrote — every API call in `CitadelSSHService` was cross-checked
`CitadelSSHService`, adjust `buildClientSettings(...)` — see the against the 0.12.1 tag in April 2026. If you've bumped the pin
FIXME comment in that file. 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 The `Assets.xcassets/` inside `<repo>/scarf/scarf-ios/` ships with:
`AppIcon` and `AccentColor`. Drop your icon asset + pick an accent
color in the Inspector. Nothing else to configure. - **`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 ## 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-*`) **Citadel fails to resolve.** Delete derived data (`~/Library/Developer/Xcode/DerivedData/scarf-ios-*`)
and `File → Packages → Reset Package Caches`, then rebuild. and `File → Packages → Reset Package Caches`, then rebuild.
**`SSHAuthenticationMethod` has no member `ed25519`.** Citadel's **`SSHAuthenticationMethod` has no member `ed25519`.** Shouldn't
private-key auth has changed variant names between 0.7 and 0.9. See happen against Citadel 0.12.1 (verified), but historically the
`CitadelSSHService.buildClientSettings(...)` — there's one line to 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. update. Keep the protocol conformance intact.
**Keychain reads empty after relaunch.** Check that you haven't **Keychain reads empty after relaunch.** Check that you haven't