Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e324-abce-7e51-bb2b-496f097afdb0
105 lines
5.2 KiB
Markdown
105 lines
5.2 KiB
Markdown
# 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.
|
|
|
|
## Consumer/platform reconciliation — 2026-09-27
|
|
|
|
The operator clarified that platform infrastructure belongs to tenant zero,
|
|
**`tenant:platform`**, including OpenBao and its platform service identities.
|
|
Coulomb is a workload on the platform; `coulomb.social` is currently shared DNS,
|
|
not a tenant assignment. The former matching `tenant:coulomb` declarations were
|
|
wrong. `codex-railiance-platform` and `secrets-engine-openbao` now declare
|
|
`tenant:platform`, coordinated with the platform role and consumer preflight.
|
|
This source correction does not migrate live registrations or existing tokens.
|
|
|
|
Public discovery at `https://kc.coulomb.social/.well-known/openid-configuration`
|
|
was rechecked: issuer `https://kc.coulomb.social`, JWKS `/jwks`, token endpoint
|
|
`/token`, with `client_credentials` advertised. This establishes public endpoint
|
|
metadata only, not that this confidential client is registered or provisioned.
|
|
Platform's RPF-WP-0035-T02 now carries the exact proposed login-only role/config/
|
|
policy. The remaining joint acceptance is protected client custody and actual
|
|
registration, attended role provisioning, native signature/claim refusals and
|
|
OpenBao self-revocation. KEY-WP-0009's provider-contract completion is unchanged.
|