Secure subprocess secret injection for AI agents
Project description
secretsh
Inject secrets from a .env file into subprocess arguments for AI agents.
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
.envfile 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
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file secretsh-0.2.0-py3-none-any.whl.
File metadata
- Download URL: secretsh-0.2.0-py3-none-any.whl
- Upload date:
- Size: 6.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
087a71dbd52bb53f8249b0c327cfe5f32ce19e86c0f7dc587bca935b0a2bd50a
|
|
| MD5 |
7c5fca8c7c1cfe229508700c4bfcfbf7
|
|
| BLAKE2b-256 |
a4283a753332ff512e26ee4f2d65501c74cf4b7dc722895ca8ffdd730aae86aa
|
Provenance
The following attestation bundles were made for secretsh-0.2.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on lthoangg/secretsh
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
secretsh-0.2.0-py3-none-any.whl -
Subject digest:
087a71dbd52bb53f8249b0c327cfe5f32ce19e86c0f7dc587bca935b0a2bd50a - Sigstore transparency entry: 1328852378
- Sigstore integration time:
-
Permalink:
lthoangg/secretsh@a9bd676146e00b9efc0881968fb94ecf1a21f63f -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/lthoangg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@a9bd676146e00b9efc0881968fb94ecf1a21f63f -
Trigger Event:
release
-
Statement type: