Implement AUDIT-WP-0006 honest operational custody.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Postgres now reports custody_class=operational with a cited 30-day
recoverable window. Join ITC-CAP operations.audit at D4, publish the
interface card, and overlay user-engine tenants [*] from Git so an
ExternalSecret refresh cannot shrink it.
This commit is contained in:
tegwick 2026-08-16 00:24:33 +02:00
parent 0a3d05ff1c
commit ded432a63f
25 changed files with 832 additions and 94 deletions

View file

@ -4,7 +4,11 @@ import json
import pytest
from audit_core.ingestion import IngestionApplication
from audit_core.interface import BackendUnavailableError, RetentionPolicy
from audit_core.interface import (
BackendUnavailableError,
RetentionPolicy,
custody_class_satisfies,
)
from audit_core.mock_file_backend import MockFileAuditBackend
from audit_core.sqlite_backend import SQLiteAuditBackend
@ -408,10 +412,71 @@ def test_required_custody_class_refuses_a_development_backend(tmp_path):
"""Losing AUDIT_CORE_DATABASE_URL must fail to start, not silently
downgrade custody to the development store."""
backend = SQLiteAuditBackend(str(tmp_path / "dev.db"))
with pytest.raises(ValueError, match="does not meet the required"):
IngestionApplication(backend, "opaque", require_custody_class="operational")
with pytest.raises(ValueError, match="does not meet the required"):
IngestionApplication(backend, "opaque", require_custody_class="archive")
def test_operational_and_archive_alias_for_one_deploy():
"""A mixed rollout must start: new backend + old require, and the reverse."""
class _Operational(_BrokenBackend):
@property
def retention_policy(self):
return RetentionPolicy(
custody_class="operational",
retention_days=None,
immutable=True,
tamper_evidence=False,
durable=True,
recoverable_days=30,
recoverable_source="cited",
recoverable_basis="measured",
)
IngestionApplication(_Operational(), "opaque", require_custody_class="archive")
IngestionApplication(_Operational(), "opaque", require_custody_class="operational")
def test_custody_class_alias_is_not_development():
assert custody_class_satisfies("operational", "archive")
assert custody_class_satisfies("archive", "operational")
assert not custody_class_satisfies("development", "operational")
assert not custody_class_satisfies("development", "archive")
assert custody_class_satisfies("development", "development")
def test_readiness_reports_recovery_fields_for_operational_backend():
class _Operational(_BrokenBackend):
@property
def retention_policy(self):
return RetentionPolicy(
custody_class="operational",
retention_days=None,
immutable=True,
tamper_evidence=False,
durable=True,
recoverable_days=30,
recoverable_source="resource-control/data/capability/platform-audit-storage.json",
recoverable_basis="measured",
)
def health(self):
return None
status, body = invoke(
IngestionApplication(_Operational(), "opaque"),
None, path="/readyz", method="GET", body=b"",
)
assert status.startswith("200")
assert body["custody_class"] == "operational"
assert body["durable"] is True
assert body["recoverable_days"] == 30
assert body["recoverable_basis"] == "measured"
assert "platform-audit-storage" in body["recoverable_source"]
def test_counters_track_each_outcome(tmp_path):
app, _ = bound_app(tmp_path, may_read=True)
invoke(app, event()) # accepted