tenant-engine/docs/evidence-emission.md
tegwick 672cf4da6e
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 37s
Implement TEN-WP-0011 security layer conformance
Engine/PIP declaration is now checkable (layer.yaml plus a Tooling-client
scan). Writes persist a decision record or the published fail-closed
stance, live-lookup freshness is published, events_for is tenant-scoped,
and mutation evidence drains to audit-core from a local outbox without
blocking the mutation.

Sender registration is requested as AUDIT-IN-0002. Boundary-contract
amendment is requested as NET-IN-0002.

Assistant: grok
Assistant-Session: 01a04cea-e5e8-7081-a0fc-808ebbc35fa9
2026-08-29 13:02:51 +02:00

2.5 KiB

Mutation evidence emission (TEN-WP-0011-T04)

Statute §9.6. Companion §6. Current event classes are attributive: no control in this engine branches on an event's presence. Role grants as facts are live PIP input; the events are the trail.

Bound

The local events table and the local audit_outbox prove that the records they hold were not altered or truncated after arrival. They do not prove an event happened, and absence is not evidence of non-occurrence. They share this service's runtime database credential, so they are not independent custody.

Independent custody belongs to audit-core. This engine POSTs /v1/events and holds no SQL, no admin, and no rewrite path against audit-core's store. The external copy therefore cannot be rewritten through tenant-engine's database credential.

Trade (declared)

Step Atomic with mutation? If it fails
Insert local events row yes (same transaction) mutation rolls back
Insert local audit_outbox row yes (same transaction) mutation rolls back
Drain outbox to audit-core no — after commit mutation already succeeded; row stays pending

Emission is non-blocking. Unavailable audit-core MUST NOT fail-open a mutation (the fact is already written) and MUST NOT fail-closed a mutation (attributive evidence is not load-bearing). Completeness is not claimed. If a future control starts branching on these events, that class must be reclassified load-bearing before it ships, and this trade revisited.

Envelope

audit-core.event.v1alpha1. source is tenant-engine. See tenant_engine.audit_core.envelope_for. Duplicate event ids are 200 and not retried; 400/409 dead-letter; 503/transport retry.

Credentials

No secret in Git. Production sender token is projected as TENANT_ENGINE_AUDIT_CORE_TOKEN_FILE, routed through warden route (audit-core sender registration), never through a State Hub message. Sender registration itself is requested as AUDIT-IN-0002.

Backfill

Decision: no backfill of pre-cutover event classes. Those rows stay in the local table. Reconstructing them into audit-core would mint evidence this engine cannot prove was complete at the time. New mutations from this workplan onward enqueue the outbox.

Configuration

Env var Default Meaning
TENANT_ENGINE_AUDIT_CORE_URL unset Drain is skipped; outbox rows stay pending.
TENANT_ENGINE_AUDIT_CORE_TOKEN_FILE unset Bearer token file for POST /v1/events.
TENANT_ENGINE_AUDIT_CORE_TIMEOUT_SECONDS 3 Drain timeout.