ov run
ov run is the CLI equivalent of vault_run. It fetches secrets for the active app, injects them as env vars, runs a command, and exits. Use it in scripts, CI, deploy pipelines, or anywhere you need secrets without the MCP layer.
If an ov agent is already running and unlocked, ov run uses it — no password prompt, no --machine-key needed, even on a workstation. The prompt-on-every-invocation flow described below only fires as a fallback when no usable agent is reachable.
ov run [flags] -- <command>Examples
Section titled “Examples”Run database migrations with all secrets for the default app:
ov run -- go run ./cmd/migrateRun with a specific app:
ov run --app my-saas -- npm testInject only specific secrets:
ov run --app my-saas --secrets DATABASE_URL,STRIPE_SECRET_KEY -- go test ./...Run non-interactively in CI or a systemd service (no password prompt):
ov run --machine-key /etc/ov/machine.kek --app my-saas -- ./serverForce the in-process path, bypassing any running agent:
ov run --no-agent --app my-saas -- ./serverThe agent-first path
Section titled “The agent-first path”ov run tries the local ov agent socket before it ever prompts for a password. It reuses the same socket discovery, key handling, and peer-UID verification as the MCP bridge, then sends the resolved (app, env) as a request the daemon honors per-invocation. This means:
- One
ov agent start, many terminal tabs, zero prompts. Everyov runin every tab uses the already-unlocked agent. - The context line shows which path served the run — an
(agent)segment appears at the end of the line when the agent handled it:Noov run: using app=my-saas env=production (.ov.yaml at /home/dev/repo) (agent)(agent)segment means the local (password-prompt /--machine-key) path ran, exactly as before this feature shipped. - Fallback is automatic and safe.
ov runfalls back to the local path only when no usable agent is reachable: no socket, an unreadable/undecodable socket key, an agent whose session is locked, or an agent older than the CLI’s minimum supported version (see ov agent for the version-gate note). A static notice prints to stderr immediately before the fallback’s context line so you always know why the agent wasn’t used, e.g.:Fallback never happens once a command may have started executing. If the agent accepted the request and something goes wrong afterward (a lost connection, an unrecognized daemon reply),ov run: agent is locked; falling back to master-password prompt (run 'ov agent stop' then 'ov agent start' to restart it)ov run: using app=my-saas env=production (.ov.yaml at /home/dev/repo)ov runreports the error and exits — it does not silently re-run the command locally, which would risk running it twice. --no-agentskips the probe entirely and always uses the local path (prompts for the master password unless--machine-keyis set). Use it when you specifically want the terminal-native environment described below, or to sidestep the agent for any of the divergences in the table.
Divergences on the agent path
Section titled “Divergences on the agent path”Because the daemon executes the command, not your shell, a handful of things differ from running ov run without an agent. None of these weaken the zero-knowledge guarantee — secret values still never cross the agent socket in either direction; the daemon runs the subprocess itself and returns only the exit code plus interceptor-filtered output.
| Axis | Local ov run | Via the agent |
|---|---|---|
| Subprocess environment | Your full shell environment | The daemon’s sanitized, allowlist-only environment (HOME, USER, PATH, SHELL, TERM, TZ, LANG, LC_*, XDG_*, OV_*, LOGNAME, TMPDIR — see below) and the daemon’s PATH, frozen from when ov agent start launched |
| Subprocess cwd | Your terminal’s cwd | Also your terminal’s cwd (sent per-request) — no divergence here |
| Ctrl-C | Kills the subprocess along with your shell’s process group | Detaches the client only — the daemon-side command keeps running. See the warning below |
| Accidental-exfiltration heuristic | Not run | The daemon’s CheckInbound heuristic applies (same one vault_run uses) — bulk-env dumps and unquoted single-secret redirects are rejected |
| Output redaction | SanitizeOutput + full-match secret redaction | Same, plus a second pass through the agent’s own interceptor |
| Legacy v1-HMAC-only secrets | Visible via the env-agnostic fallback | Not visible for any (app, env) pair other than the agent’s startup pair (see ADR-019) — re-store the secret under the current scheme, or use --no-agent |
--secrets count | Unlimited | Capped at 100 per run |
| Output size | Unlimited | Capped at 64 MB |
| Audit accessor identity | cli/<version> | The daemon’s own accessor identity (mcp-agent/<version> or machine-key/<version>) |
If a command isn’t on the daemon’s PATH, the agent path now surfaces a clear error (command could not be started — check that the binary exists on the agent's PATH) with a --no-agent hint appended, instead of a silent exit 255. This is a fix shared with vault_run: an unstartable binary used to come back as a success-shaped exit_code: -1; it’s now an honest error on both paths.
PATH hygiene note: the agent’s sanitized environment is actually more hygienic than your shell in one respect — it strips things your shell leaks into every local ov run today, like LD_PRELOAD, DYLD_*, and ambient credential variables (tokens picked up from direnv/nvm/etc.) that aren’t on the allowlist above. The trade-off is that the PATH is global to the daemon and frozen at ov agent start time, not scoped per-project — restart the agent after changing what needs to be on its PATH.
Non-interactive use (machine keys)
Section titled “Non-interactive use (machine keys)”Without a running agent, ov run prompts for your master password on every invocation. For automated deployments — CI pipelines, Docker containers, systemd services — use a machine key file instead:
# One-time setup (as a human, interactive)ov auth derive-machine-key --out /etc/ov/machine.kek
# Every subsequent run (no prompt)ov run --machine-key /etc/ov/machine.kek --app my-saas -- ./server--machine-key also participates in the agent-first path: if the agent was started with --machine-key and no interactive socket key file is available, passing the same --machine-key to ov run lets it derive the socket key and use the agent. See ov agent for the socket-key precedence details.
See the Machine Keys guide for full setup instructions, Docker and systemd examples, key rotation, and security properties.
| Flag | Description |
|---|---|
--app NAME | Target app (overrides config default) |
--env NAME | Target environment (overrides config default) |
--secrets NAME1,NAME2 | Explicit list of secrets to inject |
--machine-key PATH | Path to a machine key file — skips password prompt, and lets ov run derive the agent socket key if no key file is available (see Machine Keys) |
--no-agent | Skip the agent daemon and resolve secrets in-process (prompts for the master password unless --machine-key is set) |
--no-context-line, -q | Suppress the resolved-context line printed to stderr (also: OV_QUIET=1) |