CLIENT.ENCRYPTED SERVER.BLIND AGENT.NATIVE
v0.22.1
// AGENT-NATIVE SECRETS
▸ Agent-native secrets

For Agents

Wundervault gives AI agents their own scoped vault — a separate, secure space for secrets that agents can access via API. Built on zero-knowledge encryption so the server never sees vault key material.

Agents can also receive one-time secrets the same way a human would — give the agent the link and passphrase, it fetches and decrypts in one step, and the secret is permanently destroyed on read. No account or API key needed for that flow.

The agent vault is the set of persistent secrets you have explicitly sent to an agent. Agents can only see secrets in their own vault — they cannot access secrets belonging to other agents or your personal vault.

How It Works

Each agent has a dedicated vault scoped only to it. The human approves specific secrets from their own vault and sends them to the agent's vault. The agent accesses those secrets via REST API or MCP — no human login required.

1
You register the agent in the Wundervault dashboard — Agents tab → "Register New Agent." Give it a name and description.
2
You send secrets to its vault. On each vault secret, click "📨 SEND" and pick the agent. The secret is now in the agent's vault.
3
The agent runs the onboard script. The dashboard shows a one-time setup URL. The agent runs python3 onboard.py "SETUP_URL" — the script verifies its own signature, decrypts the credential blob, registers the profile with the local daemon (stored encrypted on disk), and burns the setup link. The agent never holds credentials directly; the daemon manages them.
4
The agent reads its secrets via MCP or REST. With MCP: vault tools are available immediately after adding the printed config snippet to the agent's framework. With REST: the agent calls GET /agent/vault/secrets to list secrets, then GET /agent/vault/secrets/{id}?purpose=... to retrieve and decrypt each one.
5
You revoke instantly. Remove the secret from the agent's vault in the dashboard, or revoke the agent entirely. The agent gets a 401 on its next request — access is cut off immediately.

Zero-Knowledge Encryption

The agent vault uses two layers of encryption — the server handles storage but never sees plaintext:

  • vault_key_for_agent — AES-GCM encrypted with the agent's encryption_key. The agent decrypts this first to get the vault key. The server never sees the vault key.
  • encrypted_content — each secret value is AES-GCM encrypted with the vault key. The agent decrypts this to get the plaintext secret. The server never sees secret values.
  • Double decrypt: Agent uses encryption_key → decrypts vault key → uses vault key → decrypts secret. Server is never involved in either decryption step.

Secret Tiers

Vault secrets have an access tier that controls how agents retrieve them:

TierAgent flowHuman approval
Tier 1 (standard)Agent calls → secret returns immediatelyNo
Tier 2 (biometric-gated)Agent calls → 403 unless biometric unlock was used in dashboardWebAuthn required

Tier 2 secrets require a separate biometric unlock even if the vault is already open. This is enforced at the API level — agents cannot bypass it.

MCP Server

Use Wundervault as native MCP tools from Claude Code, Cursor, or any MCP-compatible agent. The MCP server handles all encryption and decryption locally — secrets are never returned to the agent in plaintext.

If your agent uses OpenClaw or ClawhHub, install the ready-made skill: clawhub.ai/snoweman/wundervault-vault. It wires up all the tools and usage patterns for you.

Install

npm install -g @wundervault/mcp-server

Available tools

  • vault_entries_list — list the agent's vault secrets (names and IDs only, no values)
  • vault_entry_get(entry_id, purpose) — retrieve and decrypt a secret. Plaintext is never returned — the agent receives only a burn confirmation.
  • vault_exec(entry_id, purpose, command, working_dir?, inject_as?, remote_host?) — run any shell command with the secret injected as an env var, locally or on a remote machine via SSH. Shell escape patterns ($(), backticks, bash -c, sh -c, eval) are rejected before the secret is decrypted. Tier 1 executes automatically; Tier 2 requires session unlock.
  • vault_session_status — check current session lock state (use count, idle time, lock threshold).
  • vault_session_unlock — reset the Tier 2 session lock. Call this after a Tier 2 session locks (use count or idle timeout exceeded).
  • vault_entry_inject_env(entry_id, purpose, file_path, env_key) — write a secret into an allowlisted config file (~/.npmrc, ~/.netrc, ~/.docker/config.json, project .env files). Paths outside the allowlist are rejected.
  • vault_entry_forget(entry_id) — discard a local reference (no-op on the server)

vault_exec — exec_config

Set an exec_config on a vault entry when sending it to an agent (📨 SEND modal). This defines how the secret is injected: which env var, optional setup command (pre_command), and optional teardown (post_command). The agent then calls vault_exec with any command — the vault entry handles injection automatically.

vault_exec(entry_id, "publishing npm package", "npm publish --access public")

No template knowledge required. Built-in presets in the dashboard: npm, restic, git, AWS. Or set a custom env key and commands.

If the agent needs to override the injection recipe for a specific call, it can pass inject_as: { env_key, pre_command?, post_command? } directly.

vault_exec — remote execution via SSH

Pass a remote_host block to run the command on a different machine. The MCP server SSHes to the host and pipes the secret injection via SSH stdin — the secret is set as an env var inside the remote shell and never exists as a local env var, never appears in SSH arguments, and requires no AcceptEnv or SendEnv configuration on the remote host.

Use ssh_key_entry_id to load the SSH private key from the vault — the key is fetched, used for the connection, and never written to disk or exposed to the agent. This is the recommended approach: the agent cannot SSH directly and all remote execution is forced through the controlled vault_exec path.

vault_exec( entry_id: "api-token-entry-id", purpose: "check api on prod server", command: "curl -s -u \"user:$API_TOKEN\" http://localhost:9000/api/status", inject_as: { env_key: "API_TOKEN" }, remote_host: { host: "192.168.1.50", user: "deploy", ssh_key_entry_id: "ssh-key-vault-entry-id" ← key fetched from vault, never on disk } )

ssh_key (a filesystem path) is also accepted as a fallback. Omit both to use the SSH agent. The same security properties apply as local exec: shell escape patterns are rejected before decryption, output is scrubbed, and the secret buffer is zeroed after use.

Security note — .env files on agent-accessible hosts. When an agent has SSH access to a machine, it has general shell access and can read any file the SSH user can access — including .env files containing plaintext secrets. Vault-level controls cannot restrict what an agent reads once it has shell access. Recommended: do not store plaintext secrets in .env files on machines agents can SSH into. Use vault_entry_inject_env at container startup to inject secrets into the process environment without writing to disk, and use ssh_key_entry_id to prevent agents from holding SSH keys directly. See the Security White Paper §1.6 for the full threat model and mitigations.
Secrets never reach the conversation. The MCP server decrypts secrets locally and never sends plaintext to the agent. The tool returns only a confirmation — so secrets cannot be echoed in chat or stored in conversation memory. With vault_exec, the secret is injected as a named environment variable, the parent process buffer is zeroed immediately after the subprocess spawns, sensitive parent-env keys are stripped from the child environment, and command output is scrubbed before being returned.

Session lock policy (Tier 2)

Tier 2 vault_exec calls lock automatically after a configurable number of uses or minutes of inactivity (default: 3 uses or 5 minutes). Tier 1 executes freely with no lock. Lock state is HMAC-signed on disk and survives MCP server restarts. Call vault_session_unlock to reset. Policy is configurable per-user in the Secret Server settings.

Setup

1
Install the MCP server
npm install -g @wundervault/mcp-server
2
Run the onboard script with your setup URL
curl -fsSL https://wundervault.com/onboard -o /tmp/wv-onboard.py && python3 /tmp/wv-onboard.py "YOUR_SETUP_URL"

The script verifies its own signature, exchanges credentials with the vault, registers your profile with the local daemon, and writes a token file to ~/.wundervault/agents/AgentName.token. It prints a config snippet to add in the next step.

3
Add the printed snippet to your framework's MCP config

The script prints a block like this — copy it into your own framework config. Each agent manages their own config entry; the onboard script does not write to any framework config automatically.

{ "command": "wundervault-mcp", "env": { "WUNDERVAULT_AGENT_NAME": "AgentName" } }

Config file locations by framework:

  • Claude Code — ~/.claude.jsonmcpServers
  • OpenClaw — ~/.openclaw/openclaw.jsonmcp.servers
  • Windsurf — ~/.codeium/windsurf/mcp_config.jsonmcpServers
  • Hermes — ~/.hermes/config.yamlmcp_servers

No token or API key belongs in the config. The token is auto-discovered from the file the onboard script wrote.

4
Activate vault tools

Claude Code watches its config file and picks up the new entry automatically — vault tools appear within seconds of adding the snippet, no restart needed.

OpenClaw, Hermes, Windsurf — restart the gateway process after adding the snippet. Vault tools will be live on the first call.

Credentials are stored encrypted by the local daemon — no plaintext files, no API keys in config. WUNDERVAULT_AGENT_NAME identifies which agent profile the daemon serves to the MCP server at runtime.

Reinstall / Repair

Use this when vault tools stop working — 502 errors, auth failures, or after a credential rotation.

Option A — Repair in place (recommended)

If the agent name and vault URL haven't changed, generate a new setup link from Settings → Agents and run:

curl -fsSL https://wundervault.com/onboard -o /tmp/wv-onboard.py && python3 /tmp/wv-onboard.py --repair "YOUR_NEW_SETUP_URL"

Replaces only the named agent's profile and notifies the running daemon. Other agents are untouched.

Option B — Fresh agent name

  • Run onboard.py with the new setup URL — no flags needed
  • Update WUNDERVAULT_AGENT_NAME in your framework's MCP config to the new name
  • Restart your gateway if required

The old agent profile remains until you revoke it from the dashboard (Settings → Agents).

Uninstall

Remove one agent (keep others running)

  1. Revoke from dashboard: Settings → Agents → Revoke <AgentName>
  2. Remove token and socket:
    rm ~/.wundervault/agents/<AgentName>.token && rm -f ~/.wundervault/agents/<AgentName>.sock
  3. Remove the wundervault entry from your framework's MCP config
  4. Restart your agent session

Remove everything (last agent on machine)

systemctl --user disable --now wundervault-agent rm ~/.config/systemd/user/wundervault-agent.service rm -rf ~/.wundervault/ npm uninstall -g @wundervault/mcp-server

Troubleshooting

502 error from vault tools

Means the MCP server is hitting a stale vault URL. Diagnose step by step:

Step 1 — confirm which agent name is in use:

pgrep -a wundervault-mcp cat /proc/<pid>/environ | tr '\0' '\n' | grep WUNDERVAULT_AGENT_NAME

Step 2 — check daemon socket:

ls -la ~/.wundervault/agent.sock

Step 3 — check agent socket:

ls -la ~/.wundervault/agents/

If daemon is running but agent socket is missing → re-run onboard.py --repair.

Gateway restart

After updating MCP config, these frameworks require a gateway restart before vault tools appear. Claude Code picks up changes automatically.

# Hermes hermes gateway restart # OpenClaw systemctl --user restart openclaw-gateway # Windsurf — restart from the application menu

Stale lock files

ls ~/.wundervault/*.lock rm ~/.wundervault/mcp-OldAgentName.lock
To verify your install checksums and ed25519 signature — Install & Verify →

Audit Log

Every agent vault operation is logged. Visible per-agent on the dashboard (click an agent tab → Access Log) and in the Usage Audit Log section below the vault.

FieldDescription
AgentAgent name + fingerprint (first 6 chars of agent ID) — e.g. claude [a3f2b1]
SecretWhich secret was accessed
Actionvault_listed, vault_retrieved, sent_to_vault, agent_revoked, tier2_blocked
PurposeAgent's declared reason for access (required, max 200 chars)
IPWhere the request originated
Outcomesuccess, unauthorized, blocked

Agent fingerprints — each registration generates a unique ID. If you revoke claude and register a new claude, the fingerprints differ ([a3f2b1] vs [c91d40]), keeping the audit trail unambiguous. The fingerprint is shown in the dashboard agent tab header and in every audit log row.

Human vs. Agent Vault

Human VaultAgent Vault
AuthPassphrase + optional WebAuthn biometricScoped API key in memory
EncryptionAES-256-GCM, PBKDF2, true zero-knowledge (independent vault key)Double-layer ZK: encryption_key → vault key → secret
AccessDashboard, WebAuthn biometric unlockREST API or MCP
Secrets visibleAll vault secrets you ownOnly secrets explicitly sent to agent's vault
Secret tiersTier 1 (standard), Tier 2 (biometric-gated)Tier 1 (auto), Tier 2 (biometric-gated)
RevocationDelete or rotate secretDashboard: revoke agent or remove from vault
Audit logNot applicableFull log: agent, secret, purpose, IP, timestamp

Security Notes

  • Agent credentials are stored by the local daemon in an encrypted profile file (~/.wundervault/agent-profiles.enc), protected by a machine-id-derived key — no plaintext credentials ever exist on disk
  • The API key is provisioned via Wundervault's own one-time secret mechanism — it burns after the onboard script retrieves it, preventing replay
  • Tier 2 secrets require biometric unlock even for agents — there is no agent-side bypass
  • Revocation is immediate — the agent gets 401 on its next request
  • Audit log is append-only — entries are never deleted, even if the agent or secret is removed
  • When using the MCP server, secrets are never sent to the agent in plaintext — the tool returns only a confirmation, preventing secrets from appearing in chat or being stored in conversation memory
  • vault_exec accepts any shell command — shell escape/injection patterns ($(), backticks, bash -c, sh -c, eval) are rejected before the secret is decrypted, preventing prompt injection from escalating to arbitrary command execution with secret privileges. The injection recipe (env var, setup/teardown) lives on the vault entry as exec_config, set once in the dashboard.
  • The secret is injected as a named environment variable (not a command argument — never appears in process listings); the parent process buffer is zeroed immediately after spawn; sensitive parent-env keys are stripped from the child environment before injection
  • When using remote_host, the secret is piped via SSH stdin — it is never a local env var, never appears in SSH command arguments, and requires no server-side SSH config changes (AcceptEnv/SendEnv). All other exec security properties (pattern rejection, output scrubbing, buffer zeroing) apply equally to remote execution.
  • Tier 2 session lock limits the number of vault_exec calls per unlock — state is HMAC-signed on disk and survives process restarts; tampering resets to locked
  • Secrets in the agent's context or memory cannot be recalled by revocation — limit what you share with agents accordingly
Want to provision an agent? Go to dashboard — register under the Agents tab.
Want the full technical breakdown? Read the Security White Paper →