railiance-platform/docs/credential-lane-designs/secrets-engine-service-jwt.md
codex 93a4bc2a9f CCR-2026-0038: allow local probe input before the login proof; refresh design status
secrets-engine a9ecbd6b: the contract file was promised only after the proof,
which needs it. The founder may write a non-secret probe input from the
applied values; platform publishes or confirms the contract after the proof.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 469992@bnt-lap001
Assistant-Session: e4b8e420-df0a-441d-83ae-7f19130594d9
2026-10-09 19:53:59 +02:00

9.4 KiB

Secrets-engine service JWT login

Status: CCR-2026-0038 applied 2026-10-08 (founder, readback matched); login proof (SECRETS-WP-0012-T03 probe-openbao) pending. Owner: railiance-platform, RPF-WP-0035-T02 (design origin RPF-WP-0032). Demand: State Hub message 38b47122-07eb-4a7f-a5df-13a38f50e110.

Contract and ownership

KeyCape's accepted provider contract is ../key-cape/docs/openbao-service-auth-contract.md. The consumer implementation is ../secrets-engine/src/secrets_engine/engine_auth.py; the existing coding agent role is openbao/auth/coding-agent-jwt-role.json. The service must have its own role and identity. It must not acquire the coding-agent role or an operator's policy set.

Propose an isolated JWT auth mount keycape-services, role secrets-engine-openbao, and policy secrets-engine-login-self. Survey first: if an equivalent service mount already exists, the owner may select it in the approved contract instead. Do not reconfigure the browser netkingdom mount or the coding-agent mount to introduce this service.

Setting Proposed value / evidence required
Verification RS256 only; issuer https://kc.coulomb.social, JWKS https://kc.coulomb.social/jwks; public discovery rechecked 2026-09-27; live registration remains unverified
Role type / user claim jwt / sub
Audience secrets-engine-openbao
Subject service:secrets-engine
Bound claims, string matching principal_type=service, tenant=tenant:platform, required roles=secrets-engine, scope=openbao:login
Token policy secrets-engine-login-self only; no default policy
Token bounds service token, TTL/max/explicit max 5m, 8 uses, no periodic token; budget must include cleanup
Logging verbose OIDC logging disabled; no JWT claim dump or token/accessor in receipts

The self policy permits only auth/token/lookup-self read, sys/capabilities-self update, and auth/token/revoke-self update. These explicit self endpoints are necessary because default policy is disabled. No KV read, policy/auth mutation, SecretID minting, child-token creation or renewal is granted. Verify the token's effective identity policies too: disabling default policy alone does not prove that aliases/groups add no permissions.

This is an identity login contract. It is intentionally insufficient to apply native lanes. ../secrets-engine/docs/native-lane-cutover.md still requires canonical ActionAuthorization, successful approval-engine consume, scoped attended production authority and consumer verification per lane. Granting this service standing wildcard ACL/role-write privileges would let a raw OpenBao token bypass those application checks. Any future execution entitlement needs a separate reviewed CCR type and backend-enforced scope. The current workload-kv-read schema cannot encode this JWT administrative authority.

Publication and custody

After live verification, publish a separate non-secret consumer file with exactly these fields (the template below is deliberately invalid until filled):

mount: keycape-services
role: secrets-engine-openbao
bound_issuer: null # required exact HTTPS KeyCape issuer; do not guess

Point SECRETS_ENGINE_OPENBAO_JWT_LOGIN only at the approved final file. The consumer requires its issuer to equal SECRETS_ENGINE_KEYCAPE_ISSUER. The client secret remains in the separately approved KeyCape client custody lane, mounted as a protected file; the JWT exchange and OpenBao login remain in memory or private temporary input with cleanup. This design does not establish that client secret's live path or provision it. --auth service-jwt is the explicit pilot; failure must never fall back to bootstrap/env authentication.

KeyCape bounds JWT lifetime to 15 minutes and provides no refresh token. OpenBao login is not a one-time consumption guarantee for that JWT: it may be replayed while valid. Disabling issuance does not recall already issued JWTs or OpenBao tokens. In an incident, disable the exact role/client and revoke issued service tokens through owner custody; do not disable a shared mount. Without active revocation, residual access can extend by the OpenBao token TTL after the last accepted JWT login. Renew by a fresh exchange; always self-revoke after use.

Approval and acceptance

