Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02e3f-7301-7622-9be1-12e5f352881c
4 KiB
KeyCape service authentication for OpenBao
Date: 2026-08-23
Status: accepted provider contract; live client provisioning remains a
deployment and custody operation
Ownership boundary
KeyCape owns static confidential-client registration, client authentication, JWT signing, IAM Profile service claims, expiry, and disabling future issuance. The OpenBao platform owner owns JWT auth mounts, exact-bound roles, policies, OpenBao token TTL/use limits, and value-safe authorization evidence. The consumer owns renewal and fail-closed use. OpenBao or its approved delivery path owns confidential-client secret custody.
KeyCape accepts the codex-railiance-platform issuance identity and the
secrets-engine-openbao service identity under this contract. It does not own
the resulting OpenBao identity token or policy. ops-warden remains the SSH
certificate issuer only, and user-engine is not in the machine-principal path.
Exchange and claims
The consumer sends POST /token with grant_type=client_credentials, HTTP
Basic client authentication, and the allow-listed scope=openbao:login.
KeyCape returns an RS256 access token and no ID token or refresh token.
Every issued service token contains:
| Claim | Contract |
|---|---|
iss |
the configured KeyCape issuer |
sub |
the registration's exact serviceSubject |
aud |
the exact clientId |
iat, exp |
issue and expiry time; maximum per-client lifetime below |
principal_type |
service |
tenant |
the registration's exact tenant |
roles |
the registration's allow-listed roles |
groups |
an empty array |
scope |
only requested scopes from the registration allow-list |
assurance |
AAL1, client_secret, non-MFA, source key-cape |
OpenBao JWT roles must bind the exact issuer, audience, subject,
principal_type, tenant, and required role. A consumer must reject an issuer
or claim mismatch before attempting OpenBao login.
Accepted registrations
The source-of-truth non-secret fragments are in
config/service-clients.example.yaml.
| Client | Subject | Role | Scope | Maximum JWT lifetime |
|---|---|---|---|---|
codex-railiance-platform |
service:codex:railiance-platform |
coding-agent |
openbao:login |
15 minutes |
secrets-engine-openbao |
service:secrets-engine |
secrets-engine |
openbao:login |
15 minutes |
The confidential secret is referenced as an environment variable in KeyCape configuration and must be materialized without entering Git, State Hub, chat, process arguments, or normal logs. Registering or rotating the live value is a separate approved custody action.
Renewal, expiry, and revocation
- There is no refresh token. Obtain a new KeyCape JWT with a new
client_credentialsexchange when no more than three minutes remain. - A consumer may retry a transient exchange failure with bounded backoff only while its current JWT is still valid. It must stop the privileged operation at expiry.
- KeyCape JWTs are self-contained and cannot be recalled after issue. Disabling the client or rotating its secret stops new issuance; the residual exposure is bounded by the 15-minute JWT lifetime and the OpenBao token's independent TTL/use limits.
- After OpenBao login, the consumer must explicitly revoke the OpenBao token in its cleanup path. A cleanup failure is actionable evidence and must not print a JWT, OpenBao token, client secret, or token accessor.
Failure semantics
Authentication, scope, claim, signature, issuer, expiry, OpenBao login, and cleanup failures are fail-closed. Steady-state consumers must not implicitly fall back to bootstrap token files, an operator identity, a broader AppRole, or an unbound JWT role. Bootstrap files remain explicitly selected setup or break-glass providers and require their own authorization and evidence.
Verification must inspect metadata and capabilities without reading a secret
value. A coding-agent check must prove the agent-high-risk-boundary deny wins
against any otherwise readable workload policy.