Skip to content

feat: OS-keychain secret storage — keys never sit in config files - #16

Open
amos-aios wants to merge 1 commit into
mofa-org:mainfrom
amos-aios:feat/keychain-secrets
Open

feat: OS-keychain secret storage — keys never sit in config files#16
amos-aios wants to merge 1 commit into
mofa-org:mainfrom
amos-aios:feat/keychain-secrets

Conversation

@amos-aios

Copy link
Copy Markdown

What

Provider credentials can now live in the OS keychain instead of config files — closing the "密钥仅存 OS 钥匙串" hard constraint from studio's PRD (PLAT-04).

  • api_key gains a third indirection form alongside ${VAR} / env:VAR:

    api_key = "keychain:mofa/openai"   # resolved from the OS credential store at parse time

    The file holds only the account name; the plaintext key reaches nothing on disk, and the rest of the engine only ever sees the real key in memory.

  • Backed by the keyring crate: macOS Keychain, Windows Credential Manager, Linux Secret Service. Hosts without a credential store fail loudly (with the storing command named in the error) — never a silent plaintext fallback.

  • New CLI subcommands: mofa-engine keychain-set / keychain-get / keychain-delete --account … (--value optional; reading one line from stdin keeps keys out of shell history).

  • secrets::available() probes the store (write+delete) for diagnostics.

Testing

5 new tests: full store/load/overwrite/delete round trip, idempotent delete + missing-entry loads, empty-account rejection, availability probe, and the config-resolve chain (keychain:ACCOUNT → value, near-miss literals pass through untouched, missing entry errors name the fix command). Tests run against a persistent in-process stand-in — keyring's bundled mock is EntryOnly (password lives inside each Entry), so it cannot round-trip; the stand-in mirrors a real keychain's cross-entry persistence. Verified against the real macOS Keychain: keychain-set → keychain-get → keychain-delete → security find-generic-password confirms removal.

Full workspace suite green (192 in engine-core).

🤖 Generated with Claude Code

PLAT-04 (密钥仅存 OS 钥匙串): api_key gains a third indirection form
'keychain:ACCOUNT', resolved at config-parse time from the operating
system's credential store (macOS Keychain / Windows Credential Manager /
Linux Secret Service) — the file holds only the account name, and the
plaintext key reaches nothing on disk. Headless hosts fail loudly with
the storing command in the error; no silent plaintext fallback.

'mofa-engine keychain-set/get/delete --account …' manages entries
(value via stdin keeps keys out of shell history). Tests run against a
persistent in-process stand-in (keyring's own mock is EntryOnly, which
cannot round-trip); verified against the real macOS Keychain:
store → get → delete → system search confirms removal.

Co-Authored-By: Claude <noreply@anthropic.com>
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