AUDIT-WP-0018: answer NK-WP-0056-T02 — hold bounded-executor receipts, propose event mapping
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 994874@bnt-lap001 Assistant-Session: 6017c416-d619-47eb-bd1a-f670a02d7d87
This commit is contained in:
parent
d46e89ee21
commit
1ef939b38a
4 changed files with 258 additions and 0 deletions
|
|
@ -28,6 +28,7 @@
|
||||||
| workplan | AUDIT-WP-0015 | blocked | — | workplans/AUDIT-WP-0015-independent-evidence-returns.md |
|
| workplan | AUDIT-WP-0015 | blocked | — | workplans/AUDIT-WP-0015-independent-evidence-returns.md |
|
||||||
| workplan | AUDIT-WP-0016 | finished | — | workplans/AUDIT-WP-0016-wave2-resource-right-sizing.md |
|
| workplan | AUDIT-WP-0016 | finished | — | workplans/AUDIT-WP-0016-wave2-resource-right-sizing.md |
|
||||||
| workplan | AUDIT-WP-0017 | proposed | — | workplans/AUDIT-WP-0017-production-audit-sources-and-correlation.md |
|
| workplan | AUDIT-WP-0017 | proposed | — | workplans/AUDIT-WP-0017-production-audit-sources-and-correlation.md |
|
||||||
|
| workplan | AUDIT-WP-0018 | proposed | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
|
||||||
| task | ADHOC-2026-09-14-T01 | done | — | workplans/ADHOC-2026-09-14.md |
|
| task | ADHOC-2026-09-14-T01 | done | — | workplans/ADHOC-2026-09-14.md |
|
||||||
| task | ADHOC-2026-09-24-T01 | done | — | workplans/ADHOC-2026-09-24.md |
|
| task | ADHOC-2026-09-24-T01 | done | — | workplans/ADHOC-2026-09-24.md |
|
||||||
| task | ADHOC-2026-10-15-T01 | done | — | workplans/ADHOC-2026-10-15.md |
|
| task | ADHOC-2026-10-15-T01 | done | — | workplans/ADHOC-2026-10-15.md |
|
||||||
|
|
@ -111,6 +112,9 @@
|
||||||
| task | AUDIT-WP-0017-T05 | todo | — | workplans/AUDIT-WP-0017-production-audit-sources-and-correlation.md |
|
| task | AUDIT-WP-0017-T05 | todo | — | workplans/AUDIT-WP-0017-production-audit-sources-and-correlation.md |
|
||||||
| task | AUDIT-WP-0017-T06 | todo | — | workplans/AUDIT-WP-0017-production-audit-sources-and-correlation.md |
|
| task | AUDIT-WP-0017-T06 | todo | — | workplans/AUDIT-WP-0017-production-audit-sources-and-correlation.md |
|
||||||
| task | AUDIT-WP-0017-T07 | wait | — | workplans/AUDIT-WP-0017-production-audit-sources-and-correlation.md |
|
| task | AUDIT-WP-0017-T07 | wait | — | workplans/AUDIT-WP-0017-production-audit-sources-and-correlation.md |
|
||||||
|
| task | AUDIT-WP-0018-T01 | done | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
|
||||||
|
| task | AUDIT-WP-0018-T02 | wait | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
|
||||||
|
| task | AUDIT-WP-0018-T03 | wait | — | workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md |
|
||||||
| intake | AUDIT-IN-0001 | closed | — | intakes/intakes.md |
|
| intake | AUDIT-IN-0001 | closed | — | intakes/intakes.md |
|
||||||
| intake | AUDIT-IN-0002 | closed | — | intakes/intakes.md |
|
| intake | AUDIT-IN-0002 | closed | — | intakes/intakes.md |
|
||||||
| intake | AUDIT-IN-0003 | closed | — | intakes/intakes.md |
|
| intake | AUDIT-IN-0003 | closed | — | intakes/intakes.md |
|
||||||
|
|
|
||||||
165
docs/bounded-executor-receipts.md
Normal file
165
docs/bounded-executor-receipts.md
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
# Bounded-executor execution-attribution receipts — owner answer
|
||||||
|
|
||||||
|
Answers NetKingdom NK-WP-0056-T02 (owner request
|
||||||
|
`net-kingdom/docs/proposals/2026-10-31-nk56-t02-owner-requests.md`, section
|
||||||
|
"audit-core"; hub message `755cbeed-ecbd-46d9-883e-958fbd88378b`). Reviewed
|
||||||
|
2026-10-31 against `net-kingdom/canon/standards/bounded-execution-admission_v0.1.md`
|
||||||
|
section 6 and `playbook-capability-contract_v0.2.md` "Execution Attribution
|
||||||
|
Receipt (proposed)". Work: AUDIT-WP-0018. Canonical receipt validation remains
|
||||||
|
AUDIT-WP-0014-T02.
|
||||||
|
|
||||||
|
This is an owner proposal. It activates nothing: registration, ingress and the
|
||||||
|
sender bearer require the owner deliveries and the approved live change listed
|
||||||
|
at the end.
|
||||||
|
|
||||||
|
## Answer 1 — will audit-core hold the receipts?
|
||||||
|
|
||||||
|
Yes. Audit-core accepts the evidence-holder role for the bounded executor's
|
||||||
|
receipts, as it already did for NK-WP-0040 (`docs/cross-repo-evidence-admission.md`,
|
||||||
|
"Execution-attribution receipts"). What changes is the emitter: no longer a
|
||||||
|
Railiance component but the NetKingdom-written executor, deployed by the new
|
||||||
|
`rapp-*` package, running as `service:nk-bind-executor` in namespace `sso`.
|
||||||
|
|
||||||
|
Holding means what it means for every other sender (`docs/event-envelope.md`):
|
||||||
|
audit-core authenticates the sender, checks source and tenant against the
|
||||||
|
registered identity, stores the event verbatim, and chains it. It records that
|
||||||
|
the executor *said* the run happened this way. It does not decide whether the
|
||||||
|
`decision_refs` authorized the run, and it does not authenticate the initiator
|
||||||
|
chain. That check belongs to the independent verifier in BEA section 6
|
||||||
|
("Verify"), which reads the receipt from audit-core and compares it with the
|
||||||
|
carrier record.
|
||||||
|
|
||||||
|
Until AUDIT-WP-0014-T02 ships canonical schema validation, an accepted receipt
|
||||||
|
is generic JSON custody, not a schema-validated receipt. Audit-core will not
|
||||||
|
describe it otherwise.
|
||||||
|
|
||||||
|
## Answer 2 — one event, or separate events by `run_id`?
|
||||||
|
|
||||||
|
Both, in a fixed shape: **one receipt event per run carries all per-step detail**,
|
||||||
|
plus **one `started` event** emitted when the executor has consumed the approval and
|
||||||
|
before it takes its first step. Do not split steps into separate events.
|
||||||
|
|
||||||
|
Why the steps stay inside the receipt:
|
||||||
|
|
||||||
|
- The envelope stores `data` verbatim as one object (`docs/event-envelope.md`).
|
||||||
|
Per-step outcomes, check results, revert yes/no and budget used are the
|
||||||
|
sender's claims. They belong in `data`, where they read as claims. The stored
|
||||||
|
`outcome` is always `recorded` and `actor` is always `null`
|
||||||
|
(`docs/event-envelope.md`, "What the receiver derives").
|
||||||
|
- One receipt is the retained evidence unit. BEA section 6 says the receipt is
|
||||||
|
"kept by hash". If steps were split into separate events, nobody could tell
|
||||||
|
whether a step event was missing without a declared count. The receipt would
|
||||||
|
then become a reconstruction, not a record.
|
||||||
|
- The validator then sees one document, so `decision_refs`, budget and step
|
||||||
|
list are checked together.
|
||||||
|
|
||||||
|
Why a separate `started` event is still needed: a receipt is emitted at the end
|
||||||
|
of a run. If the executor crashes mid-run after it has consumed its single
|
||||||
|
approval, it emits nothing. Without a second event, a run that partly executed
|
||||||
|
leaves no record in custody. With a `started` event, a `started` that has no
|
||||||
|
matching receipt after the admission window is detectable. Both events share the
|
||||||
|
`run_id`.
|
||||||
|
|
||||||
|
## Event mapping (proposal)
|
||||||
|
|
||||||
|
Both events use the existing eight-field envelope. No envelope change is needed.
|
||||||
|
|
||||||
|
| Envelope field | `started` event | Receipt event |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `type` | `execution.run.started` | `execution.attribution.receipt` |
|
||||||
|
| `id` / `Idempotency-Key` | `run:<run_id>:started` | `run:<run_id>:receipt` |
|
||||||
|
| `source` | `net-kingdom.bounded-executor` (exact; emitter confirms) | same |
|
||||||
|
| `subject` | `admission:<spec.id>` | same |
|
||||||
|
| `tenant` | the admission's tenant, exact string (owner-bound, never inferred from namespace) | same; must equal `data.tenant` |
|
||||||
|
| `correlation_id` | `<run_id>` | `<run_id>` |
|
||||||
|
| `occurred_at` | consume time, offset-aware | `time_interval.end`, offset-aware |
|
||||||
|
| `data` | `run_id`, `admission_id`, `admission_digest`, `approval_ref`, `executing_workload`, `runtime_principal`, `plan_digest`, `steps_planned` | the canonical receipt (below) |
|
||||||
|
|
||||||
|
Each `id` is derived deterministically from `run_id`. A retry is therefore a
|
||||||
|
`200` duplicate. A second, different body under the same id is a `409` conflict
|
||||||
|
(`audit_core/ingestion.py` response table), which exposes reuse of a run id.
|
||||||
|
|
||||||
|
Proposed receipt `data`. The schema belongs to NetKingdom (NK-WP-0040-T02);
|
||||||
|
this lists what audit-core needs to see in it:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"receipt_version": "<canonical schema version>",
|
||||||
|
"run_id": "…",
|
||||||
|
"initiator": {"iss": "…", "sub": "admin"},
|
||||||
|
"executing_workload": "service:nk-bind-executor",
|
||||||
|
"runtime_principal": "system:serviceaccount:sso:<sa>",
|
||||||
|
"tenant": "…",
|
||||||
|
"environment": "…",
|
||||||
|
"source_commit": "<plan.source.commit>",
|
||||||
|
"artifact_digest": "<plan_digest>",
|
||||||
|
"target": "<admission-level target>",
|
||||||
|
"action": "bounded-execution",
|
||||||
|
"decision_refs": [
|
||||||
|
{"kind": "admission", "id": "<spec.id>"},
|
||||||
|
{"kind": "admission_digest", "id": "sha256:…"},
|
||||||
|
{"kind": "approval", "id": "<carrier approval reference>"}
|
||||||
|
],
|
||||||
|
"time_interval": {"start": "…+00:00", "end": "…+00:00", "clock_source": "…"},
|
||||||
|
"steps": [
|
||||||
|
{"index": 1, "operation": "…", "target": "…", "action": "…",
|
||||||
|
"outcome": "succeeded|failed|skipped",
|
||||||
|
"checks": [{"id": "…", "result": "pass|fail|unknown"}]}
|
||||||
|
],
|
||||||
|
"reverted": false,
|
||||||
|
"budget": {"limit": {"…": 0}, "used": {"…": 0}},
|
||||||
|
"outcome": "succeeded|failed|reverted"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The points that fit the existing model and need NetKingdom to decide:
|
||||||
|
|
||||||
|
1. **`target`/`action` are singular in the canonical field list but per-step in
|
||||||
|
BEA section 6.** Proposal: the receipt-level `target` is the admission's
|
||||||
|
target and the receipt-level `action` is `bounded-execution`. Each step's
|
||||||
|
target and action go in `steps[]`. Audit-core needs one answer in the schema.
|
||||||
|
It will not pick one.
|
||||||
|
2. **`decision_refs` must be typed.** The admission id, the admission digest and
|
||||||
|
the approval reference are three different things. An untyped list cannot
|
||||||
|
be checked against the carrier.
|
||||||
|
3. **`delegation` stays absent** when there is none. `time_interval.bound` stays
|
||||||
|
absent when no bound is established. Unknown attribution stays the explicit
|
||||||
|
`unknown`. This is unchanged from `docs/cross-repo-evidence-admission.md`.
|
||||||
|
4. **Check results carry an id and a result only.** They carry no response
|
||||||
|
bodies, tokens, password values or LLDAP/OpenBao payloads.
|
||||||
|
|
||||||
|
## Registration (proposal)
|
||||||
|
|
||||||
|
| Registry field | Proposed value | Reason |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `name` | `nk-bounded-executor` | one identity per executor deployment |
|
||||||
|
| `sources` | `["net-kingdom.bounded-executor"]` | exact, no wildcard |
|
||||||
|
| `tenants` | the exact admission tenant(s) | not `*`; the executor acts for named tenants only |
|
||||||
|
| `may_write` / `may_read` | `true` / `false` | the verifier reads with its own read identity |
|
||||||
|
| `secret_policy` | `reject` | a receipt is kept by hash. If audit-core redacted it silently, the stored body would no longer match the hash the executor computed, so a secret-shaped field must refuse the event (`400 secret_shaped_field`). The executor treats that as a defect |
|
||||||
|
| `evidence_kind` | `load-bearing`, if the `started` + receipt pair is adopted. Otherwise `attributive` with an explicit `completeness_trade` | the pair is what makes a missing receipt detectable |
|
||||||
|
|
||||||
|
Ingress: a new NetworkPolicy in `deploy/networkpolicies.yaml` admitting
|
||||||
|
namespace `sso` and the executor's exact pod labels, following
|
||||||
|
`audit-core-tenant-engine-ingress`. Do not admit the whole `sso` namespace,
|
||||||
|
because Authelia, LLDAP and KeyCape run there too. The bearer is
|
||||||
|
ESO-projected by railiance-platform, like the other in-cluster senders. No token
|
||||||
|
value appears in this repository.
|
||||||
|
|
||||||
|
## What audit-core needs back
|
||||||
|
|
||||||
|
- **NetKingdom (NK-WP-0040-T02 / NK-WP-0056):**
|
||||||
|
- the versioned receipt schema, validator, path and digest, with positive and
|
||||||
|
negative fixtures, covering `steps`, `reverted`, `budget` and the typed
|
||||||
|
`decision_refs`;
|
||||||
|
- an answer to point 1;
|
||||||
|
- the exact `source` and event types;
|
||||||
|
- the tenant binding;
|
||||||
|
- a decision on the `started` event and the emission guarantee.
|
||||||
|
- **The rapp-\* package owner:** the exact pod labels and ServiceAccount, and
|
||||||
|
the image revision that emits.
|
||||||
|
- **railiance-platform:** the ESO projection of the sender bearer
|
||||||
|
(catalog/route via `warden route`), and the attended seed of the registry
|
||||||
|
entry.
|
||||||
|
- **Founder:** the live registry entry and NetworkPolicy rollout are an approved
|
||||||
|
live change. Audit-core prepares them; the founder runs them.
|
||||||
|
|
@ -139,6 +139,10 @@ ordered offset-aware start/end if that is the canonical rule. No independent
|
||||||
local receipt schema is shipped ahead of the owner schema, and generic JSON
|
local receipt schema is shipped ahead of the owner schema, and generic JSON
|
||||||
acceptance is not advertised as receipt-schema validation.
|
acceptance is not advertised as receipt-schema validation.
|
||||||
|
|
||||||
|
NK-WP-0056 names a new emitter, the NetKingdom bounded executor in `sso`. Its
|
||||||
|
mapping (one receipt per run plus a `started` event, correlated by `run_id`) is in
|
||||||
|
`docs/bounded-executor-receipts.md` (AUDIT-WP-0018).
|
||||||
|
|
||||||
## Independent chain-head custody — AUDIT-WP-0015-T01
|
## Independent chain-head custody — AUDIT-WP-0015-T01
|
||||||
|
|
||||||
RESOURCE-WP-0002-T06 is done for encrypted logical database copies. That
|
RESOURCE-WP-0002-T06 is done for encrypted logical database copies. That
|
||||||
|
|
|
||||||
85
workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md
Normal file
85
workplans/AUDIT-WP-0018-bounded-executor-receipt-custody.md
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
---
|
||||||
|
id: AUDIT-WP-0018
|
||||||
|
type: workplan
|
||||||
|
title: "Hold bounded-executor execution-attribution receipts"
|
||||||
|
domain: infotech
|
||||||
|
repo: audit-core
|
||||||
|
status: proposed
|
||||||
|
flavor: implementation
|
||||||
|
owner: audit-core
|
||||||
|
topic_slug: audit-evidence-dependencies
|
||||||
|
created: "2026-10-31"
|
||||||
|
updated: "2026-10-31"
|
||||||
|
related:
|
||||||
|
- NK-WP-0056
|
||||||
|
- NK-WP-0040
|
||||||
|
- AUDIT-WP-0014
|
||||||
|
---
|
||||||
|
|
||||||
|
# AUDIT-WP-0018 — Hold bounded-executor execution-attribution receipts
|
||||||
|
|
||||||
|
NetKingdom's Bounded Execution Admission v0.1 makes a non-human executor
|
||||||
|
(`service:nk-bind-executor`, namespace `sso`) emit one execution-attribution
|
||||||
|
receipt per run. Audit-core holds those receipts. The owner answer and the event
|
||||||
|
mapping are in `docs/bounded-executor-receipts.md`. Canonical schema validation
|
||||||
|
is still AUDIT-WP-0014-T02 and is not duplicated here.
|
||||||
|
|
||||||
|
## Answer the NK-WP-0056-T02 owner request
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: AUDIT-WP-0018-T01
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
Hub message 755cbeed-ecbd-46d9-883e-958fbd88378b. Audit-core accepts custody.
|
||||||
|
Each run produces one receipt event that carries the per-step outcomes, check
|
||||||
|
results, `reverted` and `budget`. Each run also produces an
|
||||||
|
`execution.run.started` event, correlated with the receipt by `run_id`. Event
|
||||||
|
ids are deterministic from `run_id`. The proposed `secret_policy` is `reject`.
|
||||||
|
NetKingdom has to decide how a singular `target`/`action` relates to per-step
|
||||||
|
targets and actions, and has to make `decision_refs` typed.
|
||||||
|
|
||||||
|
## Prepare sender registration and ingress for the executor
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: AUDIT-WP-0018-T02
|
||||||
|
status: wait
|
||||||
|
priority: high
|
||||||
|
blocking_reason: "NetKingdom must confirm source, event types, tenant binding and the started event; the rapp-* package must name pod labels and ServiceAccount; railiance-platform must route the ESO-projected bearer."
|
||||||
|
blocked_on: message-from:net-kingdom
|
||||||
|
```
|
||||||
|
|
||||||
|
Prepare the following:
|
||||||
|
|
||||||
|
- a non-secret registry entry (`nk-bounded-executor`: exact source, exact
|
||||||
|
tenants, write only, `secret_policy: reject`, and either `evidence_kind`
|
||||||
|
`load-bearing` or `attributive` with a `completeness_trade`);
|
||||||
|
- a NetworkPolicy that admits only the executor's pods in `sso`.
|
||||||
|
|
||||||
|
Then test on synthetic fixtures:
|
||||||
|
|
||||||
|
- accept on 202;
|
||||||
|
- a retry returns a 200 duplicate;
|
||||||
|
- a reused `run_id` with a different body is a 409 conflict;
|
||||||
|
- wrong source or tenant is refused;
|
||||||
|
- a secret-shaped step check is refused with 400.
|
||||||
|
|
||||||
|
The live registry seed and the rollout are founder-run.
|
||||||
|
|
||||||
|
## Detect runs that started without a receipt
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: AUDIT-WP-0018-T03
|
||||||
|
status: wait
|
||||||
|
priority: medium
|
||||||
|
blocking_reason: "Depends on NetKingdom adopting the execution.run.started event and stating the admission window bound after which a missing receipt is a finding."
|
||||||
|
blocked_on: message-from:net-kingdom
|
||||||
|
```
|
||||||
|
|
||||||
|
Report every `execution.run.started` that has no
|
||||||
|
`execution.attribution.receipt` with the same `correlation_id` after the
|
||||||
|
declared window. Reuse the existing stream-completeness/cadence surface
|
||||||
|
(`docs/stream-completeness.md`, `docs/cadence-registration.md`) instead of
|
||||||
|
adding a new monitor. The report is a finding for the verifier and the
|
||||||
|
founder. Audit-core does not infer what the run did.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue