--- id: FLEX-WP-0017 type: workplan title: "Action-bound authorization and durable approval contract" domain: infotech repo: flex-auth status: finished owner: codex topic_slug: netkingdom planning_priority: P1 planning_order: 117 created: "2026-08-23" updated: "2026-09-01" state_hub_workstream_id: "d75b7256-8b3d-5797-911c-96c3199b8baa" --- # FLEX-WP-0017 - Action-bound authorization and durable approval contract secrets-engine needs production authorization that binds an approval to an exact action, catalog lane, stage, targets, actor, purpose, validity window, and distinct approvers. The existing flex-auth decision response and State Hub decision object each provide only part of that contract. ## Bind execute-time decisions to the evaluated request ```task id: FLEX-WP-0017-T01 status: done priority: high state_hub_task_id: "e7b47e1d-58e8-503c-be89-e8f2050215b1" ``` Add a structured `binding` to standalone `DecisionEnvelope` responses with the normalized subject, action, resource, context, and full SHA-256 request digest. Add schema and regression coverage. Prose remains diagnostic only. ## Define the durable authorization object and semantics ```task id: FLEX-WP-0017-T02 status: done priority: high state_hub_task_id: "df7984fb-c31f-5b26-bf42-193e4c3cbb9f" ``` Publish `schemas/action_authorization.schema.json` and `docs/action-bound-authorization-contract.md`, including exact target mapping, validity, distinct approvals, supersession, and fail-closed outage semantics. Corrective verification 2026-08-23: secrets-engine detected that the example's stored request digest predated its final request shape. The fixture now carries the digest produced by `NewDecisionBinding`, and the API test compares the full published binding to a freshly generated canonical binding so future fixture drift fails the suite. ## Add durable storage and authenticated approval evidence ```task id: FLEX-WP-0017-T03 status: cancel priority: high state_hub_task_id: "82d39961-8140-5a7f-9bd8-5164dd1742e5" ``` State Hub must add a structured endpoint/object equivalent to the published contract, authenticated approval entries, and atomic supersession. Its current `/decisions/{uuid}` shape has only prose plus a single free-form `decided_by`. No flex-auth-local substitute is acceptable because flex-auth does not own the organizational approval lifecycle. Re-routed 2026-08-28 by FLEX-DEC-2026-001 (assent to gate-house GH-DEC-2026-001): under the authoring/evaluation split, gate-house designs the approval contract and flex-auth validates approvals at decision time. The *design* half of this task is therefore addressed to gate-house. The *storage and lifecycle* half — durable object, authenticated approval entries, atomic supersession — remains unowned: it is not gate-house's, because Staff holds no state another layer depends on at runtime (standard §3.4), and not flex-auth's, for the reason above. Raised to gate-house as an engine gap under §5. Task stays `wait`. Final disposition 2026-09-01: cancelled in this workplan after the security layer model assigned the durable approval object, authenticated approvals, storage, and lifecycle to `approval-engine`. This is an ownership transfer, not a claim that the external capability is implemented. flex-auth consumes the result as an input claim and does not store or mutate it. ## Propagate bindings through delegated evaluators ```task id: FLEX-WP-0017-T04 status: done priority: medium state_hub_task_id: "d85089ee-ad8c-502b-ba1b-be4ad23aec46" ``` Populate the same binding in Topaz, relationship, rule, and Keycloak adapter success and fail-closed responses using the shared canonical constructor. ## Consumer handoff and live destructive-action proof ```task id: FLEX-WP-0017-T05 status: cancel priority: high state_hub_task_id: "8c3fc0a2-0855-5ac9-afa5-d03b8b1f0bf9" ``` After T03, secrets-engine validates the canonical object before every privileged production action and proves wrong action/lane/stage/targets, expiry, supersession, outage, insufficient approvals, and duplicate approvers all fail before any OpenBao call. Live destroy stays disabled until that proof. Final disposition 2026-09-01: cancelled in this workplan because enforcement and live destructive-action proof belong to the protected-system consumer, `secrets-engine`. flex-auth's handoff is the canonical request binding, published schema, and fail-closed contract delivered by T01, T02, and T04. This disposition does not enable live destroy or waive the consumer proof. ## Closeout Finished 2026-09-01. flex-auth delivered the execute-time binding, canonical durable-object vocabulary, contract documentation, schema, adapter propagation, and regression coverage. The two remaining tasks were cancelled here after the accepted layer model placed durable approval lifecycle with `approval-engine` and enforcement with `secrets-engine`; those external obligations remain fail-closed prerequisites and are not represented as completed flex-auth work.