Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
12 KiB
Approval Engine metadata execution consumer contract — source proposal
Author: approval-engine source reviewer (Codex), staged only. Work record:
APPROVAL-WP-ADHOC-2026-10-11-T01, bounded completed source preparation. Existing
APPROVAL-WP-0002-T05 remains unchanged wait/residual adoption. This is an
author-qualified technical proposal, not a formal delegated owner review,
owner acknowledgement, founder approval, identity admission or runtime receipt.
Source checkout: c8ae88057d, including pre-existing
uncommitted owner files listed in source-state.json; none modified.
Reviewed proposals: SECRETS-EXEC-20261011-v1 and platform-task-execution/v1. Their exact source-file hashes are in source-state.json. Initial execute-ccr metadata pilot only; no generic grants, daemon, standing service permissions, raw bearer handoff or full Secrets adoption.
Existing route and trust boundary
- GET /v1/approvals/{id}/claim requires approval:read and exact store tenant. It supplies fresh approval facts and native five-field act binding, approval-free PDP digest, PDP/human declarations. consumed=true lacks consumed request/decision correspondence and cannot confirm execution admission.
- POST /v1/approvals/{id}/consume requires approval:consume, exact tenant and service/agent principal. Body is request_digest plus decision_id. It returns approval_id, status=consumed, request_digest, original decision_id when stored, consumed_at and explicit idempotent boolean. Direct response to an authenticated independent mediator is authoritative mutation evidence on its trusted transport.
- GET /v1/approvals/{id} requires approval:read. Approval.as_dict returns id, persisted status, exact binding, validity, and consumption containing original request_digest/decision_id/consumed_at. This existing read is the independent consume-confirmation surface. It includes approver entries, so capture in memory and discard them; never serialize the raw object into a receipt or tool output.
There is no signed portable consume receipt or dedicated public confirmation endpoint. A caller boolean, ConsumedApproval dataclass, JSON receipt or local file is not trusted proof. The mediator fetches the authoritative object itself through its owner-pinned authenticated endpoint, with no caller-selected origin/opener. Bearer authentication identifies the client to the server; the Platform owner must also establish the server/transport identity. Generic HTTP support in the helper is not evidence of an admitted production transport.
Identity/read reachability is explicitly unproved. The admitted Secrets client does not grant a new Platform mediator approval:read/consume custody. KeyCape owns any exact client/audience/scope/tenant registration and Platform owns transport and credential custody; do not widen an identity profile or reuse Secrets credentials merely to make a read work. In-cluster callers require the existing reviewed NetworkPolicy namespace admission too. No credential was obtained in this review.
Exact binding responsibilities
Source mechanisms: api.py ApiApplication.identity and consume route; auth.py RS256/JWKS claim verification; store.py Engine.consume/Approval.as_dict/claim; binding.py canonical_binding; execution_confirmation.py staged strict client.
Native binding digest covers action/actor/principal/purpose/target, preserving arbitrary nested target. Extra top-level execution context is not part of this native digest. It must be covered by the PDP's approval-free digest issued as pdp_digest and the full enriched decision binding.request_digest submitted to consume. Access owner reports ApprovalBindingDigest removes only top-level context.approval: execution_contract remains covered. Compute through Access's actual canonical CheckRequest implementation; never assume Python sorted-key JSON is the Go request digest. Full enriched request includes the served claim.
Engine.consume validates digest syntax, not equivalence to pdp_digest or act, and does not validate a DecisionEnvelope, decision_id existence or consuming sub against binding.actor. This is the PIP boundary, not a second policy decision point. A service holding consume scope can spend an approved object with an unrelated well-formed digest; that response cannot substitute for the mediator's prior independent exact claim/PDP/body/action/target/actor/principal validation.
Access's present v2 apply rules do not evaluate execution_contract. A distinct execute-ccr action, published exact package/version/digest and verified initiating principal/representation mapping are prerequisites, not inferred semantics. Create enforces binding.actor==verified requester sub; the design must resolve actual initiating principal vs execution service with Identity/Access owners, without guessing actor strings or treating environment subject text as evidence. All owner-compiled CCR/grant/catalog/source revisions, methods/paths/body hashes, operation UUID and deadlines must remain in the approved underlying context. Approval IDs and served claim additions must follow Access's explicit exclusion.
Idempotency and lifecycle
BEGIN IMMEDIATE serializes consume; status compare-and-swap and local use outbox are one transaction. The first consume requires approved status, current validity window and declared human-control evidence. Different consumed digest conflicts. Same digest returns idempotent=true and the original decision_id even if a retry submits another decision_id, even after original expiry. No second use event is created. That success is historical consumption, never renewed execution power. The staged client requires exact original decision correspondence and rejects string/integer booleans, missing times and decision-shaped responses.
Revoked/superseded/expired objects reject a first consume. Consumed is terminal: revoke and supersede cannot cancel it, and expiry does not erase historical consumption. A spent claim has valid_now=false (after deadline its reason/state is expired while consumed remains true). Rechecking valid_now after consume cannot validate continuation. The original validated approval/PDP/operation deadlines bound every business write; failure stops further writes. In-flight cancellation requires a separately owned execution stop/cleanup mechanism, not post-consume approval revocation. Cleanup has its explicit original obligation.
Independent mediator sequence
- Independently reconstruct owner-approved operation bodies and binding; check identity/source/revisions/deadline and exact published PDP package. No native secret read/login, token mint or mutation during preparation.
- Atomically reserve a one-shot execution journal admission for the immutable approval/request/operation tuple; only creator proceeds. This journal is Platform execution state, distinct from approval-engine outbox/consumption.
- Fetch and validate a fresh human-controlled PDP-path claim, call Access Check, require exact underlying/full digest, original act and policy pin, then one CAS consume. Prefer consume at this mediator boundary to early CLI consumption.
- Validate direct consume evidence with consume_exact; if separate Secrets code returns a receipt, independently GET confirm_consumption with expected native act and approval-free PDP digest. Match original exact decision/full digest/time. Do not issue another mutating consume merely to discover whether one happened.
- Persist confirmed evidence in the existing live journal before protected calls. A successful idempotent response or GET without that same live one-shot admission never permits execution. A CAS/read timeout marks unknown; owner reconciliation may read confirmation but does not automatically resume work.
- Check original deadlines before every native call, record about-to-call then outcome, and maintain separately confirmed cleanup. Duplicate completed input returns existing receipt only. Any incomplete/unknown operation requires owner reconciliation and fresh exact recovery approval for another protected action.
The staged client implements only exact direct evidence and independent read confirmation. It does not implement the journal, authorization policy, server identity admission or execution. Existing Secrets consume_approval accepts wrong approval_id, does not require original decision match and coerces idempotent; Secrets owns its strict handoff refinement. Do not silently extend the generic ProtectedActionHarness into an at-most-once execution service.
Gates and validation
Existing owners/tasks retained: APPROVAL-WP-0002-T05 served T-06 consumer proof; APPROVAL-WP-0002-T01 issuer/profile plus schema-v6 release evidence; SECRETS-WP-0013 implementation and SECRETS-WP-0008-T06 full adoption; RPF-WP-0035-T02 native lane adoption; exact Access/Identity action/representation admission under those owners' records. No dependency is marked closed.
24 focused tests pass, including authenticated WSGI GET with no mutation/outbox change, wrong act/target/actor/principal/purpose/digest/decision refusal, strict booleans/time, denied read, filtered evidence and idempotency after expiry with terminal revoke/supersede refusal. Staged full suite: 219 passed, one existing JWT short-test-key warning. No production HTTP/DB/OpenBao call or native action.
Transport repair proposal after local redirect counterexample
The original source review above does not attest the inherited transport's authoritative reachability. A later synthetic two-origin counterexample proved that urllib's previous default followed a redirect, forwarded Authorization and accepted its JSON. This is retained as a failed transport outcome, not rewritten into a success. Bounded source repair is a new demand under APPROVAL-WP-ADHOC-2026-10-11-T02, without residual promotion or native admission.
The staged default ApprovalHTTPClient transport uses the fixed provider-owned http_transport.py child via the current Python executable with -I. Request data, including bearer material, travels only through private process pipes, never argv, logs or diagnostic output. The child receives an empty environment; environment proxy settings are ignored, every redirect refuses without issuing another request, status must be exactly 200, Content-Type must be a single application/json with UTF-8 (or no charset), compressed/oversized/inconsistent responses refuse, and duplicate-key/non-finite/ malformed/non-object JSON refuses. Backend/error bodies are not read or exposed. Public errors contain closed provider messages and numeric status only.
The parent's subprocess timeout covers DNS/connect/TLS/header/body/error-close and process cleanup for the fixed child, which has no retry or subprocess descendants. It avoids SIGALRM and multiprocessing-spawn main-guard requirements so default SDK calls remain usable in worker threads and ordinary scripts. The token is read from a bounded local regular file using nonblocking/no-follow descriptor traversal: no symlink component, final hardlink, other-readable or group-writable mode, non-current/non-root owner, FIFO/directory or oversized credential is accepted. Restrictive current/root-owned group-readable files (such as 0440) are permitted; unknown projected/symlink credential layouts must be corrected by the custody owner, never silently followed or substituted. Only ASCII non-whitespace bearer content is admitted; credential errors do not republish paths or values. The file is reopened every request, retaining rotation.
The explicit opener injection is retained solely as a trusted owner test-adapter seam; it does not inherit isolated/default transport admission or total deadline guarantees. Supplying urllib.urlopen as an injected opener recreates its own redirect/proxy behavior and must not be admitted as production execution confirmation. Native mediator implementation must use this fixed default or a separately reviewed equivalent; caller-selected transport code is unacceptable. The default code still supports HTTP for reviewed private estate routes; source validation does not prove TLS, the actual server origin, deployed custody/read scope or ingress admission. No native identity/profile is widened.
After a timeout/refusal, consume remains potentially unknown and cannot be retried to infer absence. Fresh exact claim/PDP validation, returned original consumption correspondence and separate one-shot journal remain required. This is a transport repair only, never an execution authority or replay service.