diff --git a/docs/service-auth.md b/docs/service-auth.md index a999c2d..261a5dd 100644 --- a/docs/service-auth.md +++ b/docs/service-auth.md @@ -6,7 +6,7 @@ secrets-engine now implements the consumer half of KeyCape's accepted the non-cryptographic contract before any future OpenBao login: - subject `service:secrets-engine`, audience/client `secrets-engine-openbao`; -- service principal in `tenant:coulomb`, role `secrets-engine`; +- service principal in `tenant:platform` (platform infrastructure, tenant zero), role `secrets-engine`; - exact `openbao:login` scope and KeyCape AAL1 client-secret assurance; - RS256 declaration, bounded issue/expiry timestamps, maximum 15-minute life, and renewal when no more than three minutes remain; diff --git a/docs/tenant-alignment.md b/docs/tenant-alignment.md index 0fc2790..584babd 100644 --- a/docs/tenant-alignment.md +++ b/docs/tenant-alignment.md @@ -1,3 +1,10 @@ +> Superseded source finding, 2026-09-27: the operator confirmed that OpenBao +> and its infrastructure service identities belong to `tenant:platform` (tenant +> zero). Coulomb is a workload tenant. The former `service_auth.TENANT` value +> discussed below is historical; source preflight now requires platform and +> rejects Coulomb. Live migration remains SECRETS-WP-0008-T06. See the platform +> owner's `docs/platform-tenant-essentials-review.md` for the cross-repo review. + # Tenant alignment Answer to the `GLAS-WP-0015` production-dependency handoff question, which asked diff --git a/src/secrets_engine/authorization.py b/src/secrets_engine/authorization.py index 387bee2..398b3e2 100644 --- a/src/secrets_engine/authorization.py +++ b/src/secrets_engine/authorization.py @@ -36,11 +36,9 @@ DIGEST_RE = re.compile(r"^sha256:[0-9a-f]{64}$") #: the vendored replay fixtures in ``tests/test_decision_replay.py`` instead of #: being left to a deployment to supply correctly. #: -#: This is not the KeyCape JWT tenant. ``service_auth.TENANT`` is -#: ``tenant:coulomb``, which is the exact value this package denies. Whether -#: those name one tenant or two layers is an open owner question -- see -#: ``docs/tenant-alignment.md`` -- and is deliberately not resolved by reusing -#: one constant for both. +#: Operator clarification, 2026-09-27: the platform infrastructure identity +#: also belongs to tenant:platform. Keep audience-specific profile validation; +#: matching tenant strings do not grant cross-audience or workload authority. REQUEST_TENANT = "tenant:platform" SCHEMA_VERSION = "0.1" diff --git a/src/secrets_engine/service_auth.py b/src/secrets_engine/service_auth.py index ad125b0..66abb9e 100644 --- a/src/secrets_engine/service_auth.py +++ b/src/secrets_engine/service_auth.py @@ -1,7 +1,7 @@ """KeyCape service-JWT exchange shared by two separately validated profiles. The OpenBao login profile and approval consumer profile have distinct clients, -audiences, tenants and scopes. Provider selection never falls back to another +audiences and scopes; both belong to tenant:platform. Provider selection never falls back to another identity. Token parsing is a claim preflight, not signature verification: the receiving OpenBao or approval-engine service verifies RS256 before accepting it. """ @@ -24,7 +24,7 @@ from secrets_engine.openbao import read_strict_token_file CLIENT_ID = "secrets-engine-openbao" SUBJECT = "service:secrets-engine" PRINCIPAL_TYPE = "service" -TENANT = "tenant:coulomb" +TENANT = "tenant:platform" ROLE = "secrets-engine" SCOPE = "openbao:login" MAX_TOKEN_SECONDS = 15 * 60 diff --git a/tests/test_service_auth.py b/tests/test_service_auth.py index 3616d61..6c0b271 100644 --- a/tests/test_service_auth.py +++ b/tests/test_service_auth.py @@ -30,7 +30,7 @@ def _jwt(**overrides): "iat": now, "exp": now + 900, "principal_type": "service", - "tenant": "tenant:coulomb", + "tenant": "tenant:platform", "roles": ["secrets-engine"], "groups": [], "scope": "openbao:login", @@ -68,6 +68,7 @@ def test_preflight_accepts_exact_service_contract_and_renewal_window(): ({"iss": "https://attacker.test"}, "'iss'"), ({"sub": "service:operator"}, "'sub'"), ({"aud": "different"}, "'aud'"), + ({"tenant": "tenant:coulomb"}, "'tenant'"), ({"tenant": "tenant:other"}, "'tenant'"), ({"roles": ["admin"]}, "roles"), ({"scope": "openbao:login admin"}, "scope"), diff --git a/workplans/SECRETS-WP-0008-layer-model-lifecycle-conformance.md b/workplans/SECRETS-WP-0008-layer-model-lifecycle-conformance.md index b510d61..0fbe73f 100644 --- a/workplans/SECRETS-WP-0008-layer-model-lifecycle-conformance.md +++ b/workplans/SECRETS-WP-0008-layer-model-lifecycle-conformance.md @@ -331,7 +331,7 @@ Acceptance: ```task id: SECRETS-WP-0008-T06 status: wait -blocking_reason: "RPF-WP-0035-T02 still awaits exact service claims/tenant, credential custody and scoped attended JWT role provisioning with native login/negative/revocation proof. The historical env-auth acceptance is not service-JWT adoption." +blocking_reason: "RPF-WP-0035-T02 awaits coordinated platform-tenant live registration, credential custody and scoped attended JWT role provisioning with native login/negative/revocation proof. The historical env-auth acceptance is not service-JWT adoption." priority: medium state_hub_task_id: "d7bc8bdc-a0f8-5058-a640-374ef9859148" ``` @@ -354,12 +354,13 @@ Designed: role/audience `secrets-engine-openbao`, subject budget, login-only. KeyCape issuer `https://kc.coulomb.social` and its JWKS are now confirmed live, so the remaining blocker is this registration's issued claims, consumer readiness, an approved source and attended apply authority. -**Open question for secrets-engine, not answered in the triage session:** the -JWT design says `tenant:coulomb`, while the approval chain resolved to -`tenant:platform` (decision `5ed3fb35`) and approval-engine compares tenant by -exact string. Which tenant the OpenBao service identity carries is a design -decision for an owner session; platform will correct its design before the -role exists once told. A service login grants no lane mutation authority. Companion §7 / statute §3.4: an +**Resolved by the operator, 2026-09-27:** OpenBao is platform infrastructure, +belonging to tenant zero, `tenant:platform`. Coulomb is a workload tenant; +its DNS domain does not own the platform. The former matching Coulomb claims +were incorrect. KeyCape registrations, platform JWT roles and this consumer's +strict preflight are corrected together in source. Live registration/custody, +coordinated deployment and native refusal/revocation proof remain T06. +A service login grants no lane mutation authority. Companion §7 / statute §3.4: an agent holds no long-lived credential of its own. Authority is per task, time-bounded, and attributable to the principal it acts for. @@ -511,6 +512,17 @@ refuses nested/identity-bearing version pins and names the accepted text and rulings on every run, including PASS. Declaration spellings are unchanged. T06 remains wait: railiance-platform's RPF-WP-0035-T02 still records an -unprovisioned, login-only JWT role, pending exact service claims/tenant and +unprovisioned, login-only JWT role, pending platform-tenant live registration and custody/attended admission. The existing env-auth native receipt cannot prove steady-state service JWT login. This is the only remaining task in this workplan. + +### 2026-09-27 platform essentials follow-up + +Operator tenant-zero decision `be5287cb-d458-47bc-9183-2a40dd7ae04b` corrects +OpenBao infrastructure identities to `tenant:platform`. Source changes are +coordinated in KeyCape, railiance-platform and Secrets Engine. The platform +owner records the bounded essentials review in +`docs/platform-tenant-essentials-review.md`. T06 remains wait for coordinated +live registration/custody, provisioning and native refusal/revocation evidence. +No new task or workplan was created. Warden's npm no-rotation hold remains; +factory records now consume the completed generic approval and companion returns.