Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
97 lines
5.1 KiB
Markdown
97 lines
5.1 KiB
Markdown
# Platform approval audit sender
|
|
|
|
Source return for `APPROVAL-WP-0002-T01/T04` and `AUDIT-WP-0009-T09`,
|
|
2026-09-08. This records the existing platform store's required registration;
|
|
it neither provisions a credential nor admits another tenant.
|
|
|
|
| Field | Required value |
|
|
| --- | --- |
|
|
| Sender and permitted source | `approval-engine` (exact) |
|
|
| Tenants | `["tenant:platform"]` (exact) |
|
|
| Write / read | `true` / `false` |
|
|
| Evidence kind | `load-bearing` |
|
|
| Completeness trade | none |
|
|
| Secret policy | `redact` |
|
|
| Consumer mount | Secret `approval-engine/approval-engine-audit`, key `audit-token` |
|
|
|
|
The accepted platform tenant decision is already implemented in the Engine
|
|
default, production CLI and deployment arguments. The audit envelope carries
|
|
that same store tenant without normalization. This is the tenant list Audit
|
|
Core requested; neither `platform`, `tenant:coulomb`, nor `*` is required.
|
|
A future store serving another tenant needs a separate registration review.
|
|
|
|
Choose `redact` explicitly: the receiver should retain a legitimate revocation
|
|
record with an accidentally secret-shaped field removed, rather than reject
|
|
the event and leave its delivery pending. Approval payloads must still contain
|
|
no credentials. Redaction findings do not excuse a producer defect.
|
|
Audit Core's scope overlay does not set this field; the protected sender
|
|
registry entry must explicitly carry `secret_policy: redact` when provisioned.
|
|
|
|
## Heartbeat classes
|
|
|
|
Per class, not per source (`AUDIT-WP-0009-T04`, audit-core
|
|
`docs/stream-completeness.md`). A per-source heartbeat is discharged by
|
|
whichever class happens to be busy and says nothing about the quiet one — here
|
|
that is `revocation`, whose silence is the only silence that matters. Requested
|
|
`heartbeat_classes`, all four at the same bound:
|
|
|
|
| Class | Registered wait before receiver grace |
|
|
| --- | --- |
|
|
| `issuance` | 2f = 7200s (2h) |
|
|
| `use` | 2f = 7200s (2h) |
|
|
| `supersession` | 2f = 7200s (2h) |
|
|
| `revocation` | 2f = 7200s (2h) |
|
|
|
|
These are registered base waits, not the effective alert threshold. Founder
|
|
coordination-frame decision 2026-10-15 sets f=3600s. The prepared emitter
|
|
interval is f; the receiver wait is 2f=7200s, and Audit Core's existing 1.5
|
|
grace gives an effective deadline of 3f=10800s. This source request is not a
|
|
live registry receipt or proof that deployment has changed pace. The engine emits all four in a
|
|
single transaction: a partial emission would report some classes healthy and
|
|
others stalled, which is a worse signal than none.
|
|
|
|
The first beat is emitted at startup rather than one interval later —
|
|
`heartbeat_due` is true when no heartbeat exists — because audit-core raises
|
|
`no_heartbeat_since_registration` for a declared-but-never-sent class, which is
|
|
the shape a naive compare-against-last-seen would drop silently.
|
|
|
|
Heartbeat events carry `type: audit-core.heartbeat` with `class` and
|
|
`assertion` on `data`. They are ordinary events on the same append-only chain
|
|
and the same durable drain; there is no separate heartbeat path here either.
|
|
|
|
## Reconciliation
|
|
|
|
`GET /v1/reconciliation?source=&tenant=&since=&until=` returns per-class counts
|
|
for this source's own events. `may_read: false` still holds and is not
|
|
weakened: a source asking how many of its own events the archive holds learns
|
|
nothing it did not itself emit. Both bounds must be supplied — a count whose
|
|
window this engine did not choose is not comparable against
|
|
`transition_counts()`. A request for another source's counts returns `403`, not
|
|
zero; a zero would be a false answer to a completeness question.
|
|
|
|
**The bound, restated so it is not lost:** agreement on counts proves neither
|
|
completeness nor that any event occurred, and a missing heartbeat is not proof
|
|
of suppression. Both controls cover loss, outage, drain failure and accident.
|
|
Neither covers this engine lying about itself — a compromised emitter suppresses
|
|
the event and its own heartbeat together. That is the residual, and it is not
|
|
closed by anything in this document.
|
|
|
|
The transactional outbox emits issuance, use, supersession and revocation;
|
|
heartbeat uses the same durable drain. Its atomicity and retry contract remain
|
|
in [outbox-contract.md](outbox-contract.md). Receiver identity/scope/ingress and
|
|
credential admission precede live drain proof. Attestation freshness, omission
|
|
detection and reconciliation retain their separate AUDIT-WP-0009-T02/T04/T06
|
|
owners and bounds; this registration does not claim their completion.
|
|
|
|
First provisioning still requires the platform-owned custody record linking
|
|
the receiver's protected sender registry and this mounted credential. No
|
|
credential value belongs in this document, Git, Hub or a rollout receipt.
|
|
|
|
|
|
Current timing values derive from coordination-engine/config/coordination-frame.json
|
|
through net-kingdom/tools/coordination-frame/render.py. The generated declaration,
|
|
consumer constants and deployment pod annotation carry its SHA-256. Existing
|
|
credential scope and the native September 15 bearer-revocation proof are
|
|
retained; do not repeat that unchanged proof as if it were missing. Actual
|
|
lifecycle reconciliation, class registration, lag findings and operational
|
|
observer readback remain APPROVAL-WP-0002-T04.
|