Secure credential sidecar for MCP servers — backed by Vaultwarden.
Stop pasting API keys into .env files. vaultproxy reads credentials from your self-hosted Vaultwarden, injects the right auth header for each downstream service, and never exposes plaintext secrets to the MCP layer or the AI agent.
Claude Code → MCP Server → vaultproxy (127.0.0.1:3201) → Your Service
↑
Vaultwarden (credentials stay here)
Most MCP servers store credentials in env vars or .env files. These are readable by any process running as the same user, leak into shell history and ps auxe, and end up duplicated across every server you run. The AI agent driving your MCP server can also see them in tool responses.
vaultproxy fixes this with a small loopback HTTP service that:
- Reads every credential from one place — your Vaultwarden folder.
- Injects the correct auth pattern per service (
X-Api-Key, Bearer, Basic, session cookie, UniFi dual-mode, query param). - Keeps secrets out of the MCP server's address space entirely (native
/proxymode), or at worst out of disk (launcher mode). - Detects weak / reused credentials via HMAC-SHA256 fingerprints (all local — no password hashes ever leave your LAN; HIBP is explicitly out of scope by design), and (optionally) rotates some of them via a Playwright + vision-LLM agent.
| vaultproxy | agent-vault | wirken | OneCLI | warden-mcp etc. | |
|---|---|---|---|---|---|
| Backend | Vaultwarden | Infisical | own vault + OS keychain | own encrypted file | Vaultwarden (as vault tool) |
| Hides credentials from AI | ✅ | ✅ | ✅ | ✅ | ❌ exposes vault to AI |
| Auth patterns built-in | bearer, header, basic, session, query, UniFi dual | bearer | bearer | bearer | n/a |
| Hot-reload (SIGHUP + HTTP) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Credential audit (weak/reused, local HMAC — no HIBP) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Auto credential rotation | 🚧 partial (browser-vision, experimental) | ❌ | ❌ | ❌ | ❌ |
| TPM sealing | ✅ optional | ❌ | ❌ | ❌ | ❌ |
Dual integration: smart /proxy + dumb --launch |
✅ | proxy only | proxy only | proxy only | n/a |
Transparent HTTPS_PROXY (zero-mod agents) |
✅ --features transparent (v1.1.0 beta) |
✅ | ❌ | ✅ | n/a |
| Language / binary | Rust, single static binary | Go | Rust | Rust | TS / shells bw |
vaultproxy is opinionated for the selfhost / homelab crowd — *arr stack, Home Assistant, OPNsense, UniFi, Plex, Tautulli, Nginx Proxy Manager. Other brokers assume HashiCorp Vault / Infisical / Conjur backends and target generic API providers (Anthropic, GitHub, Stripe).
1. Create a vault-proxy folder in Vaultwarden and add one item per downstream service. The password field holds the credential (API key, Bearer token, etc.).
2. Create services.toml in a local ./config/ directory:
[[service]]
name = "ha_home"
base_url = "http://homeassistant.local:8123"
auth = "bearer"
vault_item = "vault-proxy - Home Assistant"
[[service]]
name = "sonarr"
base_url = "http://sonarr.local:8989/api/v3"
auth = "header"
header_name = "X-Api-Key"
vault_item = "vault-proxy - Sonarr"Full example: services.example.toml.
3. Run the setup wizard once to unlock Vaultwarden:
# docker-compose.yml
services:
vaultproxy:
image: ghcr.io/aaronckj/vaultproxy:latest # or pin to a tag, e.g. :1.0.4
restart: unless-stopped
network_mode: host
volumes:
- ./config:/config
environment:
VAULT_FOLDER: vault-proxy
command: ["--setup"] # remove after first rundocker compose up # interactive setup
# remove `command: ["--setup"]`
docker compose up -d # run for real
curl http://127.0.0.1:3201/vault/health4. Use it from an MCP server:
curl -X POST http://127.0.0.1:3201/proxy \
-H 'Content-Type: application/json' \
-d '{"service":"ha_home","method":"GET","path":"/api/states"}'The credential never appears in your shell, the MCP server, or the response.
Working MCP server examples (TypeScript, Python, Rust): examples/.
auth value |
Required fields | Example service |
|---|---|---|
bearer |
— | Home Assistant |
header |
header_name |
Sonarr / Radarr / Plex |
query_param |
param_name |
Tautulli |
basic |
key_field, secret_field |
OPNsense |
session |
login_path, token_field |
Nginx Proxy Manager, Duplicati |
unifi_dual |
login_path |
UniFi OS (API key → session fallback) |
Add insecure_tls = true for LAN services with self-signed certs (logs a startup warning).
-
Native
/proxy(Tier 1, recommended). Your MCP server detects vault-proxy viaVAULT_PROXY_URLenv and callsPOST $VAULT_PROXY_URL/proxy. The credential resolves inside vault-proxy and is injected into the outbound header. The MCP server never holds the secret. Seeexamples/smart-mcp-server-*. -
Launcher mode
--launch(Tier 2). For unmodified upstream MCP servers, vault-proxy resolves credentials from Vaultwarden andexecs the MCP server with credentials injected as env vars. No.envfiles on disk. Weaker than Tier 1 (env vars are readable from/proc/<pid>/environby the same OS user) but stronger than file-based storage. Configure inmcp-servers.example.toml.
The two modes above cover almost everyone. Reach for the explicit POST /proxy path first — it never touches your TLS trust store and is the recommended way to use vaultproxy.
The transparent HTTPS-MITM listener is opt-in (since v1.12.0) and exists only for unmodified third-party agents that can't be taught to call /proxy (and can't be launched via --launch either). Enable it at build time with --features transparent. If your agent can call /proxy, you do not need this.
The transparent CA is trusted only by the agent process you explicitly configure to trust it — never system-wide.
| Feature | Status | Where |
|---|---|---|
| 6 built-in auth patterns | ✅ stable | this README |
SIGHUP + POST /vault/reload-services hot-reload with atomic swap and rollback-to-empty guard |
✅ stable | docs/operator/RELOAD.md |
Per-caller rate limiting via X-Caller-Id / VAULT_PROXY_CALLER_ID |
✅ stable | SECURITY.md |
| Credential audit — weak/reused detection via HMAC-SHA256 fingerprints (all local — no password hashes ever leave your LAN; HIBP is explicitly out of scope by design) | ✅ stable | docs/operator/CRED-AUDIT.md |
Credential rotation: browser-vision (Playwright + vision LLM, experimental, off by default) + UniFi key-bootstrap; generic *arr API rotation not supported (POST /rotate returns a typed unsupported) |
🚧 partial, --features browser |
docs/operator/BROWSER-ROTATION.md |
| TPM-sealed keystore | ✅ --features tpm |
SECURITY.md |
| Web dashboard | ✅ --features dashboard (127.0.0.1:3202) |
docs/operator/DASHBOARD.md |
| Audit log (JSON, sensitive fields masked, 1000-entry cap) | ✅ stable | docs/operator/AUDIT-LOG.md |
| SMB mount provisioning via vault items | ✅ stable | docs/SMB.md |
Transparent HTTPS_PROXY listener (zero-mod agents, host_inject + placeholder) |
✅ --features transparent (v1.1.0 beta) |
docs/operator/TRANSPARENT.md |
Planned for v1.2+: SIGHUP rebuild of transparent registry, audit log entries for transparent traffic, mTLS / Unix-socket listener auth, OAuth flows, response prompt-injection sanitisation, SIEM audit sinks, generic /rotate strategies. See docs/ROADMAP.md.
vaultproxy binds to 127.0.0.1:3201 only. The trust model is OS-level process isolation — any local process running as the same user can call /proxy. There is no mTLS or caller authentication on the proxy endpoint by design (network isolation is the primary defence; a startup warning fires on any non-loopback bind). Internal endpoints (/vault/connecterr-secrets, /vault/reload-services, /browser/*, /vault/notes) require an Authorization: Bearer <internal-token> from $CONFIG_DIR/internal-token (mode 0600; generated on first start, persisted at 0600, and regenerated only if the file is deleted). Credentials are decrypted in-process from an encrypted keystore; plaintext never appears in logs. Optional TPM sealing binds the keystore to the host machine. SSRF, log-injection, path-traversal, and arbitrary-command launcher guards are enforced at config load. Full threat model: SECURITY.md. Report vulnerabilities privately via GitHub Security Advisories.
vaultproxy is a young project maintained by one person, and it has not had a third-party security audit yet. Set expectations accordingly.
The thing that makes that easy to live with: vaultproxy stores no secrets of its own. Every credential lives in your Vaultwarden — vaultproxy just reads, injects, and forwards. If the project is ever abandoned, you revert to plain env vars or Vault Agent in minutes, with zero lock-in.
docker build -t vaultproxy:latest . # headless, default
docker build --build-arg FEATURES=dashboard -t vaultproxy:dashboard . # + web UI
docker build --build-arg FEATURES=dashboard,browser,engine -t vaultproxy:full .A pre-built image is published to ghcr.io/aaronckj/vaultproxy via the GitHub Actions workflow on every tagged release.
cargo install vaultproxy # from crates.io (headless)Or build from source:
cargo build --release # headless
cargo build --release --features tpm # + TPM sealing (needs libtss2-dev)
cargo build --release --features dashboard # + web UI on 127.0.0.1:3202
cargo build --release --features browser # + Playwright rotation (needs LiteLLM + Playwright)- SECURITY.md — threat model, trust boundaries, rate limits, per-folder scope guards
- docs/operator/QUICKSTART.md — fuller setup walkthrough
- docs/operator/CONFIG.md —
services.tomlreference, vault items, internal token - docs/operator/CLI.md — every CLI flag and env var
- docs/operator/RELOAD.md — SIGHUP and
POST /vault/reload-servicessemantics - docs/operator/RUNBOOK.md — diagnosing common failures
- docs/operator/CRED-AUDIT.md — weak/reused scan + apply workflow (local HMAC fingerprints; no HIBP)
- docs/operator/AUDIT-LOG.md — audit log format and shipping
- docs/operator/BROWSER-ROTATION.md — rotation via Playwright + vision LLM
- docs/operator/LAUNCHER.md —
--launchmode andmcp-servers.toml - docs/operator/DASHBOARD.md — web UI, TLS cert persistence
- docs/operator/UPGRADING.md — v0.2.x → v1.0.x breaking changes
- docs/ROADMAP.md — what's planned for v1.1+
- GAPS.md — competitive landscape and pre-release tracking
MIT. See LICENSE.