audit-core/docs/informed-decision-source-registration.md
tegwick a2173c6cc4
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
docs: record native audit sender delivery and reload
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
2026-09-11 10:32:20 +02:00

12 KiB

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.

Original owner handoff (current delivery recorded below)

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.

Native delivery return — 2026-09-11

The token is now independently held in OpenBao and delivered through its exact ESO projection. Namespace restrictions, sibling/full-registry/metadata/listing denials, wrong-SA/namespace refusals and the coding-agent boundary passed natively. Registry version 8 was compared exactly before Audit Core reloaded; receiver c82e0442 is Ready and operational/durable. The admitted manifests and sender ingress are applied. See the native receipt.

Remaining admission evidence is actual producer accepted/duplicate delivery, receiver source/tenant/read refusal, independent readback and audit bearer revocation. No native producer event or operational heartbeat is claimed by the custody receipt. Attestation/offsite operation remains a separate evidence quality bound in AUDIT-WP-0009-T12.