yoi/work-items/closed/20260529-145355-manifest-profile-encrypted-secrets/item.md
2026-06-01 07:23:54 +09:00

7.7 KiB

id: 20260529-145355-manifest-profile-encrypted-secrets slug: manifest-profile-encrypted-secrets title: Manifest/Profile: local key-value secret store status: closed kind: feature priority: P2 labels: [manifest, profiles, secrets, security, cli, tui] created_at: 2026-05-29T14:53:55Z updated_at: 2026-05-31T22:23:34Z assignee: null legacy_ticket: null

Background

Credential configuration still relies on process environment variables in important paths:

  • 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.

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:

{
  "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

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 removal

  • Do not load .env files.
  • Do not add new credential environment variables.
  • Do not keep migration/backward-compatibility behavior for credential env config in the normal profile path.
  • Remove credential env configuration from normal provider/WebSearch use as part of this ticket.
  • Docs and diagnostics should point users to insomnia keys + secret refs as the credential path.

Codex OAuth relationship

  • Codex OAuth is not part of this key-value secret store in this ticket.
  • Current Codex OAuth intentionally interoperates with Codex CLI's auth.json file and refresh behavior; that file contains a structured token bundle, not a single provider API key string.
  • Do not store or refresh Codex OAuth token bundles through the key-value store as part of this ticket.
  • Do not change CODEX_HOME / $HOME/.codex lookup behavior in this ticket.
  • A future Insomnia-owned Codex login/token store could be designed separately if needed, but it should be a dedicated OAuth token-store design, not an implicit use of the simple key-value API-key store.

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 env removal
    • update docs/environment.md and manifest/profile docs;
    • document the modest security target honestly;
    • point users to insomnia keys and secret refs as the credential path;
    • remove credential env configuration from normal provider/WebSearch docs and code paths.

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. Codex OAuth remains an external structured token-source integration in this ticket.
  • Reworking model/provider catalog ownership.

Acceptance criteria

  • 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, removes normal provider/WebSearch credential env configuration, 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.