Skip to main content
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

BackboneRoleExamples
OpenBao (new LXC, Terraform + Ansible managed)Dynamic and leased credentials; Terraform/Ansible-consumed secretsAWS STS, UniFi KV, future PKI / SSH CA / database engines
Infisical (LXC 108, in-flight)Static app secrets, env injection, syncAI 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 via infisical 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:
  1. 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.
  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 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 classCurrentTarget
AWS tf-state accessaws-vault + keychain, double password promptOpenBao AWS secrets engine → STS AssumeRole, narrow per-profile policy
UniFi credentialskeychain / env at apply timeOpenBao KV-v2, read by Vault provider; rotation job; injecting proxy later
AI model API keyskeychain / DopplerInfisical, injected at container create
App runtime envDoppler doppler runInfisical infisical run
GitHub Actions secretsDoppler sync / manualInfisical sync
GitHub API + push5-tier PATs in keychainGitHub App + broker tokens — see GitHub access
Vault bootstrapn/aAppRole + 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.