@@ -167,8 +167,66 @@ and a Red/Green/Purple security pass. All work landed on
167167
168168### Fixed
169169
170- - ** Repeated macOS keychain password prompt.** The ` clx-mcp ` server
171- re-read ` com.clx.credentials ` from the OS keychain on every
170+ - ** Repeated macOS keychain password prompt, eliminated at the root.**
171+ CLX no longer uses the macOS keychain by default. A parallel
172+ Codex + research investigation established definitively that no
173+ macOS keychain API can serve an unsigned / adhoc-signed binary
174+ prompt-free: the legacy keychain re-prompts on every read because
175+ its "Always Allow" ACL binds to a code signature that an
176+ adhoc-signed binary does not stably have, and the iOS-style
177+ data-protection keychain outright rejects unsigned binaries with
178+ ` errSecMissingEntitlement (-34018) ` . Therefore the keychain cannot
179+ be the default secret store for a tool distributed without an Apple
180+ Developer ID. CLX now follows the 2026 dev-CLI consensus
181+ (` gh ` , ` aws ` , ` stripe ` , ` doppler ` , ` cargo ` ): a local file backend by
182+ default, keychain opt-in.
183+ - New ` CredentialBackend ` trait under ` CredentialStore ` ; scoping,
184+ validation, indexing, and the session cache are unchanged above it.
185+ - New default ` AgeFileBackend ` : secrets stored in
186+ ` ~/.clx/credentials.age ` (mode 0600), encrypted with ` age `
187+ (age-encryption.org v1, X25519 + ChaCha20-Poly1305) under a random
188+ identity at ` ~/.clx/cred.key ` (mode 0600); ` ~/.clx ` is 0700. Writes
189+ are atomic (temp file + rename). Encrypted at rest specifically to
190+ defeat the realistic CLX exposure path (dotfile sync, backups,
191+ log/support bundles); a same-uid attacker remains out of scope, as
192+ it is for the keychain.
193+ - ` KeyringBackend ` retains the previous keychain code and is selected
194+ only when the user explicitly sets ` credentials.backend: keychain `
195+ (or ` CLX_CREDENTIALS_BACKEND=keychain ` ). Default is ` file ` .
196+ - Credential resolution is now env (` api_key_env ` ) then the selected
197+ backend then ` api_key_file ` ; under the default backend the keychain
198+ code path is never reached. Every credential-construction callsite
199+ (` resolve_azure_credential ` , the MCP server, the ` clx credentials `
200+ and ` clx keychain-trust ` commands) was repointed through a single
201+ config-aware constructor so nothing silently falls through to the
202+ keychain. A regression test asserts zero keychain calls under the
203+ default backend.
204+ - Fixed a latent ` SecAccessCreate(NULL) ` defect in the opt-in
205+ keychain path: ` NULL ` means "trust only the calling app" (the
206+ opposite of the intended permissive access); it now passes an
207+ empty trusted-application list, and the misleading comments were
208+ corrected. The opt-in keychain remains best-effort for adhoc
209+ binaries by macOS design.
210+ - New ` clx credentials migrate <key> ` : explicit, one-time move of a
211+ secret that exists only in the legacy keychain into the file
212+ backend. This is the single place a keychain prompt may still
213+ occur, and only when the user runs it deliberately and the secret
214+ is not already available from env / ` api_key_file ` / the file
215+ backend. Automatic paths never read the keychain.
216+ - Net result: a fresh user with default config sees zero macOS
217+ keychain prompts for the entire Claude Code session, with no code
218+ signing, no manual ` security ` command, and no temporary toggle.
219+
220+ Superseded earlier in this release cycle (kept for historical
221+ context): an interim session-scoped credential cache
222+ (` CredentialStore::new_cached ` , ` secrecy::SecretString ` , zeroized on
223+ drop, owned by ` McpServer ` ) and a ` clx keychain-trust ` repair command
224+ that relaxed the legacy keychain ACL with a single documented
225+ ` security set-generic-password-partition-list ` call. Both still exist
226+ and are correct for the opt-in keychain backend, but are no longer on
227+ the default path. The original interim note read:
228+ - ** (interim, now superseded by the file backend above)** The ` clx-mcp `
229+ server re-read ` com.clx.credentials ` from the OS keychain on every
172230 credential-bearing tool invocation (e.g. each ` clx_credentials `
173231 call), so macOS prompted far more often than necessary.
174232 ` CredentialStore ` gained an opt-in session-scoped cache
0 commit comments