KeyCape confirms issuer, verification endpoint, registration and claim types; platform approves the exact mount/config/role/policy and effective policy set; secrets-engine accepts the five-minute token/use budget and login-only scope. The installed OpenBao version must support the reviewed parameters. A metadata survey must show no conflicting role before any write.

Prove correct-service login, exact policy set, expiry/use exhaustion and cleanup. Reject wrong issuer/audience/subject/tenant/principal/role/scope, invalid or expired signatures, and coding-agent credentials. Prove KV read, policy write, SecretID issuance and child-token creation are denied. Separately prove a failed login never consults bootstrap/env providers. Retain only timestamps, public contract revision, capability outcomes and boolean cleanup evidence. First-lane apply remains gated on SECRETS-WP-0007-T04 and its own exact approval.

Rollback disables the role and revokes its tokens; removing an exclusively owned empty mount is a later review. Existing ESO lanes and interim proxies remain available. The login contract is withdrawn until verification is green.

OpenBao's JWT documentation describes signature verification and claim binding; its role API defines token TTL/use limits and the explicit maximum. These are mechanism references, not evidence of this cluster's installed configuration.

Prepared source return — 2026-09-27

The exact proposed bundle is openbao/auth/keycape-services-config.json, openbao/auth/secrets-engine-jwt-role.json and openbao/policies/secrets-engine-login-self.hcl. These files are non-secret inputs for the existing attended platform procedure, not an applied receipt. The discovery document advertises /token and client_credentials.

The operator clarified that platform infrastructure belongs to tenant zero, tenant:platform. Coulomb is a workload tenant; its DNS domain is not ownership. The former tenant:coulomb service registration and consumer preflight were consistent but incorrectly scoped. The corrected source uses tenant:platform for both OpenBao infrastructure clients and the existing approval clients.

Before writing: survey mounts/roles, confirm exact live client registration and protected client-side custody, review this bundle and obtain the scoped attended window via warden access openbao-platform-admin-login --exec -- <reviewed-command>. Do not execute a placeholder command. The reviewed child must install only this isolated mount/config, self policy and role, compare effective policy metadata, then perform the positive/negative/expiry/use-limit/revocation checks above. Do not print login responses. Publish the consumer contract only after those checks pass. A conflicting existing mount/role requires review, not overwrite. No service-client secret is created, moved or requested by this source return.

Gate check — 2026-10-03 (CUST-WP-0075)

Still proposed. SECRETS-WP-0007-T04 is done (2026-09-27), so the gate named above is open, but the "before writing" preconditions are not: KeyCape has not claimed a live registration of secrets-engine-openbao, and its client-credential custody has no owning task (KeyCape contract: "a separate approved custody action"). No reviewed child is written. RPF-WP-0035-T02 waits on key-cape naming that task.

Owner return and blocked dependency contract — 2026-10-04

KEY-WP-0037-T02 now owns separately approved protected custody, T03 the actual deployment-owned registration operation, and T04 native issuance evidence. key-cape/docs/secrets-engine-openbao-owner-packet.md supplies the candidate issuer path and exact registration contract. Platform prepared proposed CCR-2026-0033 and exact-path issuer reader policy/auth/ESO/receipt artifacts; source generation does not allocate a value or establish registration.

secrets-engine must accept the actual consumer host/file owner/path, mode-0600 delivery mechanism and rotation/revocation ownership before the bounded custody child is finalized. Issuer-side ESO is a separate copy. An owner source acceptance receipt, independent of full live service login completion, supplies this gate. KeyCape's current exact-primary receipt remains pending scoped repair of an existing grandfathered ADHOC task identity. Do not infer synchronization from its source contract or local managed IDs.

docs/provisioning-windows/2026-10-04-1000/dependency-requirements.md names all source/acceptance/reopen boundaries. RPF-WP-0035-T02 remains wait; its workplan is blocked on those real dependencies and separate attended execution approval.

Source complete — 2026-10-09

KEY-WP-0037 finished 2026-10-07 and CCR-2026-0033/0034 are applied, so the "before writing" preconditions are met. The reviewed bundle is now packaged as CCR-2026-0038 (request_type: service-jwt-login), applied by the delegated applier (scripts/service_jwt_lane.py): policy, auth enable of keycape-services only if absent (refuses a foreign type), config, role, then a value-free readback compare. The self policy gained explicit denies on the client-secret data/metadata paths (ops-mason MASON-WP-0009 invariant (ii)). The founder command is recorded in RPF-WP-0035-T02.