Register informed-decision.memo.posed for the platform tenant.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Accept the retained pose envelope and refuse packet, brief, and secret
content. Heartbeat and reconciliation follow the existing load-bearing
class pattern. This does not connect the sink or claim runtime readiness.

Assistant: grok
Assistant-Session: 01a10329-31cb-78d3-b7a3-787a69c89d54
This commit is contained in:
tegwick 2026-10-03 21:18:13 +02:00
parent fdca5758e1
commit 70e4044128
16 changed files with 534 additions and 10 deletions

View file

@ -45,6 +45,7 @@ from audit_core.redaction import (
SecretFieldRejection,
apply_policy,
finding_from_path,
scan,
)
from audit_core.senders import WILDCARD, SenderIdentity, SenderRegistry, development_registry
from audit_core.sqlite_backend import SQLiteAuditBackend
@ -552,11 +553,71 @@ class IngestionApplication:
return [body]
# Local registration of informed-decision.memo.posed (intake dependency T07).
# The retained envelope is the one informed-decision already queues: the eight
# wire fields, with data limited to the six ids and digests below. Packet,
# brief, and secret-shaped keys are refused before any other error so a later
# invalid_event cannot write that body into the dead-letter store.
POSED_EVENT_TYPE = "informed-decision.memo.posed"
POSED_SOURCE = "informed-decision"
POSED_TENANT = "tenant:platform"
POSED_DATA_KEYS = frozenset({
"memo_id",
"approval_id",
"approval_binding_digest",
"submitter",
"memo_class",
"submission_digest",
})
CONTENT_REFUSED = "content_refused"
_POSED_CONTENT_KEYS = frozenset({"packet", "brief"})
def _has_posed_content_key(value: Any) -> bool:
if isinstance(value, dict):
for key, item in value.items():
if str(key).lower() in _POSED_CONTENT_KEYS or _has_posed_content_key(item):
return True
elif isinstance(value, list):
return any(_has_posed_content_key(item) for item in value)
return False
def _refuse_posed_content(payload: dict[str, Any]) -> None:
"""Refuse packet, brief, and secret-shaped keys on this class only.
Other event types keep the sender's secret_policy. This class does not:
the dependency says refuse, and redacting would still accept the event.
"""
findings = scan(payload)
if findings:
raise SecretFieldRejection(findings)
if _has_posed_content_key(payload):
raise ValueError(CONTENT_REFUSED)
def _require_posed_envelope(payload: dict[str, Any]) -> None:
"""Accept only the locally retained field set, for this source and tenant."""
if str(payload.get("source") or "") != POSED_SOURCE:
raise ValueError("source_not_allowed")
if str(payload.get("tenant") or "") != POSED_TENANT:
raise ValueError("tenant_not_allowed")
data = payload.get("data")
if (
not isinstance(data, dict)
or set(data) != POSED_DATA_KEYS
or any(type(data[key]) is not str or not data[key].strip() for key in POSED_DATA_KEYS)
):
raise ValueError("invalid_event")
def normalize(
payload: dict[str, Any],
idempotency_key: str | None,
identity: SenderIdentity | None = None,
) -> AuditEvent:
if isinstance(payload, dict) and payload.get("type") == POSED_EVENT_TYPE:
_refuse_posed_content(payload)
required = (
"id", "type", "source", "subject", "tenant", "correlation_id", "occurred_at", "data",
)
@ -575,6 +636,8 @@ def normalize(
raise ValueError("tenant_not_allowed")
elif source != "user-engine":
raise ValueError("source_not_allowed")
if payload.get("type") == POSED_EVENT_TYPE:
_require_posed_envelope(payload)
observed_at = _normalize_timestamp(payload["occurred_at"])
policy = identity.secret_policy if identity is not None else POLICY_REDACT
# Raises SecretFieldRejection under the reject policy; that exception

View file

@ -166,10 +166,10 @@ MIGRATIONS: list[tuple[str, str]] = [
),
]
# Rejection reasons whose payload must never be persisted — storing the body of
# an event rejected *for carrying secret-shaped material* would write that
# material into the audit store.
WITHHOLD_PAYLOAD_REASONS = frozenset({"secret_shaped_field"})
# Rejection reasons whose payload must never be persisted. Storing the body of
# an event rejected for secret-shaped material, or for packet/brief content,
# would write that material into the audit store.
WITHHOLD_PAYLOAD_REASONS = frozenset({"secret_shaped_field", "content_refused"})
class PostgresAuditBackend:

View file

@ -74,9 +74,9 @@ CREATE TABLE IF NOT EXISTS secret_findings (
"""
# Rejection reasons whose payload must never be persisted. Storing the body of
# an event rejected *for containing secret-shaped material* would write that
# material into the audit store — the precise outcome the rejection prevents.
WITHHOLD_PAYLOAD_REASONS = frozenset({"secret_shaped_field"})
# an event rejected for secret-shaped material, or for packet/brief content,
# would write that material into the audit store.
WITHHOLD_PAYLOAD_REASONS = frozenset({"secret_shaped_field", "content_refused"})
class SQLiteAuditBackend: