kz CLI

How the kz CLI works -- config format, secret resolution, blind mode, and shell hooks

kz is the keyzero CLI. It reads .keyzero.toml, resolves secrets from configured providers, and injects them as environment variables into a subprocess.

How It Works

kz run -- npm start
  1. Load config -- finds and reads .keyzero.toml (walks up from the current directory)
  2. Select environment -- checks KEYZERO_ENV (or NODE_ENV, RAILS_ENV, etc.) to activate environment-specific sections
  3. Resolve secrets -- calls each configured provider (keychain, 1Password, AWS, Vault, etc.) to fetch secret values
  4. Inject env vars -- sets the resolved values as environment variables on the subprocess
  5. Spawn subprocess -- runs the command with secrets available in its environment

.keyzero.toml Config

The config maps environment variable names to secret providers:

[secrets]
DATABASE_URL = { provider = "keychain", ref = "myapp-db-url" }
API_KEY      = { provider = "1password", ref = "op://Vault/myapp/api-key" }
AWS_SECRET   = { provider = "aws", ref = "sm://prod/aws-secret" }

[production.secrets]
DATABASE_URL = { provider = "aws", ref = "sm://prod/db-url" }
API_KEY      = { provider = "aws", ref = "sm://prod/api-key" }

Set KEYZERO_ENV=production to activate the [production.secrets] section, which overrides the default [secrets].

Local overrides

.keyzero.local.toml overrides .keyzero.toml for personal settings. Add it to .gitignore.

Global config

Provider defaults go in ~/.config/keyzero/config.toml.

Blind Mode

kz run --blind -- node agent.js

In blind mode, secrets are masked with tokens before being injected into the subprocess. A local MITM proxy intercepts outgoing HTTP/HTTPS requests and swaps the masked tokens for real values at the network boundary.

What the subprocess sees:

API_KEY=kz_masked_7f3a9b...

What hits the upstream API:

API_KEY=sk-proj-abc123...

The subprocess never sees raw secrets. This is designed for AI agents and other untrusted workloads.

Connection control

In .keyzero.toml, control which hosts the subprocess can reach:

[blind]
[[blind.connections]]
allow = true
host = "api.openai.com"

[[blind.connections]]
allow = false
host = "*"

Environment Variables Set by kz

In blind mode, the subprocess inherits these environment variables in addition to the secrets:

VariableValuePurpose
HTTP_PROXYhttp://127.0.0.1:<port>Route HTTP traffic through the proxy
HTTPS_PROXYhttp://127.0.0.1:<port>Route HTTPS traffic through the proxy
http_proxyhttp://127.0.0.1:<port>Lowercase variant for compatibility
https_proxyhttp://127.0.0.1:<port>Lowercase variant for compatibility
SSL_CERT_FILEPath to ephemeral CA certTrust the MITM CA (OpenSSL)
NODE_EXTRA_CA_CERTSPath to ephemeral CA certTrust the MITM CA (Node.js)
REQUESTS_CA_BUNDLEPath to ephemeral CA certTrust the MITM CA (Python requests)

CLI Flags

kz run [OPTIONS] -- <COMMAND>...
FlagDescriptionDefault
--only <KEYS>Resolve only the listed secret keysAll secrets
--clean-envStart with a clean environment (only injected secrets)false
--keep-env <KEYS>When using --clean-env, also keep these host env varsNone
--blindMask secrets with tokens; run MITM proxyfalse
--no-force-proxyIn blind mode, don't force-set proxy env varsfalse

Shell Hook

Auto-load secrets when you cd into a project directory:

# zsh
eval "$(kz hook --shell zsh)"

# bash
eval "$(kz hook --shell bash)"

# fish
kz hook --shell fish | source