diff --git a/docs/keycape-service-registrations.md b/docs/keycape-service-registrations.md index 87c4eec..106096f 100644 --- a/docs/keycape-service-registrations.md +++ b/docs/keycape-service-registrations.md @@ -116,7 +116,54 @@ clients: Human approvers use the existing KeyCape human flow with `approval:approve` only, still with `aud=approval-engine`. They must not receive -`approval:consume`. +`approval:consume`. As of 2026-09-09 that flow is `informed-decision`'s +(`INFD-WP-0001`); see `docs/approver-surface-requirements.md`. + +## `approval-engine-operator` — requested, unpresented, do not provision + +Answering `railiance-platform` (`RPF-WP-0035-T06`, `CCR-2026-0020`) 2026-09-09. +Their question was the right one and we could not answer it from this source +either, because the answer is *nobody*. + +**Nothing in this repository presents this client, and nothing is deployed that +could.** This engine only *verifies* bearer tokens; it never obtains one. The +sole outbound credential it holds is the mounted `audit-token` used for +audit-core delivery (`approval_engine/audit.py`), which is not an OAuth client. +`approval_engine/pep.py` is a consumer-side helper for PEPs such as +`secrets-engine`, which present their own tokens. There is no client-credentials +grant anywhere in the package, and `deploy/approval-engine.yaml` is unapplied. + +Scope by scope, the bundle we originally requested does not describe one actor: + +| Scope | Who would present it | +| --- | --- | +| `approval:create` | whoever *requests* an approval. Not named anywhere in the estate today; `gate-house` doctrine says which acts need one, but no requester identity has been settled | +| `approval:approve` | `informed-decision`'s **human** client, per its own registration. A service principal holding this is exactly the question `gate-house` has not ruled on — see the `principal_type` note below | +| `approval:read` | anything rendering or inspecting an approval | +| `approval:revoke`, `approval:supersede` | attended lifecycle operations | +| `approval:observe` | monitoring and operations | +| `approval:emit` | **redundant in normal operation** — the running server emits its own heartbeat on `--heartbeat-seconds` with no token; the route exists only to force one | + +So this was a convenience bundle written when this repo assumed a single +"lifecycle operator" would exist, and it aged badly: it now carries the widest +scope set in the pair, including `approval:approve`, for an identity nobody has +named. + +**Decision, option (b): cancel `CCR-2026-0020`.** No client-side reader is +wanted, because there is no presenter. This is an owner decision and not a +deferral — recorded here so the request does not rot `in_flight`. Verifier +custody (`CCR-2026-0018`) is unaffected and remains correct: KeyCape must be +able to *authenticate* this client if it is ever presented. + +When a presenter is actually named, the registration should be re-requested +**narrowed to that presenter's scopes**, not reinstated as this bundle. In +particular `approval:approve` should not travel with the operational scopes: +schema v4 records `entries.principal_type` so a service bind is legible, but +legibility is not permission, and whether a non-human may supply approver +evidence at all is `gate-house`'s to rule on. + +`secrets-engine-approval` above is unaffected — it has a real presenter, a +narrow scope pair, and is the client that unblocks `T05`. ## Human approver client — not supplied (2026-09-08) diff --git a/workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md b/workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md index 6d29310..5085ec0 100644 --- a/workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md +++ b/workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md @@ -184,6 +184,26 @@ is accepted rather than refused. T01 remains `progress`: what is left is key-cape owning the registrations, credential custody from railiance-platform, and the open scope question (A) above — none of them reachable from here. +2026-09-09 (third): `railiance-platform` (`RPF-WP-0035-T06`) asked who presents +`approval-engine-operator` and refused to guess — correctly, since it carries +the widest scope set of the pair. The answer is that nobody does. Nothing in +this repository obtains an OAuth token: the engine only verifies them, its one +outbound credential is the mounted `audit-token` for audit-core delivery, and +`pep.py` is a consumer-side helper for PEPs that present their own. Scope by +scope the bundle does not describe a single actor — `approval:approve` now +belongs to `informed-decision`'s human client, `approval:emit` is redundant +because the server emits its own heartbeat with no token, and no requester +identity has ever been settled for `approval:create`. It was a convenience +bundle written when this repo assumed a lifecycle operator would exist. +Answered (b): cancel `CCR-2026-0020` as an owner decision rather than leave it +`in_flight`; verifier custody `CCR-2026-0018` is unaffected and stays correct. +Recorded in `docs/keycape-service-registrations.md`, with the rule that a future +presenter gets a registration narrowed to its own scopes and that +`approval:approve` must not travel with the operational scopes — schema v4 makes +a service bind legible, but legibility is not permission, and the should-it +question remains gate-house's. `secrets-engine-approval` is unaffected: it has a +real presenter and is the client that unblocks T05. T01 stays `progress`. + ## Harden durable storage and migrations ```task