Status: planned (phase 4). Infisical (LXC 108) is in-flight today; OpenBao is
not deployed. The existing OpenBao page predates this
design and sketched OpenBao as a cluster-local alternative; this design assigns it
the dynamic-credential role described below.
An unattended agent cannot type a keychain password. Every secret it touches must be fetched, scoped, and expiring — or invisible to it entirely.Today every secret is bound to the macOS keychain with a human present: AWS via aws-vault, UniFi creds, five GitHub PAT tiers. None of that works inside an ephemeral container. The replacement is a dual backbone, each tool doing what it is best at.
The split
| Backbone | Role | Examples |
|---|---|---|
| OpenBao (new LXC, Terraform + Ansible managed) | Dynamic and leased credentials; Terraform/Ansible-consumed secrets | AWS STS, UniFi KV, future PKI / SSH CA / database engines |
| Infisical (LXC 108, in-flight) | Static app secrets, env injection, sync | AI model API keys, infisical run -- runtime env, GitHub Actions secrets sync |
OpenBao: dynamic credentials and infra trust
- AWS — the AWS secrets engine issues STS credentials (AssumeRole) against narrow per-profile policies: the tf-state bucket and DynamoDB lock table, nothing else. This replaces aws-vault and the keychain entirely for Terraform state access. Minutes-lived, auto-expiring, no static IAM user anywhere.
- Terraform-consumed secrets — UniFi credentials for tofu-unifi move into KV-v2 and are read via Vault provider data sources. First-class Terraform and Ansible integration is exactly why OpenBao gets this role.
- Bootstrap — AppRole with response wrapping: the provisioner injects a single-use, short-TTL wrapped SecretID at container create (cloud-init for LXC consumers, launcher env for agent containers). The unwrap can happen exactly once; an intercepted bootstrap is self-evident.
Infisical: static app secrets
Infisical keeps the Doppler-replacement role from the existing roadmap (Doppler is still decommissioned, phase 7): AI model API keys, application runtime env viainfisical run, and GitHub Actions secrets
sync. Machine identities use Universal Auth with TTL, usage-limit, and trusted-IP
constraints on client secrets.
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 OpenBao’s secrets engine; GitHub App installation tokens (1h) via the broker. 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 phase 7 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
| Secret class | Current | Target |
|---|---|---|
| AWS tf-state access | aws-vault + keychain, double password prompt | OpenBao AWS secrets engine → STS AssumeRole, narrow per-profile policy |
| UniFi credentials | keychain / env at apply time | OpenBao KV-v2, read by Vault provider; rotation job; injecting proxy later |
| AI model API keys | keychain / Doppler | Infisical, injected at container create |
| App runtime env | Doppler doppler run | Infisical infisical run |
| GitHub Actions secrets | Doppler sync / manual | Infisical sync |
| GitHub API + push | 5-tier PATs in keychain | GitHub App + broker tokens — see GitHub access |
| Vault bootstrap | n/a | AppRole + response-wrapped single-use SecretID at container create |
What stays
- SOPS/age keeps committed deployment config — encrypted-at-rest-in-repo is a different job than runtime-leased credentials, and both backbones leave it alone.
- The macOS keychain shrinks to Mac login convenience and human break-glass material. It stops being load-bearing for any automated path.
See also
GitHub access
The App + broker design that retires the PAT tiers.
aws-vault
The tool the AWS STS engine replaces.
OpenBao (original sketch)
The earlier cluster-local design this supersedes in part.
SOPS in repos
The committed-config layer that stays.