Phase 1 REVIEW.md CR-01 (SEC-01 / D-10): `SystemKeychainStore` previously
omitted `kSecAttrAccessible` and `kSecAttrSynchronizable`, so items were
written with the OS default — eligible for iCloud Keychain migration and
Time Machine backup, contradicting the "never leaves this device"
guarantee.
packages/Keychain/Sources/Keychain/SystemKeychainStore.swift:7-40
- Add `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` to every
SecItemAdd / SecItemUpdate attrs payload.
- Add `kSecAttrSynchronizable = kCFBooleanFalse` to both the query and
the attrs payload (legacy macOS keychain matches on exact sync value).
packages/Keychain/Tests/KeychainTests/KeychainTests.swift
- Add `test_itemIsNotSynchronizable`: asserts that a query with
`kSecAttrSynchronizable = true` returns errSecItemNotFound for a
freshly-written item, and the non-sync query does find it.
- Add `test_sourceAlwaysSetsDeviceOnlyAccessibility`: source-level grep
gate so a future refactor that drops either attribute fails CI.
(The legacy file-based macOS keychain does not echo
`kSecAttrAccessible` back via SecItemCopyMatching, so a runtime
assertion is not possible without entitlements for the
data-protection keychain.)
Owner change: project will be owned by KofTwentyTwo. Bundle ID updates
propagate to three ${PRODUCT_BUNDLE_IDENTIFIER} targets, the Keychain
service name (SEC-01 / D-10), the OSLog subsystem identifier, and the
FileRotatingWriter DispatchQueue label. pbxproj regenerated via
xcodegen generate.
Forward-looking planning docs (STATE, REQUIREMENTS, PATTERNS, VALIDATION)
updated to match. Historical records (completed PLAN.md / SUMMARY.md /
RESEARCH.md / audits / CONTEXT / DISCUSSION-LOG) deliberately left as-is
to preserve the audit trail of the original naming decision.
Verified: 49 SPM tests pass (4 Keychain + 15 Config + 13 Logging + 17
Shell), xcodebuild Debug build succeeds with full codesign pipeline.
Decision logged in STATE.md Key Decisions (Locked 2026-04-22).
- Add KeychainItem / KeychainError / KeychainStore / SystemKeychainStore
- SystemKeychainStore wraps Security.framework (SecItemAdd/Copy/Update/Delete)
- KeychainItem.anthropic pinned to (com.kingsrook.jarvis, anthropic) per D-10
- set() is idempotent (SecItemUpdate then SecItemAdd on errSecItemNotFound)
- delete() swallows errSecItemNotFound
- Per-test-random account suffix avoids collision with real user Keychain state
- Deletes Plan 01 placeholder sources/tests
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- packages/Keychain — zero SPM deps (Security.framework at link time only)
- packages/Logging — depends on apple/swift-log 1.5.3+; library product is
named JarvisLogging to avoid collision with swift-log's own Logging module
- packages/Config — depends on Keychain + Logging (product JarvisLogging)
- packages/Shell — depends on Config + Logging; links AppKit,
ServiceManagement, IOKit, Carbon for hotkey/launch-at-login surfaces
Every target (main + test) opts into .swiftLanguageMode(.v6) per D-04 so
data-race errors are compile-time failures, not warnings.
Placeholder sources (one per package) plus one trivial XCTest per package
per D-03 so each package builds standalone and has a passing test. Real
sources land in Plan 02 (Keychain/Config/Logging) and Plan 04 (Shell).
Bus/LLM/MCP/Voice/Memory packages NOT pre-created per D-01 — they arrive
in their owning phases (P2/P4/P5/P6/P7) to keep rebuild scope tight.