About secret
secret is a local secret-handling CLI, names-only MCP server, and agent guard for coding agents. It lets agents select credentials for child processes without exposing values, and can redact loaded values from command output; its protections are intended for accident prevention, not as a sandbox.
Key features
- 01Names-only secret metadata
- 02Run commands with selected keys
- 03Redact values from command output
- 04Hooks to block risky secret reads
- 05Agent guidance skill
Use cases
- 01Run a deployment command with selected credentials
- 02Let an agent identify available keys without viewing values
- 03Reduce accidental secret disclosure in agent shell use
MCP support
Its opt-in local stdio MCP server lists, shows, diffs, and checks the health of explicitly approved stores. It exposes metadata only, never secret values.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
We haven’t collected a package or remote endpoint for this server. Follow the instructions in the project README.
Tools
Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.
Details
From the project READMEsecret
Let coding agents use your credentials without reading them.
An agent with a shell can cat .env, run printenv, or grep a token out of a
config file, and the value lands in its context, its logs, and possibly a
provider's servers. secret gives the agent a sanctioned path instead: it sees
key names and masked lengths, picks the keys a command needs, and the values go
straight into that child process's environment.
Three parts, usable together or alone:
| Part | What it does |
|---|---|
bin/secret | CLI. Parses KEY=VALUE stores (never sources them), shows masked names, runs a child with selected keys, and redacts every loaded value from the child's stdout and stderr. |
hooks/guard-secrets | Claude Code and Codex PreToolUse hook. Denies reads of store files, environment dumps, credential printers (gh auth token, security -w, pbpaste, ...) and searches that would print store contents. |
skills/secrets/SKILL.md | Tells the agent how to work with the CLI and how to recover from a guard denial without asking for values in chat. |
What this is not
Accident prevention, not a sandbox. It stops an agent from reading a secret
by habit or by mistake. It does not stop a determined or compromised agent with
a shell: transformed output (base64, URL encoding, substrings) is not redacted,
shell indirection the guard cannot parse is allowed unless it names a protected
file or operation, MCP search tools are not covered, and the guard makes no
decision when its input is malformed (without jq it denies every request).
Treat it as one layer, not the boundary.
Install
Opt-in Go CLI and names-only MCP
Canonical setup covers installation, approved store references, per-client registration, verification, updates and removal. The v0.3.2 release is being prepared; these pinned commands require its published assets and never select latest or fall back to another version.
Prerequisites: authenticated GitHub CLI gh, plus curl and Python 3 on Unix or
curl.exe on Windows. Install tools through trusted channels. Run gh auth login
yourself in a private terminal, or supply GH_TOKEN through your trusted
credential environment without displaying it. Never paste a token into chat
or client configuration.
sh -c 'umask 077; d=$(mktemp -d) || exit
curl -fsSL https://raw.githubusercontent.com/avhn/secret/v0.3.2/install.sh -o "$d/install.sh" && sh "$d/install.sh" "$@"
status=$?; rm -rf "$d"; exit "$status"' sh
Windows PowerShell:
& { $ErrorActionPreference = 'Stop'; $d = Join-Path ([IO.Path]::GetTempPath()) ([guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Path $d | Out-Null
try { irm https://raw.githubusercontent.com/avhn/secret/v0.3.2/install.ps1 -OutFile "$d\install.ps1" -ErrorAction Stop; & (Get-Process -Id $PID).Path -NoProfile -ExecutionPolicy Bypass -File "$d\install.ps1" @args; $status = $LASTEXITCODE }
finally { Remove-Item -LiteralPath $d -Recurse -Force }; exit $status }
These download-first snippets run only after the download succeeds and preserve
failure through cleanup. Append installer flags after the final sh or closing
PowerShell brace; see setup for details. PowerShell runs
the file in a child of the same host with -NoProfile -ExecutionPolicy Bypass
and propagates its exit code after cleanup. The bypass applies only to that one
child process, not any persistent policy; centrally enforced Group Policy can
still take precedence.
If an installer reports
secret: GitHub CLI is not authenticated; run gh auth login or set GH_TOKEN, then retry,
authenticate gh privately or correct its trusted environment, then retry without
pasting or printing a token.
The bootstrap trusts GitHub HTTPS, repository/tag governance and the reviewed
installer before verification. Both require gh and verify release attestations,
not checksums alone; SECRET_RELEASE_COMMIT optionally pins an independently
authenticated full C. Unix also needs curl and Python 3; Windows needs curl.exe.
Download/review/run or a trusted checkout offers stronger bootstrap review.
Installers publish only secret-go / secret-go.exe and print the absolute path.
Unix does not edit PATH; Windows adds only its directory to user PATH. Neither
registers MCP, reads stores or activates hooks. Raw executables do not have the
checkout launcher's per-execution rehash.
Paste to your agent: Follow https://github.com/avhn/secret/blob/v0.3.2/docs/setup.md to install and register secret MCP; ask me for exact store references, never discover/read stores, handle values or bypass a guard.
| Surface | Support and evidence |
|---|---|
| Go CLI | v0.3.2 targets Linux, macOS and Windows amd64/arm64; new native candidate/public-install gates remain required. v0.2.3 Linux/macOS publication is complete. |
| Local stdio MCP | Opt-in mcp --store STORE_REFERENCE; list/show/diff/health only, never values. Metadata can still reveal names, labels, paths and account IDs. |
| Desktop bundle | secret-v0.3.2-desktop.mcpb: macOS universal and Windows amd64; locked-CLI/native smoke validation is not host-tested Desktop installation. |
| Registry | Name io.github.avhn/secret; publication is separately owner-gated, not claimed here. |
| Plugins and hooks | All four manifests are 0.3.2; installed hooks remain the Bash guard. M4.1 Go activation is waiting sponsorship, with no fail-closed host claim. |
See installation and rollback for pins, dependencies, offline options and failure recovery, and release approvals.
Bash CLI, guard and skill (installed defaults)
Requirements: bash 3.2 or newer (stock macOS bash works), perl, and jq for the guard. These remain separate from the opt-in Go executable and MCP registration.
The CLI:
git clone https://github.com/avhn/secret ~/.local/share/secret
ln -s ~/.local/share/secret/bin/secret ~/.local/bin/secret
The guard and skill, as a Claude Code plugin:
/plugin marketplace add avhn/secret
/plugin install secret@secret
For Codex CLI, install the plugin from this repository's Codex marketplace.
It bundles the guard and skill, without modifying ~/.codex/hooks.json:
codex plugin marketplace add avhn/secret
codex plugin add secret@secret
In Codex, open /hooks and trust the installed secret hook before relying on
it. Untrusted hooks are listed but do not run; review and re-trust the hook
when it changes. /plugins lets you inspect the installed plugin. The plugin
does not put bin/secret on PATH. Install the CLI separately if you want to
use it (the symlink requires ~/.local/bin on PATH):
mkdir -p ~/.local/bin
# If you have not already cloned the repo in the CLI step above:
git clone https://github.com/avhn/secret ~/.local/share/secret
ln -s ~/.local/share/secret/bin/secret ~/.local/bin/secret
If your Codex version has no plugin marketplace support, use the manual route with a checkout of the repo. Copy the hook template with an absolute command path and install the skill in the user skills root:
mkdir -p ~/.codex ~/.codex/skills
jq --arg command "$HOME/.local/share/secret/hooks/guard-secrets" \
'.hooks.PreToolUse[].hooks[].command = $command' \
~/.local/share/secret/codex/hooks.json >~/.codex/hooks.json
ln -s ~/.local/share/secret/skills/secrets ~/.codex/skills/secrets
If ~/.codex/hooks.json already exists, merge its PreToolUse groups rather
than replacing the file. Codex also accepts inline [hooks] in config.toml;
the snippet uses hooks.json so it can be copied without changing that file.
Open /hooks to trust the manual hook as well. Codex sends shell commands
under the Bash hook name and patches under apply_patch. The guard needs
jq and perl on the hook's PATH.
The guard allows plain rg because ripgrep can be told to skip store files.
Install the protective config, or merge its globs into your own:
mkdir -p ~/.config/ripgrep
cp ~/.local/share/secret/examples/ripgreprc ~/.config/ripgrep/config
export RIPGREP_CONFIG_PATH="$HOME/.config/ripgrep/config" # in your shell profile
Opt-in age stores in the staged Go CLI
M5d adds explicit age:NAME encrypted file stores on Linux/macOS, with reviewed
filippo.io/age v1.3.2, whole-store authenticated reads and ciphertext-only
atomic writes. age-init is create-only and age-rekey verifies the new identity
before publication; both require real terminals and exact target confirmation.
Key files must be private under HOME/.secrets/.age-keys/, and value-consuming
commands require explicit --age-identity FILE. Names-only interfaces need no
identity, but their outer metadata is unauthenticated. Use list --backend age;
default discovery and the Bash CLI are unchanged. Windows age filesystem
operations remain unsupported even though M5a's separate Windows CLI gate passed.
See age setup, migration, recovery and limits. M5d passed its hosted
gate in PR #28; this does not activate hooks or authorize publication.
Usage
Stores use KEY=VALUE lines: ~/.secrets.env and ~/.secrets/*.secret
by default (SECRETS_ENV and SECRETS_DIR override them). Exact section
headers such as [env=dev,region=eu] scope following assignments; [] resets
to unlabeled. Other bracket lines are ignored. Labels use identifier names and
values made of letters, digits, underscores, dots, or hyphens (starting with a
letter, digit, or underscore). A labeled file is no longer plain dotenv for
other tools. Older secret versions read all sections as one flat store, so
upgrade before introducing headers.
secret list # store paths, never contents
secret show ~/.secrets.env # API_TOKEN=<40 bytes>
secret stash ~/.secrets.env API_TOKEN # hidden prompt, atomic write
secret run -f ~/.secrets.env --key API_TOKEN -- ./deploy.sh
secret show --env dev ~/.secrets.env # selected masked keys and unset keys
secret stash --env dev ~/.secrets.env API_TOKEN
secret run -f ~/.secrets.env --env dev --key API_TOKEN -- ./deploy.sh
Explicit keychain:NAME and libsecret:NAME references use keystore stores;
file:PATH forces a literal file path, including paths that look like backend
references. Bare names and paths still use file stores. Keystore names must
contain only letters, digits, underscores, dots, and hyphens (and start with
an alphanumeric or underscore). Each keystore store has a private, atomically
written ~/.secrets/NAME.keys manifest containing only key names, canonical
labels, and random account identifiers, never values. list and show use
only the manifest; show displays <stored> instead of a byte count. run
and exec load every registered item before selecting keys, so failed lookups
stop the child and run can redact values from unselected environments.
get and clip fetch only the selected item. A failed manifest publish after
a successful backend put can leave an unregistered keystore item.
On Linux, libsecret:NAME uses secret-tool and an unlocked Secret Service
session. Values are sent to the provider through stdin and retrieved into a
private file. Stash accepts values without a terminal newline or with one
terminal newline (which it removes). Empty values, embedded newlines or CR,
NUL, leading or trailing whitespace, and wrapping quotes are rejected.
On macOS, keychain:NAME uses the user's default Keychain, or an explicit
SECRET_KEYCHAIN absolute keychain path (only letters, digits, _, ., /,
and -; paths requiring quoting are rejected). Values are hex-encoded into a
security -i stdin command, never placed in process arguments. Keychain stash
accepts printable UTF-8 text without Unicode controls or line separators;
values outside this subset are rejected because security -w is a text interface. A private-file readback
must match before the manifest is published. Reads strip exactly the one newline
added by security -w. The test stub remains available for isolated tests;
never use it with real credentials.
--env dev means --label env=dev; repeat --label name=value to select
multiple labels. Without selectors only unlabeled records qualify. For each key,
the most specific matching label set wins; equally specific distinct matches
fail as ambiguous. show without selectors lists every record with canonical
sorted labels and masked lengths. stash updates only the exact label set,
while get and clip select the same record as run.
run exports only the selected keys (--key required, --optional-key
optional; no selector exports all), and replaces every loaded value in the
child's output with <redacted>. Prefer passing values on stdin, since a child
that puts a value in its own argv exposes it to the process list:
secret run -f ~/.secrets.env --key API_TOKEN -- bash -c \
'printf "Authorization: Bearer %s\n" "$API_TOKEN" | curl -fsS -H @- https://api.example.test'
Human-only commands, which the guard denies to agents:
secret get FILE KEYprints a value, and only to a terminal.secret exec ...isrunwithout redaction, for interactive tools and MCP server launchers that need byte-exact stdio.
secret clip FILE KEY [SECONDS] copies a value and clears the clipboard after
45 seconds unless you have copied something else since. The value never reaches
the agent's output, so the guard allows it; the skill tells the agent to use it
only when you ask for the clipboard, and reading the clipboard back is denied.
Run secret help for the full reference, including exit statuses.
Staged Go operator workflow (M3)
M3 passed its hosted CI gate (PR #8). These commands are implemented in the Go
CLI, while the shipped bin/secret remains Bash; this is not a CLI or plugin cutover. Build
go build -o /absolute/private/build/secret-go ./cmd/secret to try the staged CLI.
The examples below use secret-go for that binary and placeholder store paths.
Invoke operators directly in your own terminal, never through agent tools.
secret-go import --from dotenv:/absolute/source.env --into file:/absolute/team.secret
secret-go import --from op:OPAQUE_ID --into file:/absolute/team.secret --map password=API_TOKEN
secret-go export --from file:/absolute/team.secret --to libsecret:team --on-conflict=fail
secret-go rotate file:/absolute/team.secret API_TOKEN --env prod
secret-go rename file:/absolute/team.secret API_TOKEN SERVICE_TOKEN --env prod
secret-go delete file:/absolute/team.secret SERVICE_TOKEN --env prod
secret-go show --format=json file:/absolute/team.secret
secret-go diff --format=json --env dev --env prod file:/absolute/team.secret
secret-go doctor --format=json libsecret:team
Operators require both stdin and stdout terminals before store access, then exact
hidden confirmation of every target/key/label identity on the controlling terminal.
There is no --yes, piped confirmation or operator JSON. Rotation reads the new
value hidden, never in argv. Lifecycle labels identify the exact record. References
must be explicit file:ABS_PATH, keychain:NAME or libsecret:NAME. Import/export
are insert-only; op/bw imports require explicit field mappings. Export requires
--on-conflict=fail, a different implemented keystore destination and explicit
selectors for labeled records; without selectors only unlabeled records qualify.
--dry-run lists names/conflicts only and still requires real terminals. Doctor
reports tracked recovery candidates and manual verification guidance, not a proven
or complete historical orphan inventory or installed-host trust.
Private templates use literal {{KEY}} placeholders and selected keys only:
secret-go run -f file:/absolute/team.secret --key API_TOKEN \
--template-file /absolute/client.template -- ./consumer
The child receives the rendered path in SECRET_TEMPLATE_FILE, never file contents
in argv. Output still redacts every loaded literal value. Files live under the fixed
HOME/.secrets/.rendered/<random>/payload, independent of SECRETS_DIR, with 0700
directories and a 0600 payload. Missing/unselected placeholders, unsafe aliases,
exec and output-directory options are rejected. The supervisor stops its process
group before normal/error/catchable-signal cleanup. SIGKILL, crashes or power loss
can leave protected plaintext; cleanup is best effort, not secure erasure.
list/show --format=json, diff, doctor, completion and audit queries expose only
metadata, never values, hashes or lengths. Metadata may reveal key names, labels,
backend kind, store paths and provider account IDs. Legacy text show retains its
masked byte lengths and ordering. Completion emits literal candidates, not shell
code, and reads only explicit -f scopes. Audit is off unless an operator receives
--audit-dir ABS_PATH; query with audit --directory ABS_PATH --format=json.
Names-only events rotate across four private 0600 files capped at 1 MiB each.
MCP is off until explicitly allowlisted, for example
secret-go mcp --store file:/absolute/team.secret --store libsecret:team.
The stdio server exposes only list/show/diff/health, never values or mutation;
requests cannot expand that exact allowlist. Configuring it permits the path and
account metadata above to reach its clients. No network listener or ambient store
discovery is enabled. The unchanged installed hooks conservatively deny new
subcommands, including names-only ones; this feature does not grant an agent a
hook exception. M5a's native Windows CLI, console, storage, Job and Credential
Manager gate passed; Windows support states its limits and the
separate pending release-asset evidence. No installed-host protection is claimed.
See CONTRIBUTING.md for gates and the M3 plan
for the implementation contract.
Tests
bash tests/test-secret.sh # CLI matrix, needs perl, python3 and rg
bash tests/test-backends.sh # disposable keystore stub matrix
bash tests/test-guard.sh # guard matrix, needs jq
The suites use disposable directories and synthetic values only. CI runs them on
Linux and macOS, including stock /bin/bash 3.2.
Development
The Go rewrite is in progress; track milestones in ROADMAP.md. See CONTRIBUTING.md for setup and PR guidance and AGENTS.md for coding agent rules.
License
GPL-3.0-or-later, copyright Mert Dede.
1Password scaffolding (inactive)
The staged Go implementation includes test-only read-only 1Password scaffolding. Default registration, discovery and reads are disabled pending isolated provider evidence and licensing review. There is no environment switch to enable it. The Bash CLI, installed hooks and plugin manifests are unchanged. See the contract and limitations; do not point the Bash CLI at a Go v2 manifest.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.