Commit graph

18 commits

Author SHA1 Message Date
c44306b1b2 feat: bind the destroy gate to approval_binding_digest and pdp_path
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
The vocabulary mapping this path was waiting on is not coming: gate-house
rejected it in GH-DEC-2026-008, because a translation can be confidently
wrong and fails open by accepting a claim approved for a different action.
The stronger option arrived instead, and both halves are enforced here.

flex-auth published binding.approval_binding_digest (FLEX-DEC-2026-007) to
fix the circularity this repo reported: a pdp_digest recorded at issue time
can never equal the request_digest of the request that carries the claim in
its hashed context, so with GH-DEC-2026-008 requiring that equality, destroy
would have failed closed forever on a check no correct record could pass.

- authorization.approval_binding_digest implements the published exclusion
  rule, including Go's context,omitempty behaviour when stripping empties
  the context; digest_material drops an empty context for the same reason.
- validate_decision_envelope recomputes the field rather than trusting it,
  refuses a claim-bearing request whose decision records none, and compares
  the claim's digest from step 1 against it -- never against request_digest,
  which still covers the claim so it stays a sound replay identity.
- validate_approval_claim requires binding.pdp_path true before using
  pdp_digest at all. Path intent is never inferred from a digest that
  happens to be present; pre-schema-v3 approvals carry pdp_path false
  regardless of any digest they hold.

Replay fixtures re-vendored from dd3ce4c. The destroy pins moved a second
and final time; approval_binding_digest did not, which is the point. The
fixture now demonstrates the property instead of asserting it: we rederive
fa07becf... from its own request through our canonical implementation,
proving we hash the same material flex-auth does rather than pinning a
constant we cannot reproduce.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E4tNMAYcSQmZWUE4wqP4ij

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 715726@bnt-lap001
Assistant-Session: 80a42b32-cba6-4b23-8be0-68819b1a6092
2026-09-06 20:39:59 +02:00
64aeec921c docs: record the end-to-end authorization proof in SECRETS-WP-0007-T04
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
2026-09-06 14:56:21 +02:00
6e9c15228c fix: exclude correlation fields from the flex-auth request digest
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Verified the digest join against flex-auth's T03 replay fixtures and found
request_digest was hashing fields docs/canonical-request-digest.md excludes.
The material is tenant, subject, action, resource, context only: id is
correlation, policy_version lives in provenance, caring_context is hashed
separately. This engine included all three when present.

Because the join adopts the served request id, every real production request
would have carried one, so the computed digest would have matched no issued
decision and failed closed against every correct allow. Same unsatisfiable
shape as the removed AUTHORITY constant.

The old pinned constant was computed with the id inside the material, so it
was wrong and its passing proved nothing. Replaced with fixture-driven tests
over two real envelopes (vendored with provenance) plus a structural test
that correlation fields do not move the digest. Both fixtures are needed:
input_claim_digests.context appears only with a non-empty context.

Also stops computing the native claim digest. The claim's binding.action and
binding.target speak approval-engine's vocabulary while ours speaks the
catalog's, and no mapping is published; flex-auth makes no cross-check and
states the correspondence is ours via pdp_digest. A claim recording no
pdp_digest now fails closed naming the missing mapping rather than comparing
two different languages. That mapping is a prerequisite for destroy.

274 tests pass. Production still fails closed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
2026-09-06 14:17:38 +02:00
083bee7333 test: enforce that dry runs never reach the authorization gate
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
flex-auth's published policy package encodes an explicit limit: apply and
apply --dry-run are indistinguishable to a PDP, since both arrive as action
`apply`. What separates them is a property of this repo - the PEP does not
call the gate for a dry run - and flex-auth recorded that as a limit rather
than implying a control they do not have, asking to be told if it stops
holding.

Verified it holds across all five dry-run handlers (apply, revoke, lifecycle
suspend/deactivate/destroy) and locked it with a regression test that fails
with the instruction to notify flex-auth. A paired test proves the hook
under test is actually load-bearing, so the guard cannot pass vacuously.

Also records the FLEX-WP-0021-T01/T02 and GH-DEC-2026-005 outcomes in
SECRETS-WP-0007-T04, including the revisit trigger flex-auth flagged: with
one calling identity the denial ladder has no action_not_granted branch, so
a second identity or a per-lane/stage split is the trigger to add it.

allow_ttl 15m needed no change - normalize_wrap_ttl already caps wrap at 15m.
The policy pin stays unset; publishing is not deploying. 257 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
2026-09-06 08:09:42 +02:00
7b4b9e386e feat: split the validator by owning layer per GH-DEC-2026-005
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
gate-house resolved APPROVAL-IN-0002. Two changes fell to this repo.

1. Split validate_action_authorization. The claim from approval-engine now
   carries the approval fact (issuer, valid_now, consumption, binding digest,
   freshness, reason_code) via approval_claim.validate_approval_claim; the
   flex-auth DecisionEnvelope carries the decision (effect, binding match,
   request digest, lifetime, policy pin) via validate_decision_envelope.
   ActionAuthorization is deferred and never ratified (FLEX-DEC-2026-006) and
   cannot be served from a step-1 call; nothing validates it now.

2. Dropped AUTHORITY = "state-hub" and the provenance.authority requirement.
   State Hub is a read model with no runtime approval authority, so the check
   failed closed against every correctly issued record. flex-auth traced the
   constant to their own fixture and fixed it at source.

Two consequences recorded rather than buried: there are now two distinct
digests over the same action (approval-engine native over
{action,actor,principal,purpose,target}, and the flex-auth CheckRequest
digest) which are never compared to each other; and the distinct-approver
threshold is no longer checked here, since the claim exposes no approver
entries and approval-engine folds it into valid_now.

The canonical request digest is unchanged and its contract test is preserved
verbatim. Production still fails closed. 251 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
2026-09-06 08:02:01 +02:00
fd369d4e21 docs: record claim-envelope divergence and the authority defect
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
approval-engine (APPROVAL-IN-0002) established that ActionAuthorization is a
proposed, unratified object and that the claim body is an approval-claim
envelope instead. The join in 627810b therefore validates a shape that may
never be served. Holding all rework until gate-house rules on option A.

Also records a confirmed defect they caught independently: authorization.py
pins AUTHORITY = "state-hub" and enforces it unconditionally, contradicting
flex-auth's position that State Hub is not the runtime approval authority.
Left unpatched deliberately - under option A that check moves to the claim
issuer, so patching now would be a second guess.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
2026-09-06 01:29:13 +02:00
627810b478 feat: implement the PIP claim + validate authorization join
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
resolve_consume_binding was a `return None` stub, so protocol step 1 of
docs/approval-consumption.md (GET /v1/approvals/{id}/claim) and the
validation join never existed. validate_action_authorization had no caller
in src/ at all - it was reachable only from tests. Production fail-closed
was correct, but for an undocumented second reason, and WP-0007-T04's
"what remains is not local engine work" was wrong.

The join now reproduces the exact CheckRequest via build_action_request,
fetches the durable ActionAuthorization, and validates request binding,
digest, validity, authority, policy pin, and distinct-approver threshold
before offering a consume binding. _require_lane_approval threads the exact
field set for provision/rotate/verify/exec so the digest covers the real
proposed action.

Deliberate choices:
- The approval-engine object id is never inferred from a State Hub decision
  UUID; flex-auth stated GET /decisions/{uuid} is not the durable object.
- No default policy pin. flex-auth stated secrets-engine.lifecycle/v1 is
  example vocabulary, not a published package.
- A half-configured join raises rather than returning None, so a partial
  deployment cannot be mistaken for an unconfigured one.

Behavior is unchanged today: every new input is absent by default, so
production still fails closed and plan/--dry-run still work. 234 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
2026-09-06 01:01:55 +02:00
85d4548035 Revoke leases by operator-held lease id
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
session revoke accepts exactly one of --accessor-file or --lease-id-file.
Lease ids are read from a mode-0600 out-of-repo file and fingerprinted in
evidence; the handle is never printed. Production remains fail-closed.

Assistant: grok
Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
2026-09-02 10:08:33 +02:00
3abee434df Add named engine auth and accessor-file session revoke
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Select service-jwt, bootstrap, or env exclusively: JWT login uses a JSON
file, self-revokes, and never falls back to bootstrap or BAO_TOKEN. The
platform JWT mount/role is still unpublished, so auto keeps named
bootstrap/env providers.

session revoke --accessor-file revokes an already-issued token with
fingerprint-only evidence. Production remains fail-closed.

Assistant: grok
Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
2026-09-02 01:24:08 +02:00
4b4d556d62 Implement GH-DEC-2026-003 consume-before-OpenBao PEP gate
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Every live privileged production handler CAS-consumes through
approval-engine before OpenBao. Conflict, unavailability, or a missing
binding fail closed. Live production remains disabled until the durable
decision record is served.

Record kings-guard assent on the secret-use evidence contract.

Assistant: grok
Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
2026-09-02 01:06:50 +02:00
6f8b7bade5 Retag SECRETS-WP-0007 T03-T06 to match shipped work
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
T03, T05, and T06 are done: lifecycle split, session self-revoke, and
verification/evidence acceptance are in the CLI. T04 waits on the durable
ActionAuthorization serving path rather than looking like local progress.
Known-accessor operator command is residual H4a, not unfinished T03.

Assistant: grok
Assistant-Session: 01a04cea-cb33-7c63-bad7-c1b0f9f0076b
2026-08-29 14:36:38 +02:00
9093b9ebba chore: commit work in progress before registrar reconciliation
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Refs CUST-WP-0068

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-25 17:51:13 +02:00
01b45ab8b3 Record flex-auth digest contract fix
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 14:38:51 +02:00
70371649af Harden production authorization and service auth
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 14:15:42 +02:00
f579f3761c Record privileged action failure evidence
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 12:58:12 +02:00
c4504c6de9 Add value-safe verification and audit reporting
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 12:33:38 +02:00
3a1bd4f1c8 Harden secret provisioning and lifecycle controls
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 12:05:58 +02:00
0617923ff1 docs: plan production lifecycle hardening
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 11:27:10 +02:00