Fleet changelogs · dev.ecs0.net
rdmsm4x-changelog-20260925-0143-grok-rtty-keychain-prompt

rdmsm4x changelog 20260925-0143: grok RTTy keychain prompt triage

Agent: grok@rdmsm4x/grok0925k (AGENT_LLM=grok). Scope: read-only triage on jdmbair13m5 (uid 502) + rdmsm4x; NO build/sign/upload/ASC changes (522 freeze); RTTy PID 20867 not touched.

Actions

App Privacy check (522 code)

No developer or third-party analytics/crash/telemetry/license SDKs or endpoints. Network: user-chosen probe targets; speed.cloudflare.com /meta + /__down (public egress IP + speed test, no identifiers sent); CloudKit privateCloudDatabase only (fleet snapshot incl. device ID + HMAC egress fingerprint); LAN Bonjour/TLS peer publisher to paired peers. Egress HMAC and device ID are never sent to a developer server. Supports Data Not Collected.

Ticket body

SYMPTOM (jdmbair13m5, uid 502 richh): macOS repeatedly shows "Keychain Not Found - A keychain cannot be found to store "egress-hmac-secret-v1"" (Cancel / Reset To Defaults). Requester /Applications/RTTy.app PID 20867 (0.3.847/522, up ~17h, gui/502 Aqua session 100027 same as Finder).

SIGNING OF THAT COPY: Developer ID Direct build, not App Store/dev. Identifier com.eastcoastscience.rtty.direct; Authority "Developer ID Application: east coast science, llc (ZU2882L4HT)"; notarized + stapled (spctl: accepted, Notarized Developer ID); hardened runtime; timestamp 2026-09-24 07:12. Signed entitlements: application-identifier ZU2882L4HT.com.eastcoastscience.rtty.direct, team-identifier, iCloud CloudKit (Production). NO keychain-access-groups, NO app-sandbox (embedded "RTTy Direct Developer ID" profile would allow ZU2882L4HT.* but the signature does not claim it). Installed by the fleet deploy (script/deploy_preview_remote.sh: ditto → sudo mv → chown root:wheel → launchctl asuser open) via retry-jdmbair13m5-522.zsh on 2026-09-24 (receipt STATUS=accepted, pid 20867). No pkg receipt, no _MASReceipt.

EVIDENCE: unified log for RTTy[20867]: SecItemCopyMatching → SecItemUpdate → SecItemAdd, each followed by "securityd MacOS error: -25307" (errSecNoDefaultKeychain); 36 occurrences in the last 2h, two SecItemAdd per ~2-4 min (e.g. 01:35:35, 01:35:38, 01:37:36, 01:37:42, 01:41:36, 01:41:38 EDT), one of them on the MAIN thread (4357cd). A fresh process (security default-keychain -d user) sees login.keychain-db as default and search list login + fleet-signing, so the no-default condition is specific to the long-lived RTTy process (Security framework caches the keychain search list/default per process). Likely the list/default was transiently broken when RTTy launched (same shape as ISSUE-20260921-24 on rdmsm4x, where the search list lost login.keychain-db; ~/Library/Preferences/com.apple.security.plist was rewritten 2026-09-22 02:40 with login + fleet-signing and no explicit DefaultKeychain). Not proven.

ROOT CAUSE IN CODE (tag v0.3.847-build522 = 63229dc, Sources/RTTy/Services/StableDeviceIdentityStore.swift, StableEgressHMACSecretStore):

  1. No kSecUseDataProtectionKeychain and no kSecAttrAccessGroup, so on macOS every SecItem* call goes to the legacy file-based keychain (default keychain / search list). LegacyIdentityMigrationPolicy declares the destination as .dataProtection in access group ZU2882L4HT.com.eastcoastscience.rtty.shared, but the store never uses it; the Direct signature also lacks keychain-access-groups, so data-protection with that group would fail with -34018 today anyway.
  2. saveToKeychain() REMOVES the LAContext(interactionNotAllowed) before SecItemAdd, so the add is interactive. The legacy SecKeychainAddItem path raises the "Keychain Not Found" SecurityAgent dialog on errSecNoDefaultKeychain; interactionNotAllowed would not suppress that dialog anyway (it only governs LocalAuthentication/ACL UI). kSecUseDataProtectionKeychain would.
  3. No negative cache/backoff: secret() is called from ObservationStore.currentFleetSnapshot() whenever publicEgressAddresses is non-empty (Cloudflare /meta succeeded), and currentFleetSnapshot() runs on every CloudKit projection publish and every peer snapshot request. load → notFound, generate, add → dialog → Cancel → throw → the whole snapshot fails → next cycle repeats. Hence the repeated prompt, plus a blocked main actor while the dialog is up, plus failed CloudKit/peer snapshots on that host.
  4. Project/build42/ws1-keychain-audit.md is stale/wrong: it says these stores use the "Data Protection Keychain" with a "non-interactive pattern"; the 522 code uses the file keychain and an interactive add.

APP REVIEW RISK: UNLIKELY (≈75-80% confidence it will NOT show). The Standard (App Store) target compiles the same file (StandardSources.txt line 20) and would also use the legacy keychain (sandbox entitlements at 63229dc: app-sandbox, app groups, CloudKit, network client/server, content filter; no keychain-access-groups). But the dialog needs errSecNoDefaultKeychain; a reviewer's clean Mac has a normal login keychain as default, a fresh install has no pre-existing item from another signer (so no ACL prompt either), and the add succeeds silently. Residual risk: reviewer account with odd keychain state, or sandbox-specific file-keychain behavior. Could not verify the uploaded build's signed entitlements: no 522 xcarchive/export on rdmsm4x (Archives empty; only _review/repair3-unsigned/RTTyStandard.xcarchive and dist/RTTy.pkg (Direct)).

WHAT RICH SHOULD DO ON THE AIR: Keep clicking Cancel (safe: nothing is written, the snapshot just fails). Do NOT click "Reset To Defaults" (it creates a new login keychain and moves the old one aside, losing access to saved passwords until manually restored). Quit RTTy (menu bar → Quit) and relaunch it from /Applications; a fresh process should pick up login.keychain-db as default (verify: no -25307 in log show --process RTTy). If it recurs, check security default-keychain -d user / security list-keychains -d user.

PROPOSED FIX FOR THE NEXT BUILD (NOT NOW — App Store builds FROZEN while 0.3.847 (522) awaits submission; do not build/sign/upload/change ASC for this):