AUDIT-WP-0013-T06: independent full-scope readers may count any source (NK-WP-0035-T04)
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 4s

GET /v1/reconciliation now admits may_read + tenants ["*"] identities for
any source, marked basis: independent_reader. Scoped readers get 403
full_tenant_scope_required; writers keep 403 source_not_allowed. Counts only.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 308701@bnt-lap001
Assistant-Session: 5548e447-444c-4dcc-bed1-05c16eebfe56
This commit is contained in:
tegwick 2026-10-28 09:49:10 +01:00
parent 64c0fa5285
commit d46e89ee21
6 changed files with 238 additions and 9 deletions

View file

@ -8,7 +8,7 @@ status: blocked
owner: codex
topic_slug: audit-evidence-dependencies
created: "2026-10-04"
updated: "2026-10-15"
updated: "2026-10-28"
state_hub_workstream_id: "9e400de2-6ba1-520e-9b25-94aa2470e528"
---
@ -154,3 +154,50 @@ capped and cannot serve as complete export. Operational repeated ledger custody,
per-pin source cadence/runtime adoption and independent observation still wait.
The canonical sibling-owner fixture check passed1, resolving the isolated
checkout's expected skip without modifying foreign source.
## Sanction independent cross-source reconciliation counts
```task
id: AUDIT-WP-0013-T06
status: done
priority: high
```
NK-WP-0035-T04 (State Hub message 6ed5cafc, receipt
net-kingdom docs/evidence/2026-10-15-cadence-adoption/receiver-readback-20261023.json):
the operator reader is registered for source `user-engine`, so
`GET /v1/reconciliation?source=qonto-assistant` returned 403 `source_not_allowed`
while exact-id reads succeeded 3/3.
Decided: option (a). `GET /v1/reconciliation` admits a second caller class, an
independent reader holding `may_read: true` and `tenants: ["*"]`, for any
source. Such a reader may already read every event by id or correlation, so
per-class counts disclose strictly less. A tenant-scoped reader gets 403
`full_tenant_scope_required` (counting a whole window enumerates existence
beyond a tenant-filtered id lookup); a writer without `may_read` still gets 403
`source_not_allowed`. Own-source behaviour and response shape are unchanged.
Independent answers carry `basis: "independent_reader"` so a receipt cannot be
mistaken for the source's own §9.6 reconciliation. No new role, endpoint or
registry field: the existing operator identity is the sanctioned reader.
Response: `source`, `tenant` (null if omitted), `since`, `until` (normalized,
half-open on `accepted_at`), `counts` `[{class, count}]`, optional
`stream_instance_id`, `basis`, `means`. Counts only; sequence and lag are not
aggregated and stay with the exact-id reads (`accepted_at`, `observed_at`,
`details.data`). Source: audit_core/ingestion.py `_reconciliation`; tests:
tests/test_independent_reconciliation.py (7). Runbook and section-4 docs updated.
```task
id: AUDIT-WP-0013-T07
status: wait
priority: high
blocking_reason: "Founder-run: build/publish an image from the T06 commit, pin its digest in deploy/audit-core.yaml, roll out, then NetKingdom reruns its readback."
blocked_on: founder
```
No registry or Secret change is expected: the operator identity already reads
by id across tenants. Value-safe precheck with the operator token: `GET /v1/stats`
returns 200 only for a full-scope reader (403 `full_tenant_scope_required`
otherwise, which would need a registry scope change instead). The deployed
b479801b receiver predates T06 and will keep returning `source_not_allowed`
until the new image runs.