AUDIT-WP-0009-T11 — register informed-decision, and answer GH-DEC-2026-014
informed-decision is the browser-facing approver surface; GH-DEC-2026-012 limit 3 makes its evidence copy the one that must reach audit-core independently of the emitter, because there the actor being audited and the evidence source are the same component. Registration accepted on every proposed field — exact source, ["tenant:platform"], write true, read false, load-bearing, secret_policy redact. Prepared and inert: the scope overlay applies only to a sender the Secret already carries, asserted by test rather than by reading. Ingress ANDs namespace and pod label in one peer, following approval-engine rather than user-engine's older breadth. Gate House asked whether the record shape can carry a source-held-content declaration with a retrieval expectation, and asked for a straight answer rather than a rule the storage cannot meet. Both halves, which must travel together: It CAN carry the declaration. data is stored verbatim into details.data and hash-chained, so content_exists and custody need no schema change and become as tamper-evident as the commitment they accompany. It CANNOT detect non-production. audit-core performs no retrieval and its egress permits Postgres and DNS only. Detection happens at retrieval, by the reviewer; the stored declaration is what turns a blank into a failure attributable to the named custodian. Residual stated rather than left to be found: a custodian that never held the content can emit a false content_exists. audit-core validates the declaration's shape, never its truth — the same class as omission at source, and not closed by the chain, by attestation, or by T04/T06. A test asserts no egress to the emitter exists, because that claim silently stops being true if one appears. Cadence: reconciliation plus heartbeat is right for a mixed-volume source, with both scoped per class rather than per source — a per-source heartbeat is satisfied by the high-volume presentation stream and says nothing about a quiet month of dispositions. Bound: a compromised emitter suppresses the event and its own count together. Also recorded: commitment-only satisfies non-alteration and never reconstructability, in this repo's documents as in theirs; and tenant provenance under GH-DEC-2026-013 lands in the registration record, not the envelope, since audit-core checks a value the credential may write rather than resolving an identity claim. No secret was created and no production manifest applied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nb7Q6ZmXppNDkTWytfYqfv Assistant: claude-code Assistant-Model: opus Assistant-Process: 2069992@bnt-lap001 Assistant-Session: 167dd7f8-2a25-4be1-aa46-3b6f1a5f94c6
This commit is contained in:
parent
565e7e38d4
commit
c4016a70d5
8 changed files with 486 additions and 4 deletions
|
|
@ -93,6 +93,36 @@ spec:
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: audit-core-informed-decision-ingress
|
||||
namespace: audit-core
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: audit-core
|
||||
policyTypes: [Ingress]
|
||||
ingress:
|
||||
# AUDIT-WP-0009-T11 / AUDIT-IN-0003. Load-bearing presentation evidence
|
||||
# under GH-DEC-2026-012 limit 3 and GH-DEC-2026-014. Both selectors belong
|
||||
# to one peer and are therefore ANDed, following the approval-engine rule
|
||||
# rather than user-engine's older namespace-only breadth.
|
||||
#
|
||||
# Note what this rule does NOT create: no egress from audit-core to
|
||||
# informed-decision. The commitment-only record's custody declaration is
|
||||
# carried, never dereferenced from here — audit-core makes no retrieval
|
||||
# call, and the egress policy below is the proof.
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: informed-decision
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: informed-decision
|
||||
ports:
|
||||
- {protocol: TCP, port: 8080}
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: audit-core-operator-ingress
|
||||
namespace: audit-core
|
||||
|
|
|
|||
|
|
@ -21,5 +21,17 @@
|
|||
"may_write": true,
|
||||
"may_read": false,
|
||||
"evidence_kind": "load-bearing"
|
||||
},
|
||||
{
|
||||
"name": "informed-decision",
|
||||
"sources": [
|
||||
"informed-decision"
|
||||
],
|
||||
"tenants": [
|
||||
"tenant:platform"
|
||||
],
|
||||
"may_write": true,
|
||||
"may_read": false,
|
||||
"evidence_kind": "load-bearing"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -23,15 +23,35 @@ data:
|
|||
[
|
||||
{
|
||||
"name": "user-engine",
|
||||
"sources": ["user-engine"],
|
||||
"tenants": ["*"],
|
||||
"sources": [
|
||||
"user-engine"
|
||||
],
|
||||
"tenants": [
|
||||
"*"
|
||||
],
|
||||
"may_write": true,
|
||||
"may_read": false
|
||||
},
|
||||
{
|
||||
"name": "approval-engine",
|
||||
"sources": ["approval-engine"],
|
||||
"tenants": ["tenant:platform"],
|
||||
"sources": [
|
||||
"approval-engine"
|
||||
],
|
||||
"tenants": [
|
||||
"tenant:platform"
|
||||
],
|
||||
"may_write": true,
|
||||
"may_read": false,
|
||||
"evidence_kind": "load-bearing"
|
||||
},
|
||||
{
|
||||
"name": "informed-decision",
|
||||
"sources": [
|
||||
"informed-decision"
|
||||
],
|
||||
"tenants": [
|
||||
"tenant:platform"
|
||||
],
|
||||
"may_write": true,
|
||||
"may_read": false,
|
||||
"evidence_kind": "load-bearing"
|
||||
|
|
|
|||
198
docs/informed-decision-source-registration.md
Normal file
198
docs/informed-decision-source-registration.md
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
# informed-decision source registration
|
||||
|
||||
`AUDIT-WP-0009-T11` · intake `AUDIT-IN-0003` · statute §9.6 · rulings
|
||||
`GH-DEC-2026-012` (limit 3), `GH-DEC-2026-014`, `GH-DEC-2026-013`
|
||||
|
||||
The registration inputs for `informed-decision` as a distinct audit source: the
|
||||
browser-facing approver surface that `approval-engine` deliberately does not
|
||||
contain. Onboarded under `INTENT.md` principle 4 — declared ownership,
|
||||
retention, access, export and evidence policy, not merely events arriving.
|
||||
|
||||
This file is the owner-side record. Everything in it is non-secret. No sender
|
||||
token appears here, in Git, in State Hub, or in a workplan.
|
||||
|
||||
## Declaration
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Sender name | `informed-decision` |
|
||||
| Permitted `source` | `informed-decision` (exact; no wildcard) |
|
||||
| `evidence_kind` | **`load-bearing`** (§9.6 — GH-DEC-2026-012 limit 3 makes the presentation record the independent evidence of a binding act) |
|
||||
| `completeness_trade` | none, and none is permitted — §9.6 requires emission atomicity of a load-bearing source |
|
||||
| `may_write` | true |
|
||||
| `may_read` | false — a source does not gain a read surface by emitting. See the reconciliation note below, which is where the temptation to grant one will come from |
|
||||
| `secret_policy` | `redact` — accepted as proposed |
|
||||
| `tenants` | `["tenant:platform"]` — accepted as proposed, with the provenance obligation below |
|
||||
| Retention | no expiry set; recoverable history is the platform `data.backup` window, 30 days, `measured` |
|
||||
| Custody class | `operational` — never `archive`, never WORM |
|
||||
|
||||
Accepted as proposed on every field. The proposal was correct; the substance of
|
||||
this record is in what follows it.
|
||||
|
||||
## Event classes — one source, not two
|
||||
|
||||
Asked directly: is commitment-only content one event class or two, and does it
|
||||
belong in the same class as other evidence at all.
|
||||
|
||||
**One source, distinct `type` values per class.** This is the same shape
|
||||
`approval-engine` carries for its four §9.4 classes: audit-core stores classes
|
||||
as actions on one source and derives nothing from them.
|
||||
|
||||
| Class | Volume | Why it matters |
|
||||
| --- | --- | --- |
|
||||
| presentation | high — one per render | Carries the commitment. The only class carrying a custody declaration |
|
||||
| disposition | low | **security-relevant** — the binding verb |
|
||||
| stance application | low | **security-relevant** — what was applied to the act |
|
||||
|
||||
A separate sender for the commitment-bearing class was considered and refused.
|
||||
Two senders would mean two credentials, two tenant scopes and two ingress rules
|
||||
for one component whose defining property is that the actor and the evidence
|
||||
source are the same — splitting them makes that single residual look like two
|
||||
smaller ones. The distinction that matters is per class, and `type` carries it.
|
||||
|
||||
Commitment-only content does belong in the same store as other evidence,
|
||||
because the custody guarantee it receives is the same one and no stronger. What
|
||||
differs is what may be claimed of it, and that is stated below rather than
|
||||
encoded in a separate class.
|
||||
|
||||
## GH-DEC-2026-014 limit 3: can this fabric carry it?
|
||||
|
||||
Gate House asked whether a source-held-content declaration with a retrieval
|
||||
expectation is a thing the record shape can carry today, and asked for a
|
||||
straight answer rather than a rule nobody can meet.
|
||||
|
||||
**It can carry the declaration. It cannot itself detect non-production.** Both
|
||||
halves are load-bearing; neither should be read without the other.
|
||||
|
||||
**What is carried.** `data` is an arbitrary structured object stored verbatim,
|
||||
normalized into the envelope's `details.data`, appended under the trigger and
|
||||
linked into the hash chain like every other field. `content_exists` and
|
||||
`custody` therefore need no schema change, no new column and no new surface.
|
||||
Once stored, the chain detects any later alteration or truncation of them
|
||||
exactly as it does for the commitment digest itself. So the declaration is as
|
||||
tamper-evident as the thing it accompanies, which is the property the condition
|
||||
needs — a custodian cannot quietly retract the assertion that content existed.
|
||||
|
||||
**What is not delivered, and must not be inferred.** audit-core performs no
|
||||
retrieval. It holds no client for `informed-decision`, and its egress policy
|
||||
permits Postgres and DNS and nothing else (`deploy/networkpolicies.yaml`) — the
|
||||
new ingress rule adds no egress. So audit-core never learns whether a custodian
|
||||
would in fact produce the content, and raises no finding when it would not.
|
||||
Non-production becomes detectable **at retrieval, by the reviewer**, and the
|
||||
stored declaration is what makes it a finding rather than a blank: the reviewer
|
||||
holds a chained, timestamped statement that content existed and where custody
|
||||
sat, so failure to produce is attributable to the named custodian. That is the
|
||||
condition met in the form audit-core can honestly meet it.
|
||||
|
||||
**The residual, stated rather than left to be found.** A custodian that never
|
||||
held the content can emit a false `content_exists`. audit-core validates the
|
||||
declaration's shape, never its truth. This is the same class of defect as
|
||||
omission at source — the archive cannot retrofit a property the boundary did
|
||||
not have — and it is not closed by the chain, by attestation, or by T04/T06. It
|
||||
narrows to this: the declaration converts an unattributable absence into an
|
||||
attributable false statement, which is strictly better and is not the same as
|
||||
proof.
|
||||
|
||||
**One design consequence for the emitter.** `secret_policy: redact` scans
|
||||
`data`. If the `custody` locator is ever secret-shaped — a token in a URL, a
|
||||
path that trips the scanner — it will be redacted out and the declaration will
|
||||
reach the store without its pointer. The redaction is recorded in
|
||||
`details.redaction.paths`, so this fails visibly rather than silently, but the
|
||||
locator should be a stable non-secret identifier resolved by the custodian, not
|
||||
a credentialed URL. Under commitment-only the exposure is small by
|
||||
construction, as informed-decision observed; the custody field is the one place
|
||||
that stops being true.
|
||||
|
||||
## Cadence: the shape for a mixed-volume source
|
||||
|
||||
Asked whether reconciliation per class as primary, plus heartbeat for the
|
||||
low-volume classes, is right. **Yes, with one refinement and one bound.**
|
||||
|
||||
**Refinement — both are per class, not per source.** A per-source heartbeat
|
||||
from a component emitting one presentation per render is nearly worthless: it
|
||||
is satisfied by the high-volume class and says nothing about a quiet month of
|
||||
dispositions. Heartbeat must be scoped to the low-volume classes individually,
|
||||
carrying a *nothing to report* assertion for that class. Likewise reconciliation
|
||||
counts must be keyed `(source, class, window)`, which is the shape
|
||||
`AUDIT-WP-0009-T06` is being built to.
|
||||
|
||||
The high-volume class needs reconciliation too, and rate alone is not a
|
||||
substitute for it. Rate monitoring detects a stream stopping; it does not detect
|
||||
a stream missing the particular renders that mattered.
|
||||
|
||||
**The bound, which matters more here than for approval-engine.** Reconciliation
|
||||
compares audit-core's counts against counts the source computes from its own
|
||||
state. Where the threat is a compromised emitter — and GH-DEC-2026-012 limit 3
|
||||
exists precisely because here the actor and the evidence source are one
|
||||
component — a compromised `informed-decision` suppresses the event and its own
|
||||
count together, and reconciliation agrees. Heartbeat inherits the same weakness:
|
||||
a compromised source emits a truthful-looking *nothing to report*.
|
||||
|
||||
So reconciliation and heartbeat cover loss, accident, outage and drain failure,
|
||||
which is most of what actually goes wrong. They do not cover the residual
|
||||
informed-decision already declares and should not be described as covering it.
|
||||
Nothing audit-core can build closes that; it needs an observer independent of
|
||||
the emitter, which is out of scope here (§16).
|
||||
|
||||
**Sequencing.** T04 and T06 are open. informed-decision may declare this cadence
|
||||
now and must not describe it as operating until they land — which is exactly
|
||||
what the intake proposed, and is the right handling.
|
||||
|
||||
## Tenant provenance
|
||||
|
||||
`tenant:platform` is accepted. informed-decision notes that under
|
||||
`GH-DEC-2026-013` the value arrives by the declared-gap route
|
||||
(registration-bound rather than directory-sourced) and asks whether the
|
||||
registration or envelope inherits the obligation to record which route it came
|
||||
by.
|
||||
|
||||
**Not in this envelope, and here is why.** audit-core's `tenant` is not an
|
||||
asserted identity claim it resolves; it is a value the credential is *permitted
|
||||
to write*, checked by exact string equality against this registration. The
|
||||
provenance question — how a principal came to hold `tenant:platform` — is a
|
||||
property of the token issuer's claim, and recording a route in the audit event
|
||||
would be audit-core restating something it did not observe. That is the same
|
||||
error as claiming an event occurred.
|
||||
|
||||
Where it does land is here, in the registration record, which is the document
|
||||
that says why this sender may write this tenant. Recorded: the scope is exact,
|
||||
single-valued, and its authority is `GH-DEC-2026-013`'s bounded gap rather than
|
||||
a populated directory record. If the gap closes and the terminal
|
||||
directory-sourced shape lands, this entry should be revisited rather than
|
||||
assumed still correct.
|
||||
|
||||
## Applied in this repository
|
||||
|
||||
| Input | Where |
|
||||
| --- | --- |
|
||||
| Non-secret scope | `deploy/senders-scope.json` + `deploy/senders-scope.yaml` (lockstep) |
|
||||
| Receiver ingress | `deploy/networkpolicies.yaml`, `audit-core-informed-decision-ingress` |
|
||||
| Evidence kind in the model | `audit_core/senders.py`, `SenderIdentity.evidence_kind` |
|
||||
|
||||
The scope overlay only applies to a sender the Secret already carries, so this
|
||||
entry is **inert until the token exists** — asserted by test, not by reading.
|
||||
The ingress rule ANDs namespace and pod label in one `from` peer.
|
||||
`user-engine`'s and `approval-engine`'s policies are unchanged.
|
||||
|
||||
## Still owed by others
|
||||
|
||||
| Input | Owner | Note |
|
||||
| --- | --- | --- |
|
||||
| Sender token, custody path via `warden route` | OpenBao (`railiance-platform`) | audit-core never holds or transports it |
|
||||
| Protected registry entry with explicit `secret_policy: redact` | `railiance-platform` | The overlay deliberately does not override `secret_policy`; provisioning must set it |
|
||||
| Applying the manifests to `railiance01` | operator | audit-core does not apply cluster changes unprompted |
|
||||
| The `content_exists` / `custody` fields on the emitted payload | `informed-decision` | audit-core carries them; it does not require, validate or dereference them |
|
||||
|
||||
## What Audit Core will and will not claim about this stream
|
||||
|
||||
**Will:** every event it accepted is stored append-only, and the hash chain
|
||||
detects alteration or truncation of what it received — including of the
|
||||
`content_exists` and `custody` declarations.
|
||||
|
||||
**Will not:** that the archive proves a presentation occurred, or that the
|
||||
absence of one proves it did not. And specifically, per GH-DEC-2026-014:
|
||||
audit-core will not describe a commitment-only record as satisfying
|
||||
**reconstructability**. It satisfies non-alteration of the commitment. About
|
||||
what was committed to it establishes nothing, except conditionally — if a
|
||||
document is later produced, whether it is the one. No conformance claim, ITC-CAP
|
||||
case, or `docs/integrity.md` statement may say otherwise.
|
||||
|
|
@ -1,5 +1,74 @@
|
|||
# Intake records
|
||||
|
||||
## AUDIT-IN-0003 — Register informed-decision as a load-bearing sender; commitment-only payload doctrine
|
||||
|
||||
```yaml
|
||||
id: AUDIT-IN-0003
|
||||
kind: intake
|
||||
title: 'Register informed-decision as a load-bearing sender, and answer GH-DEC-2026-014
|
||||
limit 3'
|
||||
status: closed
|
||||
origin: cross-repo
|
||||
origin_ref: INFD-WP-0001-T08 / GH-DEC-2026-012 / GH-DEC-2026-014
|
||||
priority: high
|
||||
owner: audit-core
|
||||
requested_by: informed-decision
|
||||
description: >
|
||||
informed-decision is the browser-facing approver surface approval-engine
|
||||
deliberately does not contain, ruled PEP-shaped by GH-DEC-2026-012. Limit 3
|
||||
of that ruling requires its evidence copy to reach audit-core independently
|
||||
of the emitter, because the actor being audited and the evidence source are
|
||||
the same component.
|
||||
|
||||
Requested: (1) sender registration — source informed-decision exact, tenants
|
||||
[tenant:platform], write true, read false, evidence_kind load-bearing,
|
||||
secret_policy redact; (2) whether reconciliation per class as primary plus
|
||||
heartbeat for the low-volume classes is the right cadence form for a
|
||||
mixed-volume source; (3) whether commitment-only content is one event class
|
||||
or two, and whether it belongs in the same class as other evidence.
|
||||
|
||||
GH-DEC-2026-014 attached a condition addressed to audit-core: non-production
|
||||
of committed content must be detectable as a FINDING rather than present as
|
||||
an absence, so the record must carry an assertion that committed content
|
||||
exists and where custody sits. Gate House asked audit-core to say plainly
|
||||
whether the record shape can express that, rather than have a rule written
|
||||
that the storage cannot meet.
|
||||
created: '2026-09-09'
|
||||
updated: '2026-09-10'
|
||||
outcome: registered-with-a-stated-bound
|
||||
closed: '2026-09-10'
|
||||
resolution: 'Registration accepted on every proposed field; the proposal was
|
||||
correct and the substance is in the bounds. Prepared and inert pending token,
|
||||
protected-registry entry and operator apply —
|
||||
docs/informed-decision-source-registration.md, deploy/senders-scope.{json,yaml},
|
||||
audit-core-informed-decision-ingress. (1) GH-DEC-2026-014 limit 3: the fabric
|
||||
CAN carry the declaration — data is stored verbatim into details.data and
|
||||
hash-chained, so content_exists and custody need no schema change and become
|
||||
as tamper-evident as the commitment they accompany. It CANNOT detect
|
||||
non-production: audit-core performs no retrieval and its egress permits
|
||||
Postgres and DNS only. Detection happens at retrieval by the reviewer, and the
|
||||
stored declaration is what makes non-production attributable rather than a
|
||||
blank. Residual: a custodian that never held the content can emit a false
|
||||
content_exists; audit-core validates the declaration shape, never its truth —
|
||||
same class as omission at source, not closed by chain, attestation or T04/T06.
|
||||
(2) Cadence: reconciliation plus heartbeat is right, with both scoped PER
|
||||
CLASS rather than per source — a per-source heartbeat is satisfied by the
|
||||
high-volume presentation stream and says nothing about a quiet month of
|
||||
dispositions. Bound: a compromised emitter suppresses the event and its own
|
||||
count together, so neither control covers the limit-3 residual. Depends on
|
||||
T04/T06; declare now, do not describe as operating. (3) One source, distinct
|
||||
type values per class, following approval-engine four-class shape; a second
|
||||
sender would split one residual into two smaller-looking ones. (4) Tenant
|
||||
provenance is recorded in the registration document, not the envelope:
|
||||
audit-core does not resolve the tenant claim, it checks a value the credential
|
||||
is permitted to write, and restating an unobserved route in an event is the
|
||||
same error as claiming an event occurred. (5) Stated for all documents:
|
||||
commitment-only satisfies non-alteration and never reconstructability, and no
|
||||
audit-core document or conformance claim may describe it otherwise.'
|
||||
recorded_in: docs/informed-decision-source-registration.md
|
||||
work: AUDIT-WP-0009-T11
|
||||
```
|
||||
|
||||
## AUDIT-IN-0002 — Register tenant-engine as an attributive audit-core sender
|
||||
|
||||
```yaml
|
||||
|
|
|
|||
|
|
@ -59,3 +59,41 @@ def test_user_engine_sender_ingress_is_unchanged_by_the_new_sender():
|
|||
)
|
||||
assert "kubernetes.io/metadata.name: user-engine" in policy
|
||||
assert "approval-engine" not in policy
|
||||
|
||||
|
||||
def test_informed_decision_ingress_is_bound_to_namespace_and_pod_labels():
|
||||
"""AUDIT-WP-0009-T11. A second load-bearing source gets the narrow rule too."""
|
||||
documents = (ROOT / "deploy" / "networkpolicies.yaml").read_text().split("\n---\n")
|
||||
policy = next(
|
||||
document
|
||||
for document in documents
|
||||
if "name: audit-core-informed-decision-ingress" in document
|
||||
)
|
||||
|
||||
expected_peer = """ - namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: informed-decision
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: informed-decision"""
|
||||
assert expected_peer in policy
|
||||
assert policy.count(" - namespaceSelector:") == 1
|
||||
assert " - {protocol: TCP, port: 8080}" in policy
|
||||
|
||||
|
||||
def test_no_egress_to_informed_decision_is_created():
|
||||
"""The custody declaration is carried, never dereferenced.
|
||||
|
||||
GH-DEC-2026-014 limit 3 is met by storing an attributable declaration, not
|
||||
by audit-core retrieving content. If an egress rule to the emitter ever
|
||||
appears, the claim in docs/informed-decision-source-registration.md that
|
||||
audit-core performs no retrieval has silently stopped being true.
|
||||
"""
|
||||
documents = (ROOT / "deploy" / "networkpolicies.yaml").read_text().split("\n---\n")
|
||||
egress = next(
|
||||
document for document in documents if "name: audit-core-egress" in document
|
||||
)
|
||||
assert "informed-decision" not in egress
|
||||
assert "approval-engine" not in egress
|
||||
# Postgres and DNS only.
|
||||
assert egress.count(" - namespaceSelector:") == 2
|
||||
|
|
|
|||
|
|
@ -339,3 +339,62 @@ def test_user_engine_evidence_kind_is_not_asserted_on_its_behalf():
|
|||
scope = json.loads(SCOPE_FILE.read_text())
|
||||
entry = next(e for e in scope if e["name"] == "user-engine")
|
||||
assert "evidence_kind" not in entry
|
||||
|
||||
|
||||
# --- AUDIT-WP-0009-T11: informed-decision registration inputs --------------
|
||||
|
||||
|
||||
def test_informed_decision_is_declared_load_bearing():
|
||||
scope = json.loads(SCOPE_FILE.read_text())
|
||||
entry = next(e for e in scope if e["name"] == "informed-decision")
|
||||
assert entry["evidence_kind"] == "load-bearing"
|
||||
assert entry["sources"] == ["informed-decision"]
|
||||
assert entry["tenants"] == ["tenant:platform"]
|
||||
assert entry["may_read"] is False
|
||||
# §9.6 permits no completeness trade for a load-bearing source.
|
||||
assert "completeness_trade" not in entry
|
||||
assert "tokens" not in entry and "token" not in entry
|
||||
|
||||
|
||||
def test_informed_decision_scope_entry_admits_nothing_without_a_token():
|
||||
"""Safe to land ahead of the credential, asserted rather than read."""
|
||||
registry = SenderRegistry.from_env({
|
||||
"AUDIT_CORE_SENDERS": json.dumps(
|
||||
[{"name": "user-engine", "tokens": ["live"], "sources": ["user-engine"]}]
|
||||
),
|
||||
"AUDIT_CORE_SENDERS_SCOPE_PATH": str(SCOPE_FILE),
|
||||
})
|
||||
assert "informed-decision" not in [i.name for i in registry.identities]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tenant", ["platform", "tenant:coulomb", "tenant:Platform", "tenant:platform "]
|
||||
)
|
||||
def test_informed_decision_scope_restricts_a_stale_wildcard_registration(tenant):
|
||||
registry = SenderRegistry.from_env({
|
||||
"AUDIT_CORE_SENDERS": json.dumps([{
|
||||
"name": "informed-decision", "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("informed-decision")
|
||||
assert not identity.permits_source("approval-engine")
|
||||
assert identity.may_write and not identity.may_read
|
||||
assert identity.is_load_bearing
|
||||
|
||||
|
||||
def test_a_load_bearing_source_cannot_be_given_a_completeness_trade():
|
||||
"""informed-decision declares no trade and may not acquire one by overlay."""
|
||||
with pytest.raises(ValueError, match="completeness_trade"):
|
||||
SenderIdentity(
|
||||
name="informed-decision",
|
||||
tokens=("t",),
|
||||
sources=frozenset({"informed-decision"}),
|
||||
evidence_kind="load-bearing",
|
||||
completeness_trade="emits after commit",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -287,6 +287,62 @@ intervals for a store another layer reads, which `platform-pg` is. List it and
|
|||
the State Hub now. Add a conformance script and test in the shape ops-warden
|
||||
offered estate-wide.
|
||||
|
||||
```task
|
||||
id: AUDIT-WP-0009-T11
|
||||
status: progress
|
||||
priority: high
|
||||
```
|
||||
Register `informed-decision` as a distinct load-bearing source under
|
||||
`AUDIT-IN-0003`, and answer the doctrine question `GH-DEC-2026-014` put to
|
||||
audit-core: whether the record shape can carry a source-held-content
|
||||
declaration with a retrieval expectation.
|
||||
|
||||
`informed-decision` is the browser-facing approver surface. `GH-DEC-2026-012`
|
||||
limit 3 makes its evidence copy the one that must reach audit-core
|
||||
independently of the emitter, because there the actor being audited and the
|
||||
evidence source are the same component. `GH-DEC-2026-014` then granted
|
||||
commitment-only payloads for Stage 1 under a condition Gate House added: the
|
||||
record must assert that committed content **exists** and where **custody**
|
||||
sits, so non-production is a finding attributable to the custodian rather than
|
||||
an unremarkable blank.
|
||||
|
||||
**Done 2026-09-10, prepared and inert:**
|
||||
`docs/informed-decision-source-registration.md`, scope entries in
|
||||
`deploy/senders-scope.{json,yaml}`, and
|
||||
`audit-core-informed-decision-ingress` in `deploy/networkpolicies.yaml`
|
||||
(namespace AND pod label in one peer, following approval-engine rather than
|
||||
`user-engine`'s older breadth). Every proposed field accepted as-is: exact
|
||||
source, `["tenant:platform"]`, write true, read false, `load-bearing`,
|
||||
`secret_policy: redact`.
|
||||
|
||||
**The answer to Gate House's condition, in two halves that must travel
|
||||
together.** The fabric *can* carry the declaration — `data` is stored verbatim
|
||||
into `details.data` and chained, so `content_exists` and `custody` need no
|
||||
schema change and become as tamper-evident as the commitment they accompany. It
|
||||
*cannot* detect non-production: audit-core performs no retrieval and its egress
|
||||
policy permits Postgres and DNS only, so it never learns whether a custodian
|
||||
would produce. Detection happens at retrieval, by the reviewer, and the stored
|
||||
declaration is what turns a blank into an attributable failure. Residual stated
|
||||
rather than left to be found: a custodian that never held the content can emit a
|
||||
false `content_exists`; audit-core validates the declaration's shape, never its
|
||||
truth. Not a requirement nobody can meet, and not the stronger thing it might be
|
||||
mistaken for.
|
||||
|
||||
**Cadence answered:** reconciliation-per-class plus heartbeat for the low-volume
|
||||
classes is the right shape for a mixed-volume source, with both scoped **per
|
||||
class** rather than per source — a per-source heartbeat is satisfied by the
|
||||
high-volume presentation stream and says nothing about a quiet month of
|
||||
dispositions. Bound stated: where the emitter itself is compromised, it
|
||||
suppresses the event and its own count together, so neither control covers the
|
||||
residual `GH-DEC-2026-012` limit 3 exists for. Depends on T04/T06 for the
|
||||
detection half; declaring the cadence now is correct, describing it as operating
|
||||
is not.
|
||||
|
||||
Remaining: token custody, the protected registry entry, operator manifest
|
||||
application, and live ingestion evidence. No secret was created and no
|
||||
production manifest applied.
|
||||
|
||||
|
||||
## Acceptance
|
||||
|
||||
- No custody claim is returned unconditionally where `docs/integrity.md`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue