iOS Keychain and shared-store capabilities¶
The SDK’s storage capability response (POST /db/capabilities, relayed by CtrlProxy as
storage_capabilities_result) reports two things clients must not guess (#5161): the
app’s Keychain policy, and how each registered store is shared and coordinated. The
types live in ios/sdk-wire (KeychainCapability, StorageStoreState) so the SDK and
CtrlProxy encode one definition. The daemon reduces the payload to these fields
(src/features/storage/iosStorageDiscovery.ts) and folds them into the
automobile:devices/{deviceId}/storage/capabilities resource.
Keychain contract¶
- No values. The capability and every Keychain route carry selectors only: item class, service, account and access group. The daemon’s schema drops any other field, so a value cannot reach a client even if a future SDK sent one.
- Inspection is unavailable. There is no read path. An inspection adapter, if one
is added, must be host-provided and opt-in, must name exact items in an allowlist,
must redact per field and bound value size before anything leaves the app, and must
emit an audit event per read. Until then
inspectionisunavailableand thesecure_statereadoperation stays unavailable. - Deletion is exact.
deletionisenabledonly when the host setallowKeychainDeletionand allowlisted at least one entry inallowedKeychainDeletionEntries, both on itsStorageInspectionConfiguration;deletionScopesis that allowlist, sorted. It is the same set/keychain/delete(thedeleteKeychainItemtool) matches byte for byte. Reporting a scope does not authorize a call: each delete still passes the confirmation, app-id and storage mutation gates. - No reset or write. Deleting allowlisted items is not a namespace reset, so
namespace_resetandwritestay unsupported.resetKeychainis the separate, simulator-wide reset. - Unknown is not disabled. An SDK that predates this field, or a malformed
capability, is reported as
deletion: unknownwithSDK_TOO_OLD; an unreachable runner asBRIDGE_UNAVAILABLE. The storage routes exist only in Debug builds, so a release build is always unreachable here.
Store states¶
Each registered app-group UserDefaults suite and Core Data store becomes one
StorageStoreState:
kind |
sharing |
coordination |
query |
|---|---|---|---|
user_defaults_suite |
always app_group |
declared at registerAppGroupSuite(_:coordination:) |
supported |
core_data |
app_group when the registration names an appGroup, else app_private |
single_process when app-private; otherwise declared, default uncoordinated |
unsupported (#15692) |
Hosts declare it at registration:
DatabaseInspector.shared.registerAppGroupSuite("group.example", coordination: .hostCoordinated)
for suites, and CoreDataStoreRegistration(identifier:appGroup:coordination:) for Core Data.
Coordination is always the host’s declaration. The SDK does not probe containers or
file locks to infer it; re-registering a suite replaces the earlier declaration.