diff --git a/work-items/open/20260529-145355-manifest-profile-encrypted-secrets/item.md b/work-items/open/20260529-145355-manifest-profile-encrypted-secrets/item.md index aac961c6..65cce8da 100644 --- a/work-items/open/20260529-145355-manifest-profile-encrypted-secrets/item.md +++ b/work-items/open/20260529-145355-manifest-profile-encrypted-secrets/item.md @@ -1,64 +1,146 @@ --- id: 20260529-145355-manifest-profile-encrypted-secrets slug: manifest-profile-encrypted-secrets -title: Encrypted secret store for manifest profiles +title: Manifest/Profile: local key-value secret store status: open kind: feature priority: P2 -labels: [manifest, profiles, secrets, security] +labels: [manifest, profiles, secrets, security, cli, tui] created_at: 2026-05-29T14:53:55Z -updated_at: 2026-05-31T21:04:45Z +updated_at: 2026-05-31T21:19:29Z assignee: null legacy_ticket: null --- ## Background -WebSearch/WebFetch made API keys more visible as a UX problem: `WebSearch` currently expects `web.search.api_key_env`, so users must export `BRAVE_SEARCH_API_KEY` before starting the Pod/TUI process. That is inconvenient for long-lived Pods, profile switching, and per-project/provider configuration. +Credential configuration still relies on process environment variables in important paths: -This should not be solved by adding `.env` loading as an implicit side effect. `.env` files are easy to leak into projects, do not solve profile-specific credential selection cleanly, and still expose secrets through process environments. Instead, when manifest profiles are designed/implemented, add a first-class encrypted secret store that manifests/profiles can reference. +- provider API keys use `AuthRef::ApiKey { env, file }` and provider-default `INSOMNIA_API_KEY_*` names; +- WebSearch currently uses `web.search.api_key_env`; +- normal runtime intentionally does not load `.env` files. -Related work item: `work-items/open/20260527-000022-manifest-profiles/item.md`. +This should not be solved by implicit `.env` loading. `.env` files are easy to leak into projects, do not solve profile-specific credential selection cleanly, and still expose secrets through process environments. + +The desired replacement is a local key-value secret store plus explicit references from manifest/profile/tool configuration. + +The security target is intentionally modest. This is not a high-assurance password manager. The goal is to avoid casual plaintext exposure and generic environment-variable scraping, not to defend against a local attacker with the user's account or process memory access. + +## Intent + +Implement a provider-independent local key-value secret store and use it as the normal credential path for provider and WebSearch credentials. + +The logical store model is just: + +```text +{ + "anthropic/default" = "sk-..." + "web/brave/default" = "..." +} +``` + +The store must not know that a key is Anthropic, Brave, OpenAI, or any other provider-specific kind. Provider/model/tool configuration chooses which key to reference. ## Requirements -- Design a typed secret reference format for manifest/profile fields that need credentials. - - Add a new encrypted-store reference form, e.g. `api_key_secret = "brave.search.default"` or a more general `SecretRef` enum. - - Existing env references such as `api_key_env = "BRAVE_SEARCH_API_KEY"` may be supported only as a migration/compatibility input during the transition; the target state is to remove credential environment-variable configuration rather than keep it as a normal fallback. - - Secret references must be explicit in resolved config; do not silently read arbitrary `.env` files. -- Add an encrypted local secret store suitable for API keys/tokens. - - Store secrets outside tracked project files by default, under the user data/config directory. - - Use authenticated encryption and atomic writes. - - Do not log plaintext secrets, include them in session logs, expose them to model context, or return them through normal tool output. - - Keep encrypted blobs out of git-managed work-items/memory/session records. -- Integrate with manifest profiles. - - Profiles should be able to select different secret names for different roles/providers, e.g. Orchestrator/Coder/Researcher or web search provider variants. - - Profile resolution should validate that referenced secrets exist or produce a clear startup/tool diagnostic. - - A profile switch must not require restarting the shell just to change API keys. -- Provide a small CLI/TUI management surface. - - Add/update/list/delete secrets without printing plaintext by default. - - Support non-interactive set from stdin for scripts. - - Show references and metadata, not secret values. - - Consider migration helpers from existing env-var based configuration, but keep migration optional. -- Update credential consumers. - - WebSearch should use encrypted secret refs instead of requiring env vars. - - Provider API keys/tokens and future hosted/search credentials should use the same mechanism. - - Remove env-var credential configuration from the normal supported path once encrypted secret refs and migration diagnostics exist. -- Security and UX constraints. - - Fail closed when a referenced secret is missing or cannot be decrypted. - - Diagnostics should name the missing reference, not the secret value. - - Do not add hidden context injection or history mutation for secret resolution. - - Document the threat model and limitations, including OS account access and backup implications. +### Store model + +- Add a local secret/key store that maps a validated string id to a secret string value. +- Keep the user-visible logical schema provider-independent: `id -> value`. +- Do not add provider-specific slots, credential kinds, or required metadata to the store schema. +- Technical envelope fields needed for versioning/nonce/ciphertext/checksum are allowed, but they must not become user-facing semantic metadata. +- Store data outside the repository, under the user data directory, e.g. `/secrets/store.json` or equivalent. +- Use atomic writes. +- Validate ids: + - reject empty ids; + - reject path traversal / absolute-path-like ids; + - reject control characters; + - bound length; + - allow a conservative useful set such as ASCII alnum plus `._/-`. + +### Obfuscation / encryption stance + +- Apply lightweight encryption or obfuscation at rest so the file is not a casual plaintext key dump. +- Do not claim strong local security guarantees. +- Do not introduce OS keychain dependency or interactive passphrase UX in this ticket. +- Do not store plaintext values in logs, work items, session history, diagnostics, or normal command output. +- Decryption/decoding failures must fail closed and name only the key id, not the value. + +### `insomnia keys` TUI management + +- Add `insomnia keys` as an interactive TUI key manager. +- The product CLI owner (`insomnia` crate) routes the subcommand. +- Use the TUI implementation crate for the terminal screen if practical. +- Minimum UI features: + - list key ids; + - add/set a key; + - delete a key with confirmation; + - quit. +- Do not display plaintext values in the list or normal screen output. +- During add/set, mask the value input or otherwise avoid echoing plaintext. +- Scriptable commands such as `insomnia keys set --stdin` may be added if convenient, but the required user surface is the TUI manager. + +### Config references / consumers + +- Use explicit secret references from configuration. +- Existing `AuthRef::SecretRef { ref_ }` in manifest model should resolve through the new store for provider API keys. +- WebSearch must gain an explicit secret reference path so Brave search can be configured without `BRAVE_SEARCH_API_KEY` / `api_key_env`. + - Prefer a generic auth/secret-ref shape if it stays small. + - A focused `api_key_secret = "web/brave/default"` field is acceptable if it avoids a broad schema redesign. +- Secret refs are resolved at the consumer/runtime boundary only; resolved config/debug output must not contain plaintext. +- The store must not implicitly choose default keys based on provider name. No ambient lookup like "anthropic automatically reads anthropic/default" unless the profile/config explicitly references it. + +### Env credential migration + +- Do not load `.env` files. +- Do not add new credential environment variables. +- Existing env credential paths may remain temporarily as compatibility/migration input during this ticket if removing all of them would make the change too large. +- If env credential paths remain, docs and diagnostics should prefer `insomnia keys` + secret refs as the normal path. +- The target state is to remove credential env configuration from normal profile use in a follow-up or final phase of this ticket if feasible. + +## Phases within this ticket + +1. Core store + - key-value store API; + - id validation; + - lightweight encrypted/obfuscated file format; + - atomic load/save; + - focused tests. +2. `insomnia keys` TUI manager + - list/add/delete; + - masked input; + - no plaintext display. +3. Provider integration + - implement provider `AuthRef::SecretRef` resolution through the store; + - keep plaintext in memory only; + - fail closed on missing/invalid/decode failures. +4. WebSearch integration + - add a secret-ref credential path; + - make Brave search usable without env credentials. +5. Docs and migration + - update `docs/environment.md` and manifest/profile docs; + - document the modest security target honestly; + - point users to `insomnia keys` and secret refs as the normal credential path. + +## Non-goals + +- A high-assurance password manager. +- OS keychain integration. +- Passphrase prompt UX. +- Provider-specific secret-store schema. +- Automatic provider-name-to-secret-id lookup. +- Loading `.env` files. +- Changing Codex OAuth behavior unless a narrow integration need appears. +- Reworking model/provider catalog ownership. ## Acceptance criteria -- Manifest/profile schema has a typed credential reference for encrypted secret-store entries; env-var credential inputs are at most transitional migration inputs, not the final supported configuration path. -- Encrypted secret-store files are created outside the repository by default and use authenticated encryption with atomic update behavior. -- A user can add/list/delete a Brave Search API key in the secret store and configure `WebSearch` to use it without exporting an environment variable. -- Resolved configuration and diagnostics never display plaintext secrets. -- Missing/decryption-failed secrets produce clear fail-closed errors. -- Existing env-var based credential configuration is either removed or produces an explicit migration diagnostic after encrypted secret references are available. -- Documentation explains how profiles reference secrets, how to manage them, and why credential env vars are no longer the normal path. -- Focused tests cover config parsing/resolution, missing secret diagnostics, no-plaintext serialization/logging paths, and WebSearch secret resolution. -- `cargo fmt --check` -- Relevant manifest/provider/tools/pod tests pass. +- A user can run `insomnia keys` and manage key ids interactively. +- The store persists key-value entries under the user data directory without plaintext values in the on-disk file. +- Store id validation rejects unsafe ids. +- Provider `AuthRef::SecretRef` resolves through the store and does not print/serialize plaintext. +- WebSearch can use a configured secret ref without exporting an environment variable. +- Missing key, invalid id, unreadable store, and decode/decrypt failure produce clear fail-closed errors naming only the key id. +- `docs/environment.md` no longer presents credential env vars as the normal path and documents the limited protection goal. +- Focused tests cover store round-trip, id validation, decode failure, provider secret-ref resolution, WebSearch secret-ref resolution, and no-plaintext debug/serialization paths where applicable. +- `cargo fmt --check`, relevant crate tests/checks, `./tickets.sh doctor`, and `git diff --check` pass. diff --git a/work-items/open/20260529-145355-manifest-profile-encrypted-secrets/thread.md b/work-items/open/20260529-145355-manifest-profile-encrypted-secrets/thread.md index 221a7554..78edba93 100644 --- a/work-items/open/20260529-145355-manifest-profile-encrypted-secrets/thread.md +++ b/work-items/open/20260529-145355-manifest-profile-encrypted-secrets/thread.md @@ -147,4 +147,41 @@ Implications for implementation planning: - Provider/WebSearch integration should resolve `secret_ref` by direct key lookup only. +--- + + + +## Plan + +Preflight update: implementation-ready as a single phased ticket. + +Finalized intent: +- Build a provider-independent local key-value secret store and wire it into provider/WebSearch credential resolution. +- Add `insomnia keys` as the required interactive TUI management surface. +- Keep the security claim modest: avoid casual plaintext exposure and generic env scraping; do not claim strong local security. + +Settled decisions: +- Store model is user-visible `id -> value`; no provider-specific slots and no required metadata. +- Store may use technical envelope fields for version/nonce/ciphertext/checksum, but those are implementation details. +- No OS keychain, passphrase UX, or high-assurance key-management dependency in this ticket. +- No automatic provider-name-to-secret-id lookup. Config must explicitly reference a key id. +- This is one ticket with phases, not separate tickets. + +Suggested implementation order: +1. Add a focused secret store crate/module with id validation, obfuscated/encrypted persistence, atomic writes, and tests. +2. Add `insomnia keys` TUI manager for list/add/delete with masked input. +3. Implement provider `AuthRef::SecretRef` resolution through the store. +4. Add WebSearch secret-ref configuration path. +5. Update docs and migration messaging. + +Critical risks for coder/reviewer: +- Accidentally turning the store into provider-aware schema. +- Displaying plaintext in Debug/errors/TUI list output. +- Overstating security guarantees in docs. +- Adding ambient defaults that recreate the env-var problem in secret-store form. +- Expanding into OS keychain/passphrase design despite the settled modest threat model. + +Validation should include focused tests for id validation, store round-trip, decode failure, provider secret-ref resolution, WebSearch secret-ref resolution, and no plaintext in display/debug paths where applicable. + + ---