fix(admission): restrict approval audit sender to the platform tenant
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-luna
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-08 17:01:41 +02:00
parent 633f68b81b
commit bbe8c15494
5 changed files with 46 additions and 22 deletions

View file

@ -20,7 +20,7 @@ token appears here, in Git, in State Hub, or in a workplan.
| `may_write` | true |
| `may_read` | false — a source does not gain a read surface by emitting |
| `secret_policy` | `redact` (estate default; see below) |
| `tenants` | `["*"]` — **proposed, needs approval-engine's confirmation** |
| `tenants` | `["tenant:platform"]` — exact platform store tenant |
| Retention | no expiry set; recoverable history is the platform `data.backup` window, 30 days, `measured` |
| Custody class | `operational` — never `archive`, never WORM |
@ -39,25 +39,21 @@ Audit Core stores all four and derives nothing from them. It renders no verdict
on whether an approval is still valid (§9.4), asserted by test in
`tests/test_approval_validity_prohibition.py`.
### The `tenants` value is the one input still owed
### Source return — 2026-09-08
`["*"]` is proposed because approval issuance is not obviously tenant-
partitioned, and the alternative — an explicit tenant list — cannot be invented
here. `audit_core/senders.py` states that omitting a tenant restriction "should
be justified per sender, not adopted by default", so this needs
`approval-engine` to either confirm the wildcard with its reasoning or supply
the list. Tenant identifiers are opaque to Audit Core; their shape is owned by
the IAM Profile and `tenant-engine`.
`approval-engine/docs/audit-source-registration.md` now records the exact
`["tenant:platform"]` scope and explicit `secret_policy: redact`. The tenant
choice follows the already accepted platform tenant decision and the Engine,
production CLI and deployment defaults; no cross-tenant scope is needed.
Both scope manifests now use that exact value. Tests prove that the overlay
removes a stale wildcard tenant/source/read grant and refuses near-miss tenant
spellings while retaining the accepted platform tenant.
### `secret_policy`
`redact` accepts the event and redacts a secret-shaped field, so a legitimate
event is not lost over one field. For a load-bearing source that trade is worth
re-examining: `reject` refuses the event instead, which for approvals converts a
redactable field into a delivery failure the source must retry. `redact` is the
recommendation — losing a revocation record is worse than storing it with one
field redacted — but it is `approval-engine`'s call to make explicitly rather
than inherit.
`redact` retains a legitimate event with a secret-shaped field removed rather
than turning it into a delivery failure. This does not permit credentials in
producer payloads. The scope overlay deliberately does not override
`secret_policy`: the protected sender registry entry must set it explicitly
when platform provisioning occurs, and live admission must verify that value.
## Applied in this repository
@ -80,8 +76,7 @@ older rule's breadth. `user-engine`'s sender and its policy are unchanged.
| Input | Owner | Note |
| --- | --- | --- |
| Token at `approval-engine/approval-engine-audit`, key `audit-token` | OpenBao (`railiance-platform`), routed via `warden route` | Audit Core never holds or transports it |
| `tenants` confirmation | `approval-engine` | see above |
| `secret_policy` confirmation | `approval-engine` | see above |
| Receiver registry custody and explicit `secret_policy: redact` | `railiance-platform` | Link protected registry and sender credential in the first-provision record; no token generated here |
| Applying the manifests to `railiance01` | operator | Audit Core does not apply cluster changes unprompted |
## What Audit Core will and will not claim about this stream