plan: clarify phased secrets implementation

This commit is contained in:
Keisuke Hirata 2026-06-01 06:19:31 +09:00
parent 50ccf0f21b
commit faac237f0d
No known key found for this signature in database
2 changed files with 162 additions and 43 deletions

View File

@ -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. `<data_dir>/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 <id> --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.

View File

@ -147,4 +147,41 @@ Implications for implementation planning:
- Provider/WebSearch integration should resolve `secret_ref` by direct key lookup only.
---
<!-- event: plan author: hare at: 2026-05-31T21:19:29Z -->
## 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.
---