secretsh 0.2.0

Secure subprocess secret injection for AI agents
Documentation

secretsh

Inject secrets from a .env file into subprocess arguments for AI agents.

CI Crates.io License: MIT

Honest summary: secretsh prevents secrets from appearing in LLM context and shell history. It does not encrypt secrets at rest, hide them from the child process, or block filesystem access. If the agent can read the .env file or the child's argv, it can read the secrets.

AI agents write commands with {{PLACEHOLDER}} tokens. secretsh resolves them from a .env file at exec time and scrubs any secrets that leak back through output.

Agent writes:  curl -u admin:{{API_PASS}} https://internal/api
Child runs:    curl -u admin:hunter2 https://internal/api
Agent sees:    curl -u admin:[REDACTED_API_PASS] https://internal/api

Install

CLI (Rust binary)

# Homebrew
brew tap lthoangg/tap && brew install secretsh

# Cargo
cargo install secretsh

Pre-built binaries for x86_64/aarch64 on macOS and Linux: GitHub Releases.

Python package

# pip
pip install secretsh

# uv
uv add secretsh

The Python package wraps the CLI binary — the secretsh binary must be installed separately (see above).


Quick Start

CLI

# Create a .env file
echo 'API_PASS=hunter2' > .env

# Run commands — secrets injected and scrubbed
secretsh --env .env run -- curl -u admin:{{API_PASS}} https://api.example.com

Python

import secretsh

result = secretsh.run(".env", "curl -u admin:{{API_PASS}} https://api.example.com")
print(result.stdout)      # curl output with [REDACTED_API_PASS] in place of the secret
print(result.exit_code)   # child process exit code

Usage

secretsh --env <.env-file> run [flags] -- <command>

Flags

Flag Default Purpose
--env required Path to the .env file
--no-shell off Block sh/bash/zsh/etc. as argv[0]. Recommended for AI agents.
--timeout 300s Kill child after N seconds
--max-output 50 MiB Kill child if stdout exceeds this
--max-stderr 1 MiB Kill child if stderr exceeds this
--quiet off Suppress audit JSON on stderr
--verbose off Show tokenization debug output

What it does and does NOT

Does

Keeps secrets out of LLM context Agent only ever sees {{PLACEHOLDER}}, never the value (assuming it can't read the .env file or child's argv)
Keeps secrets out of shell history secretsh reads from the .env file, not the command line
Scrubs output (best effort) Aho-Corasick substring redaction on stdout/stderr — raw, base64, URL-encoded, hex
Blocks shell oracle attacks --no-shell rejects sh/bash/zsh/etc. before any child runs

Does NOT

Protect the .env file It's plain text on disk. If the agent can read the file (e.g. cat .env), it can read the secrets. Use chmod 600 .env.
Block filesystem access If the agent has read access to .env, it can read secrets directly.
Hide secrets from the child process Secrets are injected as command-line arguments. The child process can read /proc/<pid>/cmdline or the .env file to see them. Output redaction catches most cases, but not all.
Stop prompt injection If the agent is tricked into running a malicious command, secretsh executes it
Handle common-value false positives If your secret is 123456, every 123456 in output is redacted
Fully close the redaction oracle echo {{KEY}}==guess leaks one bit per probe
Replace a secrets manager No access control, no rotation, no audit trail beyond local stderr JSON

Exit Codes

Code Meaning
0 Success
1–125 Child exit code (passthrough)
124 Timeout or output limit exceeded
125 secretsh error (tokenization, shell blocked)
126 Command not executable
127 Command not found
128+N Child killed by signal N

Documentation

Doc Content
docs/cli.md All flags, exit codes
docs/threat-model.md Full security model, oracle attacks, known limitations
docs/architecture.md Execution pipeline, memory hardening
docs/tokenizer.md Quoting rules, placeholder syntax
examples/ Runnable examples

Development

cargo test                    # 187 tests
cargo clippy -- -D warnings  # must be zero warnings
cargo fmt --check

License

MIT · Contributing · Security