"""Auditable outcome for every read and mutation. One JSONL ledger per tenant per day. The record shape is ``common.schema.json#/$defs/audit_outcome``; it never carries request bodies, only the action name, the resource id and the decision. """ from __future__ import annotations from hux.identity import Identity from hux.store import TenantStore, now_iso OUTCOMES = ("allow", "deny", "not_found", "conflict", "flag_off") def record(store: TenantStore, identity: Identity, action: str, resource: str, outcome: str, reason: str = "") -> dict: """Append an audit outcome and return it.""" if outcome not in OUTCOMES: raise ValueError(f"unknown outcome {outcome!r}") entry = { "at": now_iso(), "identity": identity.record(), "action": action, "resource": resource[:200], "outcome": outcome, } if reason: entry["reason"] = reason[:200] store.append("audit", now_iso()[:10], entry) return entry def recent(store: TenantStore, limit: int = 200) -> list[dict]: """Newest audit rows across day ledgers, newest last.""" rows: list[dict] = [] for name in reversed(store.ledgers("audit")): rows = store.read("audit", name) + rows if len(rows) >= limit: break return rows[-limit:]