AUDIT-WP-0018-T03: report bounded-executor runs that started without a receipt
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 3s

A sender may declare closing_classes; nk-bounded-executor declares
execution.run.started closed by execution.attribution.receipt within
7200s. GET /v1/stream-findings reports opening_without_closing past the
bound, counted from the receiver's accepted_at plus the heartbeat grace
allowance; a per-run data.window.not_after tightens but never extends.
Identifiers and timing only. The overlay may add or shorten, never
remove, rebind or lengthen. Both backends: 403 passed (1 unrelated
live-catalog env failure).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 482098@bnt-lap001
Assistant-Session: 9643c95a-a398-4f4a-b56a-6119be4e845e
This commit is contained in:
tegwick 2026-11-03 22:04:14 +01:00
parent 9fb167871b
commit 2b8c9097ad
13 changed files with 654 additions and 6 deletions

View file

@ -206,3 +206,24 @@ Remaining work:
and the 202 fix.
- **railiance-platform:** custody of the sender bearer (ESO projection to
`sso`).
## Status 2026-11-03 — T03 detection implemented
A `started` event that has no receipt is reported on `GET /v1/stream-findings`
as `opening_without_closing` (see `docs/stream-completeness.md`, "Opening
events that never close"). The pair is declared on `nk-bounded-executor` in
`deploy/senders-scope.{json,yaml}` with a 7200 s bound, which matches the pilot
windows. The finding appears 3 h after acceptance at the latest, because the
heartbeat grace allowance is added.
The executor's `started` body (net-kingdom `bounded-executor/executor.py`)
does not yet carry the admission window. If NetKingdom adds
`data.window.not_after`, each run is held to its own window wherever that is
earlier than the declared bound. Without it, admissions longer than 2 h rely
on the declared ceiling.
The executor's emitter now accepts 202 (accepted) and 200 (duplicate)
(`clients.AuditCore.emit`), which closes the defect recorded on 2026-10-31.
The new detection runs once the ConfigMap is applied with a receiver image
built from this commit or later. Image `sha256:304cfb83` predates it.

View file

@ -180,3 +180,48 @@ schema is identical). The result, stated at no more than it is:
onto audit-core's evaluation by itself: a source needs `heartbeat_classes` on
its registration too, and the two can drift apart. That is fixable on
either side and has not been fixed on either.
## Opening events that never close (AUDIT-WP-0018-T03)
Some streams come in pairs. The bounded executor emits
`execution.run.started` once it has consumed its single approval, and
`execution.attribution.receipt` when the run ends. A crash in between leaves
only the start in custody, and neither heartbeats nor rate counts can see that.
A sender's registration may therefore declare `closing_classes`:
```json
"closing_classes": {
"execution.run.started": {
"closed_by": "execution.attribution.receipt",
"within_seconds": 7200
}
}
```
`GET /v1/stream-findings` then carries an `opening_without_closing` finding
for every opening event that has no closing event with the same
`correlation_id` from the same source by its deadline.
- **Clock.** The deadline counts from the receiver's own `accepted_at`, never
from a source-supplied time.
- **Bound.** The deadline is the declared `within_seconds`. If the opening event
carries `data.window.not_after`, the earlier of the two applies (`bound` names
which one). A per-run window can tighten the declaration but never extend it.
- **Grace.** The same 1.5× allowance as heartbeats is added
(`within_seconds × 0.5`), so a receipt accepted moments late does not flap
the finding. For the executor that means a finding at 3 h at the latest.
- **Lookback.** Opening events accepted in the last 30 days, the operational
custody window, are examined.
- **No correlation id.** An opening event without a `correlation_id` can never
be closed and is always reported.
- **Disclosure.** A finding carries identifiers and timing only: event id,
correlation id, accepted time, deadline. No payload is disclosed.
- **Overlay.** The scope overlay may add a pair or shorten its bound. It may
never remove a pair, rebind its closing class, or lengthen the bound.
The finding's `means` says what it does not prove. The run may have partly
executed, failed before emitting, or had its receipt lost or suppressed.
Audit-core does not infer which, and does not infer what the run did. It is a
finding for the verifier and the founder. As with heartbeats, a compromised
emitter that suppresses its start and its receipt together is not covered.