diff --git a/workplans/SECRETS-WP-0007-production-lifecycle-hardening.md b/workplans/SECRETS-WP-0007-production-lifecycle-hardening.md index 5ea11af..195738c 100644 --- a/workplans/SECRETS-WP-0007-production-lifecycle-hardening.md +++ b/workplans/SECRETS-WP-0007-production-lifecycle-hardening.md @@ -453,6 +453,42 @@ class we had just been warning about. one package covering all twelve actions. Do not build per-action pin configuration for a distinction that does not exist. +Brought into practice 2026-09-06. The chain is complete in code and proven end +to end; what remains is deployment only. + +- Step 2 is implemented (`decision_check.check_decision`, POST `/v1/check`) and + `approval_consume.authorize_action` coordinates steps 1 and 2. Both build the + same CheckRequest through one `_expected_request`, since two descriptions of + the proposed action cannot produce corresponding digests. +- `apply_unreachable_engine_stance` now takes `authorized=`. The published map + defines `fail_closed` as no protected side effect *without* a durable + access-engine decision record, so holding a validated one means the engine was + reachable and the residue does not apply. This is not a stance bypass: both + steps must have succeeded, CAS consume still precedes OpenBao, and with + nothing configured `authorize_action` returns `None` and production fails + closed exactly as before. Verified by CLI, not only by unit test. +- `tests/test_integration_authorization.py` drives the real gate through + claim -> check -> consume -> OpenBao against a live throwaway OpenBao, and + asserts PIP-before-PDP ordering, consume as the last step before the backend, + and that an unreachable PDP, a denied decision, an invalid claim, a missing + `pdp_digest`, a consume conflict, and an action mismatch each stop before the + backend. The stub serves transport and sequencing only; wire contracts stay + pinned to flex-auth's real fixtures. +- The end-to-end run found a third instance of the cross-vocabulary defect: a + leftover comparison of the claim's `binding.action` against ours. Real claims + say `secrets.kv.destroy` where we say `destroy`, so it would have failed + against every one of them. Removed; the tie is `pdp_digest`. Worth noting the + pattern: unit tests with self-consistent fakes hid all three, and each was + found only by a real artifact or a real chain. + +Remaining to go live is configuration and deployment, not engine work: +`SECRETS_ENGINE_PDP_URL`/`_PDP_TOKEN_FILE` (awaiting the `flex-auth-secrets-engine` +pin, `FLEX-WP-0021-T04`/`T05`), `SECRETS_ENGINE_APPROVAL_URL`/`_TOKEN_FILE` +(awaiting `APPROVAL-WP-0002-T03`), the policy pin (published but not to be set +until T05), a KeyCape RS256 credential in place of the static Bearer token, and +`approval.authorization_id` on each lane. Destroy additionally needs the +vocabulary mapping or a `pdp_digest` guarantee. + Define and enforce the decision contract needed by production commands. A resolved approval must bind at least: