Machine Keys (Non-interactive)
import { Aside, Steps } from ‘@astrojs/starlight/components’;
By default, ov run prompts for your master password every time it runs. That’s fine for local development but blocks automated deployments — CI pipelines, Docker containers, and systemd services have no terminal to type into.
Machine keys solve this. You derive a 32-byte key file once (interactively, as a human), then automated processes read it to load the KEK without prompting.
How it works
Section titled “How it works”ov auth derive-machine-key → writes raw 32-byte KEK to a chmod 600 fileov run --machine-key <path> → reads file, loads KEK, runs without promptingThe key file is raw binary — 32 bytes, no JSON wrapper, no headers. ov run validates permissions before reading: if the file isn’t exactly chmod 600 and owned by the current user, it hard-errors and refuses to load the key.
-
Log in and create your app
If you haven’t already:
Terminal window ov auth loginov app create myappov secret set DATABASE_URL --app myapp -
Derive the machine key
Run this once as a human — it prompts for your master password and writes the derived key to disk:
Terminal window ov auth derive-machine-key --out /etc/ov/machine.kek --app myappThe
--appflag scopes the key to a single app. A scoped key cannot decrypt secrets from other apps, limiting blast radius if the file is ever compromised.You’ll see:
Master password: ▌⚠ Machine key written to /etc/ov/machine.kekThis file is as sensitive as your master password.It grants decryption access to secrets.- Keep it chmod 600, owned by the service account- Never commit to version control- Revoke access by rotating the OV API key for this deployment- Rotate with: ov auth derive-machine-key --out /etc/ov/machine.kek --forceVerify the permissions:
Terminal window ls -la /etc/ov/machine.kek# -rw------- 1 deploy deploy 32 Apr 15 10:00 /etc/ov/machine.kek -
Test non-interactive injection
Terminal window ov run --machine-key /etc/ov/machine.kek --app myapp -- env | grep DATABASE_URL# DATABASE_URL=postgres://...No password prompt. If it doesn’t work, see Troubleshooting.
Using in a deploy script
Section titled “Using in a deploy script”#!/usr/bin/env bashset -euo pipefail
ov run \ --machine-key /etc/ov/machine.kek \ --app myapp \ --secrets DATABASE_URL,REDIS_URL \ -- ./bin/serverThe process starts with DATABASE_URL and REDIS_URL in its environment. They are never written to disk, never logged, and never visible in ps output.
Docker
Section titled “Docker”Store the machine key on the host and bind-mount it read-only:
# Dockerfile — no secrets baked inFROM debian:bookworm-slimRUN curl -fsSL https://get.opaquevault.com | shCOPY ./bin/server /app/serverENTRYPOINT ["ov", "run", "--machine-key", "/run/secrets/machine.kek", "--app", "myapp", "--", "/app/server"]docker run \ -v /etc/ov/machine.kek:/run/secrets/machine.kek:ro \ myapp:latestThe container never has network access to your vault credentials — just the machine key for decrypting the specific app’s secrets.
systemd service
Section titled “systemd service”[Unit]Description=My AppAfter=network.target
[Service]Type=simpleUser=deployExecStart=/usr/local/bin/ov run \ --machine-key /etc/ov/machine.kek \ --app myapp \ -- /opt/myapp/serverRestart=on-failureRestartSec=5
[Install]WantedBy=multi-user.targetsystemctl daemon-reloadsystemctl enable --now myappThe service account (deploy) must own /etc/ov/machine.kek and the file must be chmod 600.
Key rotation
Section titled “Key rotation”If you suspect the key file is compromised:
-
Revoke the OV API key for this deployment immediately — this cuts off access regardless of whether the attacker has the machine key file:
Terminal window ov apikey listov apikey delete <key-id> -
Create a new API key and update the deployment config.
-
Rotate the machine key:
Terminal window ov auth derive-machine-key --out /etc/ov/machine.kek --force
Security properties
Section titled “Security properties”| Property | Detail |
|---|---|
| Permission enforcement | Hard error if not exactly chmod 600 and owned by current user — no silent warnings |
| Symlink protection | Opened with O_NOFOLLOW — symlinks to other files are rejected at the OS level |
| TOCTOU safety | Permissions checked on the open file descriptor via fstat, not a separate stat call |
| Memory protection | KEK bytes are mlocked on load (Linux/macOS) — cannot be swapped to disk |
| Core dump exclusion | KEK memory marked MADV_DONTDUMP on Linux — excluded from crash reports |
| App scoping | --app flag derives a per-app key via HKDF — one leaked file ≠ all apps |
| Zero after use | KEK is zeroed from memory after the subprocess exits |
Troubleshooting
Section titled “Troubleshooting”machine key has permissions 0644 — must be 0600
chmod 600 /etc/ov/machine.kekmachine key is owned by uid 0 but current uid is 1001
The file is owned by root but the service runs as a different user. Fix ownership:
chown deploy:deploy /etc/ov/machine.kekmachine key must be exactly 32 bytes
The file is corrupted or was created incorrectly. Re-derive it:
ov auth derive-machine-key --out /etc/ov/machine.kek --forceopening machine key: too many levels of symbolic links
The path is a symlink. ov run refuses symlinks by design. Use the real file path.