diff --git a/deploy/senders-scope.json b/deploy/senders-scope.json index d5366bc..3a0ea4d 100644 --- a/deploy/senders-scope.json +++ b/deploy/senders-scope.json @@ -16,7 +16,7 @@ "approval-engine" ], "tenants": [ - "*" + "tenant:platform" ], "may_write": true, "may_read": false, diff --git a/deploy/senders-scope.yaml b/deploy/senders-scope.yaml index 56a9aa6..e1a034f 100644 --- a/deploy/senders-scope.yaml +++ b/deploy/senders-scope.yaml @@ -31,7 +31,7 @@ data: { "name": "approval-engine", "sources": ["approval-engine"], - "tenants": ["*"], + "tenants": ["tenant:platform"], "may_write": true, "may_read": false, "evidence_kind": "load-bearing" diff --git a/docs/approval-engine-source-registration.md b/docs/approval-engine-source-registration.md index 1f61288..d0b84c0 100644 --- a/docs/approval-engine-source-registration.md +++ b/docs/approval-engine-source-registration.md @@ -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 diff --git a/tests/test_senders.py b/tests/test_senders.py index 4d74cd1..17188f8 100644 --- a/tests/test_senders.py +++ b/tests/test_senders.py @@ -314,6 +314,26 @@ def test_a_scope_entry_without_a_token_admits_nothing(): assert registry.authenticate("Bearer live").name == "user-engine" +@pytest.mark.parametrize("tenant", ["platform", "tenant:coulomb", "tenant:Platform", "tenant:platform ", "tenant:customer"]) +def test_approval_scope_restricts_a_stale_wildcard_registration(tenant): + registry = SenderRegistry.from_env({ + "AUDIT_CORE_SENDERS": json.dumps([{ + "name": "approval-engine", "tokens": ["fixture-only"], + "sources": ["*"], "tenants": ["*"], + "may_read": True, "secret_policy": "redact", + }]), + "AUDIT_CORE_SENDERS_SCOPE_PATH": str(SCOPE_FILE), + }) + identity = registry.authenticate("Bearer fixture-only") + assert identity.permits_tenant("tenant:platform") + assert not identity.permits_tenant(tenant) + assert identity.permits_source("approval-engine") + assert not identity.permits_source("user-engine") + assert identity.may_write and not identity.may_read + assert identity.secret_policy == "redact" + assert identity.is_load_bearing + + def test_user_engine_evidence_kind_is_not_asserted_on_its_behalf(): """Undeclared means attributive by default, not a claim audit-core made.""" scope = json.loads(SCOPE_FILE.read_text()) diff --git a/workplans/AUDIT-WP-0009-evidence-role-conformance.md b/workplans/AUDIT-WP-0009-evidence-role-conformance.md index 98bd114..0a5d2fe 100644 --- a/workplans/AUDIT-WP-0009-evidence-role-conformance.md +++ b/workplans/AUDIT-WP-0009-evidence-role-conformance.md @@ -264,6 +264,15 @@ approvals is omission, which tamper evidence does not address at all. T04 does. A consumer waiting on T02 before trusting approval evidence would be waiting on the wrong thing. +**2026-09-08 source return:** approval-engine now records the exact existing +store scope `["tenant:platform"]` and explicitly selects `redact` in +`docs/audit-source-registration.md`. This supersedes the two input waits above. +The JSON and ConfigMap overlays are narrowed together. The overlay preserves +secret_policy from the protected registry, so provisioning must set it there. +T09 remains progress for the platform custody record, credential, admitted +manifest application and positive/negative live ingestion evidence. No secret +was created and no production manifest was applied by this source change. + ```task id: AUDIT-WP-0009-T10 status: todo