Heartbeat per event class, not per source
audit-core's completeness contract landed, and our single per-source beat is the shape it rules inadequate: it is discharged by whichever class is busy, so a revocation stream that has gone silent looks identical to a quiet one — and revocation is the only silence here that matters. Emit one nothing-to-report assertion per declared class, all four in one transaction so a partial emission cannot report some classes healthy and others stalled. Carry type audit-core.heartbeat with class and assertion on data. Pin that the first beat goes out at startup rather than an interval later, since a declared-but-never-sent class is their no_heartbeat_since_registration finding and not a skip. Declare heartbeat_classes and the reconciliation surface in the source registration, including the residual neither control covers: a compromised emitter suppresses the event and its own heartbeat together. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HyybaE7DUXrWYrhbnESCTe Assistant: claude-code Assistant-Model: opus Assistant-Process: 1275879@bnt-lap001 Assistant-Session: eb464208-f821-41b2-bc5a-a6c33d92a8ad
This commit is contained in:
parent
c8f85c6d76
commit
bfb1e66646
6 changed files with 213 additions and 18 deletions
|
|
@ -513,6 +513,32 @@ the same tenant. This closes those source-input waits, not live registration.
|
|||
Platform still owes linked receiver/sender custody and the protected token;
|
||||
T04 stays wait for admission and live drain/reconciliation evidence.
|
||||
|
||||
2026-09-10: audit-core landed the detection half (`AUDIT-WP-0009` T04/T06/T07
|
||||
at `b098fb1`, contract in their `docs/stream-completeness.md`), so the sender
|
||||
side is no longer describing a cadence that does not operate. Implemented the
|
||||
per-class heartbeat contract: `HEARTBEAT_CLASSES` covers `issuance`, `use`,
|
||||
`supersession` and `revocation`, each asserting `nothing-to-report` for itself,
|
||||
all four written in one transaction. The previous single per-source beat was
|
||||
exactly the shape audit-core rules inadequate — it is discharged by whichever
|
||||
class is busy, so a silent `revocation` stream hides behind `issuance`, and
|
||||
`revocation` is the only silence here that matters. Heartbeats now carry
|
||||
`type: audit-core.heartbeat` with `class` and `assertion` on `data` rather than
|
||||
inside our details object. The first beat is emitted at startup, not one
|
||||
interval later, because a declared-but-never-sent class is their
|
||||
`no_heartbeat_since_registration` finding rather than a skip — `heartbeat_due`
|
||||
already returned true on an empty outbox and that is now pinned by test.
|
||||
`docs/audit-source-registration.md` declares `heartbeat_classes` at 172800s for
|
||||
all four (twice the deployed 86400s default, so one missed cycle is not a
|
||||
finding and two are), records the reconciliation surface and both bounds, and
|
||||
restates the residual audit-core stated on every response: counts and
|
||||
heartbeats cover loss, outage, drain failure and accident, and neither covers
|
||||
this engine lying about itself, since a compromised emitter suppresses the
|
||||
event and its own heartbeat together. Tests: per-class emission including
|
||||
`revocation`, atomicity under outbox failure, the envelope placement, and that
|
||||
an ordinary event gains no heartbeat fields. 155 pass. T04 stays `wait`: live
|
||||
drain still needs the sender registration and credential admission, and the
|
||||
`heartbeat_classes` values above are a request until audit-core accepts them.
|
||||
|
||||
## Prove one live PEP consumption path
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue