Skip to content

keychain: run subcommand injects secrets into a child process's environment - #663

Open
clawdbot-glitch003 wants to merge 2 commits into
mainfrom
glitch003/keychain-run-env-inject
Open

clawdbot-glitch003 wants to merge 2 commits into
mainfrom
glitch003/keychain-run-env-inject

Conversation

@clawdbot-glitch003

@clawdbot-glitch003 clawdbot-glitch003 commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Bitwarden-style handoff for tools that need the raw credential:

keychain run <identity> <config> [--only A,B] [--env SECRET=ENV_VAR]... [--file SECRET=PATH]... -- <command> [args...]
  • Decrypts the config's export-release secrets (or the --only subset) in parallel and places each in the child's environment under the secret's name.
  • The CLI prints nothing. The child inherits stdio; SIGINT/SIGTERM/SIGHUP are forwarded; the CLI exits with the child's status (or 128+signal).
  • "Use inside Lit" secrets are skipped with a stderr note, and are an error under --only. Names that are not valid variable names must be mapped with --env; colliding mappings are rejected.
  • --file SECRET=PATH writes the secret to a new mode-0600 file (never overwrites) that is unlinked when the child exits, for tools that only read credentials from a path (service-account JSON, kubeconfig, SSH/TLS keys). Such secrets stay out of the environment unless --env names them too.
  • Attestation and credential-shape checks run exactly as for get. Bad arguments fail before any network call.
  • Agent key zeroed and value references dropped once the child has spawned.

Nothing changes in the protocol, server, or existing get/use/MCP paths. No MCP tool for run on purpose: an MCP tool that runs arbitrary commands with secrets injected is a prompt-injection footgun.

Docs

  • sdk/README.md, SKILL.md: usage and when to prefer run over get.
  • SECURITY.md: same-user processes can read another process's environment (ps eww, /proc/<pid>/environ); run is a handoff, not a sandbox. JS strings cannot be scrubbed, so plaintext may linger in the CLI heap briefly.
  • Web: stored-secret detail cards gain a "How agents use this secret" panel showing the run command.

Test plan

  • tests/sdk-package.test.ts: parsing, injection planning, spawn contract with a fake child (secrets land only in child env, parent env untouched, client destroyed, exit code propagates, missing executable reported without leaking), CLI rejects bad args before the network.
  • tests/api.test.ts: real CLI run against the mock Lit runtime, both env and --file (0600 while the child runs, absent from env, removed after); value reaches only the child under the --env name, original name absent, child exit code 7 propagates.
  • Full local suite via scripts/test-local.mjs on keychain_test DB: 3/3 integration, 62/62 unit.
  • tsc --noEmit, prettier --check, vite build.
  • POSIX only; Windows .cmd shims and signals untested.

🤖 Generated with Claude Code

…ironment

Bitwarden-style handoff for tools that need the raw credential: `keychain run
<identity> <config> [--only A,B] [--env SECRET=ENV_VAR]... -- <command>` decrypts
the config's export-release secrets, places each in the child's environment
under the secret's name, inherits stdio, forwards SIGINT/SIGTERM/SIGHUP, and
exits with the child's status. The CLI prints nothing, so the value stays out
of agent transcripts, shell history and logs. "Use inside Lit" secrets are
skipped with a stderr note (an error under --only), invalid variable names must
be mapped with --env, and colliding mappings are rejected.

Unit tests cover parsing, planning and the spawn contract with a fake child;
the Postgres integration test runs the real CLI against the mock Lit runtime
and checks the value reaches only the child. Docs: sdk/README.md, SKILL.md,
SECURITY.md (same-user process environments are readable; not a sandbox), and
a "How agents use this secret" panel on stored-secret detail cards.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@clawdbot-glitch003
clawdbot-glitch003 requested a review from a team September 16, 2026 01:29
… disk

Writes the secret to a new mode-0600 file before the child starts (open with
wx, so an existing path is never overwritten), keeps it out of the environment
unless --env names it too, and unlinks it when the child exits or fails to
start. Multi-line values such as PEM keys are written byte for byte. Mapping a
secret outside --only is now an error rather than silently ignored.

Tests: parser cases, planner cases (file-only secrets, file+env, path clash,
unlisted mapping), spawn contract against a real temp dir (0600 while the
child runs, gone after, refuses to overwrite, no spawn on create failure), and
the Postgres integration test runs the real CLI with --file. Docs in
sdk/README.md, SKILL.md, SECURITY.md and the web stored-secret panel.

Co-Authored-By: Claude Fable 5.1 <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.

2 participants