Implement AUDIT-WP-0006 honest operational custody.
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:
parent
0a3d05ff1c
commit
ded432a63f
25 changed files with 832 additions and 94 deletions
|
|
@ -13,7 +13,23 @@ from uuid import uuid4
|
|||
|
||||
SCHEMA_VERSION_V1ALPHA1 = "audit-core.event.v1alpha1"
|
||||
|
||||
CustodyClass = Literal["development", "archive", "hot_search"]
|
||||
CustodyClass = Literal["development", "operational", "archive", "hot_search"]
|
||||
|
||||
# Production Postgres reports ``operational``. Manifests written before
|
||||
# AUDIT-WP-0006 required ``archive``. The two are aliases for one deploy so
|
||||
# a mixed rollout cannot refuse to start. ``development`` is never an alias.
|
||||
_PRODUCTION_CUSTODY_CLASSES = frozenset({"operational", "archive"})
|
||||
|
||||
|
||||
def custody_class_satisfies(actual: str, required: str) -> bool:
|
||||
"""Whether a backend's class meets a startup requirement.
|
||||
|
||||
``operational`` and ``archive`` satisfy each other. A development
|
||||
backend satisfies only ``development``.
|
||||
"""
|
||||
if actual == required:
|
||||
return True
|
||||
return {actual, required} <= _PRODUCTION_CUSTODY_CLASSES
|
||||
|
||||
_REQUIRED_STRING_FIELDS = (
|
||||
"schema_version",
|
||||
|
|
@ -41,6 +57,24 @@ class RetentionPolicy:
|
|||
immutable: bool
|
||||
tamper_evidence: bool
|
||||
durable: bool
|
||||
# Recoverable history is the platform backup window, not a deletion
|
||||
# policy. ``None`` means not declared (development backends).
|
||||
recoverable_days: int | None = None
|
||||
recoverable_source: str | None = None
|
||||
recoverable_basis: str | None = None
|
||||
|
||||
def as_readiness(self) -> dict[str, Any]:
|
||||
"""Sender-visible /readyz body. Keeps ``custody_class`` and adds recovery."""
|
||||
payload: dict[str, Any] = {
|
||||
"status": "ok",
|
||||
"custody_class": self.custody_class,
|
||||
"durable": self.durable,
|
||||
}
|
||||
if self.recoverable_days is not None or self.recoverable_source:
|
||||
payload["recoverable_days"] = self.recoverable_days
|
||||
payload["recoverable_source"] = self.recoverable_source
|
||||
payload["recoverable_basis"] = self.recoverable_basis
|
||||
return payload
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
|
@ -142,7 +176,7 @@ def validate_event(event: AuditEvent) -> None:
|
|||
class AuditBackend(Protocol):
|
||||
"""Protocol implemented by replaceable audit sinks.
|
||||
|
||||
Production backends provide durable archive or hot-search custody.
|
||||
Production backends provide durable operational or archive custody.
|
||||
Development backends (such as :class:`~audit_core.mock_file_backend.MockFileAuditBackend`)
|
||||
are for wiring only and must not be treated as audit custody.
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue