repo.work.resolve_decision GH-DEC-2026-002
correlation_id: 75014e3f-ab3f-4038-9e2a-719f2da53abc reason: GH-WP-0002-T03: record the revocation failure mode source: repo-manager Assistant: grok Assistant-Session: 01a04d89-aaa5-7443-945e-b3055cd4b7e4
This commit is contained in:
parent
6a5d2170a4
commit
23cbefcb7b
1 changed files with 132 additions and 2 deletions
|
|
@ -167,7 +167,7 @@ the estate declining to adopt the authority vocabulary.
|
|||
id: GH-DEC-2026-002
|
||||
kind: decision
|
||||
title: Revocation fails closed only when approval-engine's own store is down
|
||||
status: open
|
||||
status: resolved
|
||||
owner: Bernd Worsch
|
||||
repo: gate-house
|
||||
standard: net-kingdom/canon/standards/security-layer-model_v0.7.md
|
||||
|
|
@ -182,9 +182,75 @@ affects:
|
|||
- audit-core
|
||||
- flex-auth
|
||||
created: '2026-08-29T12:50:04.747439Z'
|
||||
updated: '2026-08-29T12:50:04.747439Z'
|
||||
updated: '2026-08-29T12:50:58.873684Z'
|
||||
rationale: 'Approved as GH-WP-0002-T03. Local-outbox fail-closed is the only remaining
|
||||
closed path: if approval-engine cannot insert the outbox row, the revocation does
|
||||
not commit. An audit-core outage must not block a revocation. Synchronous emission
|
||||
inside the mutation is forbidden even though it is atomic. Proceed-with-gap is rejected
|
||||
for load-bearing approval evidence.'
|
||||
decided_by: Bernd Worsch
|
||||
decided_at: '2026-08-29T12:50:58.873684Z'
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
`GH-IN-0001` (from `audit-core`) required that the revocation failure mode be a
|
||||
recorded decision rather than an implementation accident. Both sides are
|
||||
defensible on their own: fail-closed makes an audit dependency into an
|
||||
availability risk on the revocation path; proceed-with-gap needs a detectable
|
||||
marker so the gap is visible rather than silent.
|
||||
|
||||
v0.5's local-outbox rule already narrowed this. With the queue in
|
||||
`approval-engine`'s own store, fail-closed triggers only when that store is
|
||||
down — where the change could not have been recorded anyway — and an
|
||||
`audit-core` outage does not block a revocation. Satisfying atomicity by
|
||||
emitting synchronously to `audit-core` inside the mutation is also atomic, and
|
||||
turns an audit outage into an inability to revoke: the operation least
|
||||
tolerable to block during an incident.
|
||||
|
||||
The statute states the locality rule at §9.4. This record is the decision the
|
||||
workplan still owed, so an implementer cannot pick the other side by accident.
|
||||
|
||||
Wire: `docs/contracts/approval-outbox.md`.
|
||||
|
||||
## Decision
|
||||
|
||||
**If the local outbox insert cannot commit, the revocation does not commit.**
|
||||
The same rule applies to issuance, use, and supersession.
|
||||
|
||||
**An `audit-core` outage MUST NOT block a revocation.** Drain is asynchronous
|
||||
and at-least-once. `audit-core` dedupes on `event_id`.
|
||||
|
||||
**Synchronous emission to `audit-core` inside the state-change transaction is
|
||||
forbidden**, even though it is atomic.
|
||||
|
||||
Proceed-with-gap is rejected for load-bearing approval evidence. A revocation
|
||||
that succeeds while its event is lost is the exact case `AUDIT-IN-0001`
|
||||
conditioned assent on.
|
||||
|
||||
## What this authorizes
|
||||
|
||||
- `approval-engine` failing a revoke (and any other mutation) when its own
|
||||
store cannot insert the outbox row.
|
||||
- Continuing to revoke while `audit-core` is down, with the row draining later.
|
||||
- Treating emit-after-commit, a second non-local queue, or a sync `audit-core`
|
||||
call inside `BEGIN`…`COMMIT` as out of contract.
|
||||
|
||||
## What this does not authorize
|
||||
|
||||
- Claiming the outbox closes adversarial omission. Atomicity prevents crash;
|
||||
cadence and reconciliation detect suppression after the fact (§9.6).
|
||||
- A validity query on `audit-core`.
|
||||
- Any change to another repository's workplans.
|
||||
|
||||
## Reversal
|
||||
|
||||
Revert this record and the outbox contract. The falsifier is operational: if
|
||||
failing closed on the local store removes the ability to revoke during the
|
||||
incidents this engine exists for, the trade needs revisiting — but the
|
||||
alternative is a silent evidence gap on the event an attacker most wants
|
||||
missing, which is worse.
|
||||
|
||||
## GH-DEC-2026-003 — The PEP consumes an approval before the protected side effect
|
||||
|
||||
```yaml
|
||||
|
|
@ -209,3 +275,67 @@ affects:
|
|||
created: '2026-08-29T12:50:06.944092Z'
|
||||
updated: '2026-08-29T12:50:06.944092Z'
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
Statute §16 left open who marks an approval consumed, and at what point
|
||||
relative to the decision. `flex-auth` named three failure modes neither engine
|
||||
closes alone: an ALLOW never consumed; a double consumption by racing callers;
|
||||
consumption after a failed action. `approval-engine` performs the mutation
|
||||
because `access-engine` never mutates, and refused to wire a public consume by
|
||||
guessing the contract. `APPROVAL-WP-0001-T05` and `FLEX-WP-0017-T05` are wait
|
||||
on this record.
|
||||
|
||||
§9.7.3 forbids inferring consumption from a decision record. That stands. The
|
||||
same paragraph currently reads as if the action must precede the consume call.
|
||||
That reading cannot enforce single use: two racing PEPs can both act, and CAS
|
||||
then prevents only the second record.
|
||||
|
||||
## Decision
|
||||
|
||||
**The PEP-shaped consumer consumes, by compare-and-swap, before the protected
|
||||
side effect.** The PDP never mutates. Staff never calls consume.
|
||||
|
||||
**There is no unconsume and no reserve/release.** An approval authorizes one
|
||||
attempt, not one success. A consume followed by a failed action spends the
|
||||
object; retry is a new approval.
|
||||
|
||||
**Same `request_digest` against an already-consumed object is idempotent
|
||||
success** (a retry of one logical request). A different digest is conflict,
|
||||
and the PEP MUST NOT act.
|
||||
|
||||
The three failure modes have owners:
|
||||
|
||||
1. *Allow never consumed* — PEP duty to consume; PDP lifetime bounds the
|
||||
window; a stale ALLOW without a matching `use` is a finding, not a consume.
|
||||
2. *Double consumption* — `approval-engine` CAS; the PEP that receives
|
||||
conflict does not act.
|
||||
3. *Consumed then the action fails* — accepted as the cost of closing (2).
|
||||
|
||||
Protocol: `docs/contracts/approval-consumption.md`. Statute v0.8 will replace
|
||||
the §9.7.3 implication that action precedes the consume call; until then this
|
||||
record governs the blocked implementers.
|
||||
|
||||
## What this authorizes
|
||||
|
||||
- `approval-engine` exposing `POST /v1/approvals/{id}/consume` against this
|
||||
contract, including the digest column the internal CAS does not yet store.
|
||||
- `FLEX-WP-0017-T05` requiring the PEP (secrets-engine, for that task) to
|
||||
consume before the OpenBao call.
|
||||
- Canon `T-06` consume-side replay (a second, different digest against a
|
||||
consumed object) becoming in-scope once the endpoint exists.
|
||||
|
||||
## What this does not authorize
|
||||
|
||||
- The PDP consuming, or inferring consumption from a decision record.
|
||||
- Unconsume, reserve, or any path that returns a consumed object to `approved`.
|
||||
- A consume that is a decision ("may this actor do X").
|
||||
- Any change to another repository's workplans. Work structure stays with the
|
||||
repository doing the work.
|
||||
|
||||
## Reversal
|
||||
|
||||
Revert this record and the consumption contract. The falsifier is operational:
|
||||
if spending an approval on a failed attempt makes the estate unable to
|
||||
complete the actions this object exists for, a reserve/commit protocol can be
|
||||
raised then. Unconsume is not the alternative — it reopens replay.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue