Skip to content

ov agent

import { Aside } from ‘@astrojs/starlight/components’;

ov agent is a long-running daemon that holds your KEK in memory after a one-time authentication, then serves secret injection requests over a Unix socket. It has two clients today: the MCP bridge (ov mcp serve connects to the agent for all crypto operations) and ov run, which dials the same socket to run commands with per-request secrets and zero password prompts once the agent is unlocked (see ov run for the full agent-first flow, its divergences from the local path, and the Ctrl-C caveat). If the agent isn’t running when a client starts (or, for ov run, isn’t reachable at all), MCP tool calls fail with a plain-text ov agent is not running error while ov run silently falls back to its own password prompt / --machine-key path; if a running agent’s session locks or expires, both clients see SESSION_LOCKED (see Troubleshooting for both, including when the MCP client needs a restart).

On a workstation, ov agent start with no flags prompts for your master password once and keeps the session unlocked — it runs in the foreground, so keep the terminal open. On Linux servers and CI runners, pair it with a machine key so ov run needs no prompt. Note that the MCP bridge can only authenticate to an interactively started agent today — a machine-key agent derives its socket key from the KEK and writes no key file for the bridge to read.


ov agent start --machine-key /etc/ov/machine.kek
daemon starts, loads KEK, listens on Unix socket
ov run --machine-key /etc/ov/machine.kek --secrets DATABASE_URL -- ./migrate
run finds the socket automatically, sends a signed request →
agent decrypts → subprocess runs on the daemon side

The agent authenticates requests via HMAC-SHA256 derived from the machine key. Only a client that holds the same machine key can send valid requests. The socket itself is protected by 0700 directory + 0600 permissions.

Plaintext secret values never cross the socket — the agent runs the subprocess itself and returns only the exit code.

Each request declares its own (app, env) pair and the daemon resolves it per request (with a short-lived cache); --app/--env are only the defaults for requests that don’t declare one. If a request names an environment that doesn’t exist in its app, the daemon refuses with the ENV_NOT_FOUND wire code. The startup pair is validated once at start and is not re-validated per request.

Version gate for ov run: the daemon reports a version string (currently ov-agent/2) on ov agent status. ov run checks this before dispatching a request — it requires at least ov-agent/2 (the version where per-request env routing shipped) and falls back to its local password-prompt path against anything older, rather than risk silently sending a per-request env to a daemon that would ignore it and serve its startup env instead. Restart the agent after upgrading ov to pick up the new version.


A machine key file is required to start the agent:

Terminal window
ov auth derive-machine-key --out /etc/ov/machine.kek
chmod 600 /etc/ov/machine.kek

See Machine key setup for details.


CommandDescription
ov agent startStart the agent daemon
ov agent stopStop the running agent
ov agent statusShow agent status
ov agent installInstall a systemd unit file

Terminal window
ov agent start --machine-key /etc/ov/machine.kek --app my-saas

Starts the daemon in the foreground. Use --foreground with systemd Type=simple.

FlagDescription
--machine-key PATHPath to machine key file (required for HMAC socket auth)
--app NAMEDefault app slug for all secret ops; clients may override per request — as of v0.12.0 (OV-319) every MCP tool (not just vault_run) sends its own resolved app per call
--env NAMEDefault environment slug for all secret ops; clients may override per request (resolved via flags → .ov.yaml → config → production) — as of v0.12.0 (OV-319) every MCP tool sends its own resolved env per call, including the three write tools, which additionally require declared expected_app/expected_env under OV_STRICT_CONTEXT (default on)
--socket PATHUnix socket path (default: $XDG_RUNTIME_DIR/ov-agent/ov-agent.sock)
--pid-file PATHPath to write PID file
--foregroundRun in foreground (required for systemd Type=simple)

Terminal window
ov agent stop
ov agent stop --pid-file /run/ov-agent/ov-agent.pid

Sends SIGTERM to the agent and returns immediately. SIGTERM cancels the daemon’s own context, which propagates into any in-flight subprocess and kills it abruptly — the daemon does not wait for running commands to finish before exiting. To kill a single orphaned command while keeping the daemon (and its unlocked session) alive, see the Ctrl-C caution in ov run.


Terminal window
ov agent status
ov agent status --machine-key /etc/ov/machine.kek

Queries the agent socket and prints uptime and version. If the socket is unreachable, falls back to checking the PID file.

Pass --machine-key if the agent was started with one — without it, the signed request will be rejected and status falls back to the PID file check.


Terminal window
ov agent install --machine-key /etc/ov/machine.kek --app my-saas --env production
ov agent install --machine-key /etc/ov/machine.kek --app my-saas --env production --enable

Writes a systemd unit file and prints the commands to enable and start it. Pass --enable to run systemctl daemon-reload && systemctl enable --now ov-agent automatically.

--app and --env are required — they are routing flags baked into the unit’s ExecStart so the agent isn’t coupled to the service account’s config-file state. They do not scope the key.

FlagDescription
--machine-key PATHPath to machine key file (required)
--app SLUGApp slug baked into the unit’s ExecStart (required)
--env SLUGEnv slug baked into the unit’s ExecStart (required)
--socket PATHUnix socket path
--unit-name NAMEsystemd unit name (default: ov-agent)
--user NAMEsystemd User= field (default: $SUDO_USER if present, else current user; cannot be root)
--enableRun systemctl to enable and start immediately

The recommended production setup:

Terminal window
# 1. Create the machine key
ov auth derive-machine-key --out /etc/ov/machine.kek
chmod 600 /etc/ov/machine.kek
# 2. Install the systemd unit
ov agent install \
--machine-key /etc/ov/machine.kek \
--app my-saas --env production \
--enable

The generated unit file uses Type=simple and restarts on failure. It runs as the current user.


  • Socket directory: 0700 (only the owning user can access it)
  • Socket file: 0600
  • HMAC-SHA256 per-request authentication when started with --machine-key
  • Replay window: ±30 seconds (timestamp-based; no stateful nonce store)
  • Plaintext values never cross the socket — the agent injects them directly into subprocess environments
  • Per-request (app, env) routing: the daemon serves the environment each request declares, never silently substituting the start-time default; a request naming a nonexistent environment fails closed with ENV_NOT_FOUND. Environment selection is routing, not authorization — every env is reachable with the one unlocked key (an operator env allowlist is tracked as OV-320)

Known limitations:

  • No server-side nonce store: a captured signed request can be replayed within the 30-second validity window
  • Interactive sessions (no --machine-key) have no socket authentication beyond filesystem permissions

  • Machine key setup — generating and managing machine keys
  • ov run — uses a running agent automatically (zero prompts); falls back to its own no-daemon path when one isn’t reachable
  • CI/CD integration — using OpaqueVault in automated pipelines