mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
ba368d2f6d
First iOS phase. Delivers all the code needed to build + TestFlight a
functional v1 iOS app (onboarding with SSH-key generate / import +
real Citadel-backed connection test; persistent Keychain key +
UserDefaults server config; placeholder Dashboard) — but NOT the
scarf-ios.xcodeproj. Creating that from scratch by hand is too risky
without an iOS SDK to build against, so Alan creates it in Xcode's UI
following scarf/scarf-ios/SETUP.md (~5 minutes, one-time).
## ScarfCore additions (all Linux-testable)
Packages/ScarfCore/Sources/ScarfCore/Security/:
- SSHKey.swift — SSHKeyBundle + SSHKeyStore protocol
+ InMemorySSHKeyStore test actor
- IOSServerConfig.swift — IOSServerConfig + store protocol + mock;
toServerContext(id:) bridges to the
existing ServerContext so all ScarfCore
services work against an iOS config
- OnboardingState.swift — OnboardingStep enum + pure validators
(host, port, PEM shape, public-key parse)
- SSHConnectionTester.swift — protocol + error enum + mock
- OnboardingViewModel.swift — @Observable @MainActor state machine,
fully dependency-injected (key store /
config store / tester / generator closure)
## New Packages/ScarfIOS local SPM package
Depends on ScarfCore + Citadel (from: "0.7.0").
- KeychainSSHKeyStore.swift — real iOS Keychain storage
(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, no iCloud
sync). Gated on canImport(Security) for Linux skip.
- UserDefaultsIOSServerConfigStore.swift — JSON-encoded single-key
persistence of IOSServerConfig.
- Ed25519KeyGenerator.swift — CryptoKit-backed Ed25519 minting.
Emits standard OpenSSH public-key lines (authorized_keys-ready).
Stores the private half in a compact SCARF ED25519 PRIVATE KEY
PEM shape that CitadelSSHService decodes back into a
Curve25519.Signing.PrivateKey. Non-interop with OpenSSH's
`BEGIN OPENSSH PRIVATE KEY` envelope — export flow for sharing
keys is deferred to a later phase.
- CitadelSSHService.swift — SSHConnectionTester conformance +
key-generation wrapper. Runs `echo scarf-ok` over a one-shot
Citadel exec for the onboarding connection test. One FIXME on
buildClientSettings because Citadel 0.7→0.9 shifted the
`.ed25519(...)` authentication-method variant name; every other
line is Citadel-version-independent. Gated on
canImport(Citadel) && canImport(CryptoKit).
## scarf/scarf-ios/ app source tree
- App/ScarfIOSApp.swift — @main, RootModel routes to
onboarding or dashboard based on
stored state.
- Onboarding/OnboardingRootView.swift — 8 sub-views, one per
OnboardingStep. Validated
server-details form, key-source
picker, generate / show-public
/ import / test / retry /
connected.
- Dashboard/DashboardView.swift — M2 placeholder: connected host
details + Disconnect button.
M3 replaces with real data.
## scarf/scarf-ios/SETUP.md
Step-by-step Xcode project creation:
- iOS 18 / iPhone-only / team 3Q6X2L86C4 / Bundle ID
com.scarf.scarf-ios / Swift 5 language mode.
- Wire Packages/ScarfCore + Packages/ScarfIOS (Citadel resolves
transitively).
- Replace Xcode's default scaffolded files with this source tree.
- Smoke-test procedure (simulator → physical iPhone).
- TestFlight upload steps.
- Troubleshooting for the known Citadel-variant-name drift.
## Test coverage (Linux, `swift test`)
M2OnboardingTests, 26 new tests (ScarfCore):
- SSHKeyBundle memberwise + display fingerprint
- InMemorySSHKeyStore + InMemoryIOSServerConfigStore round-trips
- IOSServerConfig.toServerContext bridging (with + without
remoteHome override)
- All OnboardingLogic validators (empty / whitespace / port range /
legacy-RSA rejection / public-key line parser)
- MockSSHConnectionTester scripting (success + failure)
- 10 OnboardingViewModel end-to-end paths: happy-path
save-and-test, invalid-host blocks advance, connection-failure
routes to .testFailed (and crucially does NOT save config),
retry-after-failure-works, import-happy, import-rejects-bad-PEM,
reset clears all state
ScarfIOSSmokeTests, 3 tests (Apple-only, won't run on Linux):
- Ed25519KeyGenerator bundle shape + base64 wire format
- OpenSSH public-key line byte-length pinned at 51 bytes
- Corrupted PEM rejection on round-trip decode
Running
docker run --rm -v $PWD/scarf/Packages/ScarfCore:/work -w /work swift:6.0 swift test
reports **88 / 88 passing** (62 pre-M2 + 26 new).
## Real bug caught in development
First pass of OnboardingViewModel had `confirmPublicKeyAdded()` set
`isWorking=true`, then call `runConnectionTest()` which bailed on
`!isWorking` — meaning the connection probe never ran and the config
was never saved. Caught by the end-to-end test. Fixed by extracting
the shared probe body into `performConnectionTest()` and letting
both entry points own their own `isWorking` transition.
## Manual validation still needed on Mac
1. Xcode project creation per SETUP.md — confirm the resulting
project builds cleanly.
2. Citadel 0.9.x authentication-method variant — verify the one
FIXME line in buildClientSettings.
3. End-to-end onboarding: simulator against `localhost:22` (or a
test host), then TestFlight → physical iPhone → real SSH host
with the shown public key in authorized_keys.
Updated scarf/docs/IOS_PORT_PLAN.md with M2's shipped scope, the
scope decision about NOT generating the xcodeproj, and the list of
rules M3+ can rely on (Citadel transport dispatch, ChannelFactory
hook, single-server invariant).
https://claude.ai/code/session_019yMRP6mwZWfzVrPTqevx2y