Skip to main content
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, a sudo 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 tmpfs file 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.
Two independent dynamic secrets engines back the highest rung of the ladder: 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:
  1. 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.
  2. 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.
  3. 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_injector sits 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_id into 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.