omp secret
omp secret — capability-over-redaction

The agent uses secrets
without ever seeing them.

A security-native fork of the OMP terminal coding agent. The model holds only a handle — {{vault:bitwarden/9f3a…}} — while a broker executes with the real value in a heap the model cannot read. Containment by architecture, not by filter.


01 — the problem

Every credential your agent touches
lands in the transcript.

standard agent $ fetch_api_logs --key="sk-live-51Mz2…"
critical leak — the key is now in the model’s context, the session log, and the provider’s retention window.
omp secret flow $ fetch_api_logs --key={{vault:main_api_key}}
secure execution — the model passes a handle; the sidecar resolves it off-context; output returns scrubbed.

02 — three seams

Redaction is a boundary,
not a search-and-replace.

SEAM / OUT

Outbound obfuscate

Every value registered in secrets.yml or via addSecret() is replaced with a deterministic #ABCD# placeholder before a single token reaches the provider. The JSONL session log is sealed the same way, before it hits disk.

SEAM / IN

Inbound deobfuscate

Tool calls resolve placeholders back to real values inside the broker’s heap — never in the model’s. The agent requests a capability; the broker performs it; only the typed result crosses back.

SEAM / DISPLAY

Operator restore

You still see everything, tagged: hunter2 [redacted from LLM — source:bitwarden, visible-to:human, ttl:session]. Provenance is part of the value — who can see it, where it came from, when it dies.


03 — the broker

A capability surface,
not a credential store.

The agent never calls a secret-bearing CLI directly. It calls the broker — closed PATH allowlist, 20-variable env blacklist, Landlock fs-write sandbox, array-form argv, output scrubbing on the way back.

1 · HANDLE {{vault:…}} 2 · RESOLVE provider.resolve fail-closed 3 · SPAWN hardened env + secret in env 4 · EXECUTE closed PATH no SSH agent · landlock 5 · SCRUB scrubOutput exact+b64+urlenc 6 · RESULT ExecResult no raw value field hardening at every hop — closed PATH · env blacklist · SSH agent stripped · array-form argv · fs-write guard · landlock inside run_with_secret — six hops, every one hardened
run_with_secret

Resolve a handle, inject the value into a hardened subprocess env, scrub the output.

run_with_chain

Multi-step resolution — Bitwarden → SSH → Infisical — intermediate values never surface.

browser_with_secret

The broker types credentials into its own isolated page. The agent’s browser can’t read it back.

human_terminal

An operator-only PTY. Output renders to your screen and never enters the agent’s context.

/redact

Register a vault item for redaction in one step — resolve, then obfuscator-register by value.

/bw-unlock

Unlock Bitwarden into the daemon’s vault via a masked TUI dialog. Paste works; the model sees nothing.


04 — the sidecar daemon

The vault lives in another heap.
Unlock once, everything shares it.

A detached daemon holds the credential vault in its own process memory — PR_SET_DUMPABLE=0, mlock-attempted, token-gated, speaking a capability-only protocol over a unix socket (and Tailscale TCP for your other machines).

the secret never crosses to the agent AGENT (LLM) sees handles only {{vault:…}} requests capabilities SIDECAR DAEMON own heap, own process resolve → hardenedSpawn scrubOutput on the way back leases: TTL-scoped borrows VAULT (in-memory) #ABCD# BW_SESSION … PROVIDERS bitwarden · infisical 1password · hashicorp varlock · totp SUBPROCESS closed PATH · env blacklist · landlock capability request (handle only) resolve bw get item secret value → daemon heap ONLY env injection stdout → scrub typed ExecResult (scrubbed) every boundary labeled — the value flows down, never back

the secret never crosses back into the agent’s context


05 — the chain

Bitwarden, then the server,
then every service on it.

The real flow: run_with_chain resolves the SSH credential from your laptop’s Bitwarden, SSHes into the VPS with a restricted agent holding only scoped keys — never your personal agent — then pulls each service’s secrets from the Infisical instance on that server. Three secrets cross; the agent sees none of them.

WORKSTATION OVH VPS · TAILSCALE AGENT run_with_chain 3-step recipe BITWARDEN ssh key + vps password items BROKER restricted SSH agent scoped keys only (vps machine key) ✗ your personal agent — never inherited ssh · tailscale INFISICAL V2 machine identity universal-auth → jwt SERVICES — each gets its own secret via the chain keycloak · grafana · kong · oauth2-proxy postgres · traefik (cf dns) · alertmanager step 1: handle resolve ssh key step 2: ssh into vps step 3: secrets get final result — scrubbed three secrets cross — the agent watches all of it and sees none of it

06 — the rotation loop

“Rotate all my ten-year-old passwords.”
That was the brief. It works.

Lease the old password

A TTL-scoped borrow held in the daemon’s vault. If the flow fails, the lease dies with the old credential still valid.

Log in via the broker’s page

The broker types the old password into its own isolated browser page. The agent orchestrates; it observes only URLs and selectors.

Generate the new one out-of-band

CSPRNG-generated in the broker’s heap. It never enters the model’s context, the chat, or the log.

Set it, then verify a fresh login

Change, then prove the new credential works on a clean page. On failure, the worker reverts to the old password automatically.

Write the vault back, retire the lease

bw encode → bw edit over stdin pipes — never argv. The old lease is revoked; both values join the scrub taint.


07 — providers as adapters

Two methods. Any vault.

resolve() and isAvailable() — that’s the whole interface. Fail-closed everywhere; a missing CLI is a clean no, never a crash.

bitwardenbw cli
infisicalrest + cli
1passwordop cli
hashicorpvault cli
varlock@env-spec
totprfc 6238

08 — the guarantee, honestly stated

What we claim.
And what we don’t.

proven

The LLM never holds a raw secret. Handles, typed results, and scrubbed output only — verified against real infrastructure.
No plaintext of known secrets in the session log. The pre-persist seam obfuscates before anything hits disk.
One unlock per day, every process. The daemon model — spawn or attach, share the vault, die with nothing on disk.
Broker children get no inherited authority. SSH agent stripped, env blacklist, closed PATH, Landlock writes confined.

honest limits

Containment ≠ intent. A maliciously-prompted model can still request bad actions; the broker gates execution, not desire.
Unknown pasted secrets pass through. Redaction covers registered values — the operator registers once via /redact.
Encoding is an arms race. Scrubbing handles exact + base64 + URL-encoded. Double-encoded or transformed leaks need the deterministic redactors.
Hardware keys stay human. TOTP is broker-side; a physical WebAuthn touch remains an operator act via the human terminal.