Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02e47-6aac-7ee1-914d-0584c75d3c81
4.4 KiB
Action-bound authorization contract
Status: implemented for execute-time request binding; durable approval storage requires State Hub support.
Date: 2026-08-23
Ownership and endpoints
flex-auth owns authorization evaluation and the canonical request/decision vocabulary. A protected system obtains a current decision from:
POST /v1/check
The response now includes binding, the exact normalized subject, action,
resource, context, and full SHA-256 request digest evaluated by the policy.
Consumers must compare structured fields; reason, diagnostics, titles, and
other prose are never an authorization contract.
State Hub owns durable organizational-decision identity, resolution, supersession, and availability. Its current endpoint is:
GET /decisions/{uuid}
That object is not yet sufficient for privileged action execution. As of
2026-08-23 it has lifecycle fields and superseded_by, but action details live
only in prose and it records only one free-form decided_by value. Production
consumers must not infer action binding or dual control from it.
Durable object
../schemas/action_authorization.schema.json
defines the proposed ActionAuthorization storage and transport object. It
combines:
- a canonical UUID and explicit lifecycle status;
- one complete
CheckRequestcontaining the actor, action, resource, exact target attributes, stage, purpose, and other policy inputs; - a bounded validity window;
- approval count and individual approval evidence;
- the flex-auth
DecisionEnvelopewhose structured binding and digest match that request.
The schema is generic. For secrets-engine, use this profile:
| Requirement | Canonical field |
|---|---|
| catalog id | request.resource.id |
| stage | request.resource.attributes.stage |
| lifecycle operation | request.action |
| exact secret fields | request.resource.attributes.fields |
| policy/auth targets | request.resource.attributes.policy_targets / auth_targets |
| requesting actor | request.subject |
| purpose | request.context.purpose |
| execution window | validity.not_before / validity.expires_at |
| dual control | approvals.required_count and distinct entries[].subject_id |
Array values that express exact targets are sets: producers sort and de-duplicate them before evaluation; consumers require exact set equality. Wildcards are policy inputs and should be denied for destructive production actions.
Required verification
A production consumer may execute only when all of the following hold:
- The durable object resolves from its canonical authority; an outage fails the action closed.
statusisapproved,superseded_byis absent, and the current time is inside the validity window.- The caller's proposed action exactly matches
request, including resource id, system, stage, target sets, actor, and purpose. decision.effectisallow;decision.bindingmatches the request and its digest; the policy package/version are accepted for the deployment.- Approval entries contain at least
required_countdistinct authenticated subjects. Repeated entries from one subject count once.
Local fixtures, CCR labels, workplan ids, prose, and a caller-supplied flex-auth decision id are provenance only. They cannot unlock a production action.
Outage and supersession semantics
- State Hub or future authorization-object store unreachable: privileged live action denied; read-only inspection and dry-run may continue with a clear non-authoritative status.
- flex-auth unavailable when a fresh decision is required: privileged live action denied. A previously stored decision is usable only inside its bounded validity window and only if the authoritative lifecycle object can still be checked for supersession or revocation.
superseded,expired,revoked,denied, or unknown status: deny.superseded_bypresent regardless of status: deny and resolve the replacement explicitly; never follow it silently during execution.
Remaining external work
State Hub needs a structured object/endpoint capable of storing this schema or equivalent fields, authenticated approval entries, and atomic supersession. Until that lands, secrets-engine is correct to keep live destructive actions disabled. The standalone evaluator and the Topaz, relationship, rule, and Keycloak delegated adapter boundaries now all populate the same binding.