Status: live. OpenBao is the T2 runtime manager and the primary machine
interface. The AWS engine brokers automated and AI-agent AWS access, replacing
the static aws-vault base key. The GitHub service engine mints every GitHub
credential on demand; its cutover is complete.
An unattended agent cannot type a keychain password. Every secret it touches must be fetched, scoped, and expiring — or invisible to it entirely.The interactive paths this replaced bound AWS access and GitHub tokens to the macOS keychain with a human present. That does not work inside an ephemeral container or on an always-on agent host. The replacement is the four-tier secrets model, whose T2 runtime manager is built for exactly this read path.
The hard requirement: zero interactive prompts
An always-on agent host has no human at the keyboard when a job fires at 3 a.m. A GUI unlock, asudo Touch ID prompt, an MFA challenge — each is a wall the agent
cannot climb. So the hard requirement for any secret an autonomous agent consumes
is that the read is non-interactive: an AppRole login or a token file, never a
prompt. This is the single property that disqualifies the keychain for automated
flows and makes the self-hosted runtime manager the primary interface.
OpenBao: the headless read path
OpenBao (T2) is the primary AI/machine interface. An agent reaches it three ways, all promptless:- AppRole login. The agent presents a role ID (durable) plus a secret ID delivered as a response-wrapped, single-use, short-TTL token at container create (cloud-init for a host consumer, launcher env for an agent container). The unwrap happens exactly once; an intercepted bootstrap is self-evident.
- Rendered token / env file. A lightweight agent renews the lease and renders
the secrets the service needs into a
tmpfsfile the binary reads. Memory-only, destroyed on reboot, never on disk. - Process supervision. On a pooled agent guest, that same lightweight agent is the job unit’s entry point rather than a sidecar: it authenticates, renders the job’s credentials straight into the child environment, and execs the job. Credentials exist for the life of one job and never touch a shared file, so no job can start with credentials that were not scoped and leased for it.
- Signed SSH certificate. For SSH-based automation, OpenBao’s SSH CA signs a
short-TTL certificate for the automation principal (
ai-agent,ansible,ci). Humans keep their static keys; machines carry expiring certs. No long-lived automation key waits on disk to be stolen.
The engines have separate root credentials, roles, policies, leases, and
consumer endpoints. They share the OpenBao cluster, root and admin control
plane, Raft storage boundary, hosts, plugin runtime, and outbound HTTPS path.
Normal AWS-engine access does not authorize GitHub-engine access, or vice versa.
The AWS engine now replaces aws-vault and the keychain for automated state
access — a dedicated broker identity assumes narrow purpose roles (a role per
OpenTofu root, plus a permissions-boundary-capped admin-purpose role for AI
and general IaC use), and its own base key is seeded into OpenBao once
(write-once; rotation is a deliberate, separate operator action) so no
long-lived AWS key sits anywhere routine automation can reach it. The GitHub engine has
likewise replaced stored GitHub tokens: every GitHub credential is now minted on
demand and nothing is written to disk. UniFi and similar
static-credential upstreams move into KV v2, read by the provider at plan time.
The credential security ladder
Not every upstream can mint short-lived credentials. The ladder, from best achievable down to the floor — always climb as high as the upstream allows:- Dynamic, short-lived credentials where the upstream supports it. AWS STS via the OpenBao AWS engine; GitHub App installation tokens via the separate OpenBao GitHub engine. The credential the agent holds expires on its own; exfiltration has a deadline measured in minutes.
- Short-lived access to a static credential where the upstream cannot. UniFi is the canonical case: its API keys and local admin passwords are static — there is no rotation API, no STS equivalent. So the short-lived thing becomes the vault token that fetches it: minutes-to-1h lease, IP-bound, single-use bootstrap. Pair it with a dedicated least-privilege account (read-only local admin for exporters) and a scheduled rotation job so the static secret itself has a bounded lifetime.
- Credential-injecting egress proxy — the ceiling, and the only way to get
“safe even if the agent sees its own token” for backends stuck on static creds.
mitmproxy or Envoy’s
credential_injectorsits outside the boundary and adds the auth header in flight. The agent never sees the credential at all; there is nothing inside the container to exfiltrate. This matches Anthropic’s Agent SDK secure-deployment guidance and is an optional later item.
Rung 2 is honest about its limits: rotating a UniFi password is semi-manual and
the secret is still static between rotations. The protection you actually rely on
is the dying vault token plus the account’s narrow privileges — not the secrecy
half-life of the password.
Migration: current → target per secret class
The split that decides T2 vs T3 is self-hosted vs SaaS: credentials for
self-hosted services (object storage, the IaC control plane, config-management
runners, local databases) move to OpenBao (T2), while third-party SaaS logins
(external cloud APIs, package-registry accounts) stay in Doppler (T3) even when they
are not kingdom keys — they authenticate to outside services and remote CI needs
them. SOPS (T1) is correspondingly narrowed to values that are non-critical, front
no live or public service, and matter to a single repo.
What stays
- SOPS/age (T1) keeps committed deployment config — encrypted-at-rest-in-repo is a different job than runtime-leased credentials, and the runtime manager leaves it alone.
- The general-purpose macOS keychain shrinks
to Mac login convenience and human break-glass material — it stops being
load-bearing for the per-secret automated path. In its place, on an always-on
Mac agent host, the runtime secret injector publishes each per-domain OpenBao
AppRole
role_id/secret_idinto the agent process’s ambient environment (doppler run), so agent reads stay promptless. The injector delivers just the secret-zero that fetches the rest from T2.
See also
Tools comparison
The four-tier model, the tool matrix, and the read-path diagram.
OpenBao
The T2 runtime manager this page’s headless path talks to.
GitHub access
The separate GitHub service engine that mints every GitHub credential.
SOPS in repos
The T1 committed-config layer that stays.