Problem
Setting a provider or connector token mid-session means leaving the TUI and running codewhale auth set in another terminal. The agent asks for a GitHub token; the person has to break flow to provide one.
The obvious fix — let them paste it into the composer — is the one thing we must not build. A secret typed into the composer becomes a user message, and from there it reaches the transcript, the session event log, the compaction summary, and the model's context. That is a credential disclosure, not a UX wart.
What already exists (do not rebuild)
The storage half is done and is good:
codewhale-secrets: KeyringStore trait, FileKeyringStore default at ~/.codewhale/secrets/, opt-in OS keyring via CODEWHALE_SECRET_BACKEND=system|keyring, InMemoryKeyringStore for tests
Secrets::resolve precedence: config -> secret store -> env
- CLI
codewhale auth set --provider <p> already does a hidden prompt, plus --api-key-stdin. Never a CLI argument, so it never reaches shell history
zeroize::Zeroizing is already used on the credential handoff path
The OS keyring is deliberately NOT the default, and that reasoning should be preserved: on macOS every unsigned or rebuilt binary is a new Keychain ACL principal, so credentials written by one build stop being readable by the next. crates/secrets/src/account.rs bars account sessions from Keychain entirely and asserts it in a test.
What to build
An in-session secret entry surface (a slash command opening a modal, or equivalent) that writes straight to the existing secret store.
The whole value is in the boundary, so it has to be built as a boundary rather than an input field:
- the modal's buffer is never appended to the transcript
- it is never written to the session event log or any replay artifact
- it never reaches compaction or any model request
- it is never echoed to the terminal
- it is held in
Zeroizing and dropped immediately after the store write
- paste is supported, because these are long tokens
Reuse CWC_PROVIDER_KEY_SHAPES-style pre-save shape hints where they exist, so an obviously truncated key is caught before it is stored rather than failing later as an opaque 401.
Acceptance
The test that matters is adversarial, not a happy path: enter a known sentinel value through the modal, then assert that the sentinel appears in no session artifact — not the transcript, not the event log, not a compaction summary, not any outbound model request body — and that it is readable back from the secret store. A feature that passes a happy-path test and fails this one is worse than not shipping it.
Desktop (GPUI) has credentials.rs doing the equivalent paste-and-forget for provider keys already; the TUI is the gap.
Problem
Setting a provider or connector token mid-session means leaving the TUI and running
codewhale auth setin another terminal. The agent asks for a GitHub token; the person has to break flow to provide one.The obvious fix — let them paste it into the composer — is the one thing we must not build. A secret typed into the composer becomes a user message, and from there it reaches the transcript, the session event log, the compaction summary, and the model's context. That is a credential disclosure, not a UX wart.
What already exists (do not rebuild)
The storage half is done and is good:
codewhale-secrets:KeyringStoretrait,FileKeyringStoredefault at~/.codewhale/secrets/, opt-in OS keyring viaCODEWHALE_SECRET_BACKEND=system|keyring,InMemoryKeyringStorefor testsSecrets::resolveprecedence: config -> secret store -> envcodewhale auth set --provider <p>already does a hidden prompt, plus--api-key-stdin. Never a CLI argument, so it never reaches shell historyzeroize::Zeroizingis already used on the credential handoff pathThe OS keyring is deliberately NOT the default, and that reasoning should be preserved: on macOS every unsigned or rebuilt binary is a new Keychain ACL principal, so credentials written by one build stop being readable by the next.
crates/secrets/src/account.rsbars account sessions from Keychain entirely and asserts it in a test.What to build
An in-session secret entry surface (a slash command opening a modal, or equivalent) that writes straight to the existing secret store.
The whole value is in the boundary, so it has to be built as a boundary rather than an input field:
Zeroizingand dropped immediately after the store writeReuse
CWC_PROVIDER_KEY_SHAPES-style pre-save shape hints where they exist, so an obviously truncated key is caught before it is stored rather than failing later as an opaque 401.Acceptance
The test that matters is adversarial, not a happy path: enter a known sentinel value through the modal, then assert that the sentinel appears in no session artifact — not the transcript, not the event log, not a compaction summary, not any outbound model request body — and that it is readable back from the secret store. A feature that passes a happy-path test and fails this one is worse than not shipping it.
Desktop (GPUI) has
credentials.rsdoing the equivalent paste-and-forget for provider keys already; the TUI is the gap.