Credential vault
The vault holds the underlying credentials your agents use (Stripe key, database password, OAuth token, anything). Operators store them once. Agents request short-lived leases at the moment they need them.
Why this exists
Section titled “Why this exists”Long-lived static secrets in an agent’s environment are a problem for three reasons:
- They survive agent suspension. Killing the agent’s JWT doesn’t kill the API key that’s already cached in its process.
- They’re hard to rotate. Every change requires redeploying every agent.
- They have no audit. You can’t tell from the logs which agent used the key when.
Vault leases solve all three. The lease has an expires_at; revoking it is one API
call; every issue and every revoke writes an audit event.
Backends
Section titled “Backends”| Backend | Where the bytes actually live | Picked by |
|---|---|---|
static | credentials Postgres table, encrypted with AES-256-GCM (root key from AGENTUM_ENCRYPTION_KEY_HEX) | default (no env var) |
hashicorp | HashiCorp Vault KV v2 mount | AGENTUM__VAULT_BACKEND=hashicorp |
aws | AWS Secrets Manager | AGENTUM__VAULT_BACKEND=aws |
azure | Azure Key Vault | AGENTUM__VAULT_BACKEND=azure |
Configuration for each backend is in .env.
The lease lifecycle
Section titled “The lease lifecycle”-
Store the underlying credential (operator action, one-time):
Terminal window # Read the secret from an env var so it doesn't show up in shell historySTRIPE_KEY="sk_live_..." \agentum vault store \--agent-id <uuid> \--service-name stripe \--from-env STRIPE_KEY \--cred-type api_keyOr pipe it from stdin:
Terminal window printf '%s' "$STRIPE_KEY" | agentum vault store \--agent-id <uuid> \--service-name stripeTerminal window curl -X POST http://localhost:3000/api/v1/vault/credentials \-H "X-API-Key: $LUPID_API_KEY" \-H "Content-Type: application/json" \-d '{"agent_id": "<uuid>","service_name":"stripe","secret_value":"sk_live_...","cred_type": "api_key"}'Response includes the
cred_id. The plaintext is now encrypted in Postgres and never returns from this endpoint again. -
Issue a lease (agent or operator on its behalf):
Terminal window agentum vault issue --agent-id <uuid> --service-name stripeResponse:
{"lease_id": "2948e177-b327-448e-a746-b20161b3b444","value": "sk_live_...","expires_at": "2026-05-13T19:05:32.084569451Z","credential_type": "api_key","header_name": "X-Api-Key","backend": "static"}The
valueis delivered once. If you lose it, issue a new lease. -
Use the credential. Two patterns:
- Pass it directly. The agent puts the
valuein whatever HTTP client it uses. Simple, but the secret lives in the agent’s process for the lease duration. - Placeholder injection (proxy path only). The agent puts
Authorization: Bearer agentum://stripe-keyin its outbound headers. Lupid’s HTTPS gateway sees theagentum://marker and substitutes the real secret before the request leaves. The agent’s process never sees plaintext.
- Pass it directly. The agent puts the
-
Revoke immediately when the agent is done with it (or compromised):
Terminal window agentum vault revoke <lease-id>Or revoke every active lease for an agent in one shot (e.g. on suspension):
Terminal window curl -X POST http://localhost:3000/api/v1/vault/revoke-agent/<uuid> \-H "X-API-Key: $LUPID_API_KEY"Leases also auto-expire at
expires_at; the background lease-tracker reaps them.
Listing & inspection
Section titled “Listing & inspection”# Active leases for one agentagentum vault leases --agent-id <uuid>
# All active leasesagentum vault leases
# Pretty-print with table outputagentum --output table vault leasesThe dashboard’s /vault page shows the same data with a health strip (active vs
expiring-in-1-hour vs revoked-today), per-credential rail, and live lease feed over
SSE.
End-to-end test
Section titled “End-to-end test”There’s a Playwright test that exercises the whole pipeline against the live stack
end to end —
vault-live-flow.spec.ts.
Useful as a reference for the expected UI flow if you’re building your own
integration.