Skip to content

feat(credentials): add native iOS Keychain lifecycle#122

Merged
tcballard merged 4 commits into
mainfrom
codex/ios-keychain-credential-store
Jul 25, 2026
Merged

feat(credentials): add native iOS Keychain lifecycle#122
tcballard merged 4 commits into
mainfrom
codex/ios-keychain-credential-store

Conversation

@tcballard

Copy link
Copy Markdown
Contributor

Summary

Implements the first Phase 3 credential boundary in WF-ROADMAP-0016.

Adds:

  • native add, replace, and remove flows for supported mobile API keys
  • device-only, non-synchronizing iOS Keychain custody behind a typed actor boundary
  • deterministic policy tests, a signed system-Keychain smoke test, and updated roadmap documentation

Scope

Included

  • stable credential identities for OpenAI Platform, Moonshot/Kimi Platform, and OpenRouter
  • a CredentialStore actor contract and Security framework backend
  • kSecAttrAccessibleWhenUnlockedThisDeviceOnly protection
  • explicit non-synchronizing Keychain requests
  • configured/not-configured snapshots with no secret material
  • native Settings navigation, secure entry, replacement, confirmation, and removal
  • explicit distinction between OpenAI Platform keys and ChatGPT/Codex accounts
  • bounded key size and sanitized user-facing errors
  • per-user .swiftpm and Xcode metadata ignores

Excluded

  • live provider execution or network requests
  • model discovery or destination publication
  • changes to Automatic or user-created routes
  • ChatGPT/Codex native account authentication
  • OAuth, Kimi account auth, Apple Foundation Models, or paired Mac support
  • changes to the existing macOS credential broker

Product / Architecture Decisions

  • Credentials are identified by stable, provider-scoped IDs and stored under the iOS-only com.wayfinder.router.ios.credentials service.
  • Secret reads remain inside the credential/provider boundary. AppModel receives only configured/not-configured snapshots.
  • Keychain records use whenUnlockedThisDeviceOnly and explicitly disable synchronization.
  • Add, replace, and delete are idempotent at the storage boundary.
  • Explicit removal verifies the credential is absent before publishing the updated status.
  • Connecting a key does not publish a destination, modify Automatic, or change the current privacy posture.
  • OpenAI Platform remains separate from ChatGPT/Codex subscription access in both type identity and UI copy.

User-Facing Contract

CLI

No CLI changes.

Human Output

  • Settings now exposes an API Keys destination with a saved-key count.
  • Each supported provider shows Saved or Not configured.
  • Users can add, replace, or remove a key through native forms and confirmation.
  • Saved keys are never displayed again.
  • The UI states that credentials remain on the device and do not change routing.

JSON Output

No public JSON schema changes. Credential values and paths never enter conversation exports.

Exit Codes

No exit-code changes.

Verification

Ran

Signed simulator:

xcodebuild test
  -project ios/WayfinderIOS/WayfinderIOS.xcodeproj
  -scheme WayfinderIOS
  -destination 'platform=iOS Simulator,name=iPhone 17,OS=latest'

Result: 30 passed, 0 failed, 0 skipped. This includes a real Security framework save/read/delete cycle.

CI-equivalent unsigned simulator:

xcodebuild test
  -project ios/WayfinderIOS/WayfinderIOS.xcodeproj
  -scheme WayfinderIOS
  -destination 'platform=iOS Simulator,name=iPhone 17,OS=latest'
  CODE_SIGNING_ALLOWED=NO

Result: 29 passed, 0 failed, 1 skipped. Only the system-Keychain entitlement smoke test skips in the deliberately unsigned host; deterministic custody and policy tests still run.

Manual iPhone 17 simulator flow:

  • navigated Settings to API Keys
  • added a dummy OpenAI Platform key
  • confirmed Saved and Replace Key states
  • confirmed the accessibility tree exposed only Entered, never the secret
  • removed the key through confirmation and verified Not configured
  • removed the dummy simulator credential before handoff

Covered

  • create, read, update, delete, and missing-delete idempotency
  • device-only, non-synchronizing request policy
  • real signed simulator Keychain access
  • empty and oversized key rejection
  • no secret in AppModel snapshots or conversation exports
  • removal without privacy-posture or route mutation
  • OpenAI Platform and ChatGPT identity separation

Review Path

  1. CredentialStore.swift for credential identity, Keychain policy, and Security backend.
  2. AppModel.swift for status-only state and lifecycle orchestration.
  3. APIKeysView.swift and SettingsView.swift for the native user flow.
  4. CredentialStoreTests.swift and credential cases in AppModelTests.swift.
  5. Roadmap, README, project membership, and generated-metadata ignores.

Notes For Reviewer

The next Phase 3 PR will add the generic OpenAI-compatible provider runtime. This PR deliberately stops at credential custody and does not make any key routable.

@tcballard
tcballard marked this pull request as ready for review July 24, 2026 20:26
@tcballard
tcballard merged commit 6c5479c into main Jul 25, 2026
4 checks passed
@tcballard
tcballard deleted the codex/ios-keychain-credential-store branch July 25, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant