vault_run
vault_run is the core tool. It decrypts the requested secrets locally, spawns a subprocess with them as env vars, and returns only the exit code and run metadata — never the subprocess output. Since v0.16.0, stdout/stderr are diverted to a channel the user owns (the daemon’s terminal when running in the foreground, or the agent log: ~/.local/state/ov/agent.log on Linux — $XDG_STATE_HOME/ov/agent.log if set — or ~/Library/Logs/ov/agent.log on macOS) instead of the MCP response, because tool responses are visible to the AI model. The plaintext values are zeroed from memory immediately after the process exits.
Each call runs against the (app, env) pair resolved and verified from the project’s .ov.yaml. Since OV-305 the vault daemon enforces that environment end-to-end — previously it was bridge-verified only, and the daemon silently served its start-time env. If the environment no longer exists in the app, the call fails closed with ENV_NOT_FOUND.
Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
command |
string[] | Yes | The command to run as an argv array |
secret_names |
string[] | Yes | Names of secrets to decrypt and inject as env vars (may be empty) |
expected_app |
string | Yes (OV_STRICT_CONTEXT, default on; optional when strict context is disabled) |
The app slug the caller expects to target — refused on mismatch with the resolved .ov.yaml (strict context) |
expected_env |
string | Yes (OV_STRICT_CONTEXT, default on; optional when strict context is disabled) |
The env slug the caller expects to target — refused on mismatch |
scope |
string | No | Delegation token from ov token mint, narrowing this call within the session floor |
work_dir |
string | No | Working directory for the command. Defaults to the current directory |
timeout_seconds |
number | No | Timeout in seconds. 0 or omitted means no timeout |
Response
Section titled “Response”{ "exit_code": 0, "invocation_id": "744e4fb9-c051-4f9c-bfa8-f784083746ef", "redacted": true, "redacted_fields": ["stdout", "stderr"], "output_note": "subprocess stdout/stderr are not returned to the agent; the user can view them on the daemon terminal (foreground) or in the agent log", "secrets_injected": ["DATABASE_URL"], "timed_out": false}| Field | Description |
|---|---|
exit_code |
Process exit code |
invocation_id |
Daemon-minted UUID — the join key between this response, the audit trail, and the output block in the daemon sink |
redacted / redacted_fields |
Always true / ["stdout", "stderr"] — subprocess output never rides the MCP response |
output_note |
Static pointer to where the output went |
secrets_injected |
Names of secrets that were injected (never values) |
timed_out |
true if the process was killed due to timeout |
The response never contains secret values — and since v0.16.0 it never contains subprocess output either. To read a run’s output, check the daemon terminal or the agent log (~/.local/state/ov/agent.log on Linux, ~/Library/Logs/ov/agent.log on macOS) and match the invocation_id header. If your agent needs to act on output, have the subprocess write a non-secret artifact (a file, a summary line count, an exit code) and inspect that instead.
Examples
Section titled “Examples”Run database migrations:
vault_run({ "command": ["go", "run", "./cmd/migrate"], "secret_names": ["DATABASE_URL"], "expected_app": "myapp", "expected_env": "dev"})Run tests with multiple secrets:
vault_run({ "command": ["npm", "test"], "secret_names": ["DATABASE_URL", "STRIPE_SECRET_KEY", "OPENAI_API_KEY"], "expected_app": "myapp", "expected_env": "dev"})What Claude says
Section titled “What Claude says”When Claude Code calls vault_run, it typically says something like:
“I’ll run the database migrations using
vault_runwith yourDATABASE_URLsecret.”
Then calls the tool. The MCP response comes back with the exit code and the redaction notice. Claude never sees the database URL — and never sees the subprocess output either; it reasons from the exit code, and you read the output on the daemon terminal or in the agent log.
Error responses
Section titled “Error responses”| Error | Meaning |
|---|---|
secret_not_found |
One or more requested secrets don’t exist. Use vault_list_secrets to check available secrets. |
decryption_failed |
KEK/DEK mismatch — session may have expired. Re-authenticate. |
command_timeout |
Command exceeded the timeout. Increase timeout_seconds or check the command. |
vault_run blocked |
The command was blocked by the inbound interceptor — it could exfiltrate injected secrets. |