key-cape/docs/openbao-service-auth-contract.md
tegwick efce3e9331
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 25s
Implement KeyCape provider and service identity contracts
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02e3f-7301-7622-9be1-12e5f352881c
2026-08-23 13:10:13 +02:00

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_credentials exchange 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.