Skip to content

aether_quant.audit

The tamper-evident hash chain (formally verified in formal/AetherFormal/HashChain.lean); not the Redis / Postgres workers.

The tamper-evident hash chain (formally verified in formal/AetherFormal/HashChain.lean); not the Redis / Postgres workers.

GENESIS_HASH module-attribute

GENESIS_HASH = '0' * 64

canonical_timestamp

canonical_timestamp(value: datetime | str) -> str

The text a row's timestamp is hashed as, and read back as (Problems #339). Postgres stores created_at in a TIMESTAMPTZ column and returns a datetime, while the hash was taken over the producer's string, so a chain read back from a real database could not be verified: verify_chain raised on the datetime, and would have mismatched on the offset's spelling even if it had not. A datetime, or an ISO 8601 string that parses, is written in UTC as YYYY-MM-DDTHH:MM:SSZ (with microseconds only when there are some); anything else is returned as it is. build_audit_event already writes that form, so rows hashed before this existed verify unchanged.

compute_entry_hash

compute_entry_hash(prev_hash: str, event_type: str, created_at: str, payload: dict[str, Any]) -> str

sha256(prev_hash || event_type || created_at || canonical-JSON payload).

created_at must be a string (ISO 8601), not a datetime - callers own formatting so this stays pure and independent of any particular Postgres driver's timestamp type. payload is serialized with sorted keys and no extra whitespace so the same logical event always hashes identically regardless of dict insertion order.

verify_chain

verify_chain(rows: list[dict], expected_tail_hash: str | None = None) -> tuple[bool, int | None]

Walks rows (each needing prev_hash/hash/event_type/created_at/payload keys, oldest-first) recomputing every hash and comparing. Returns (all_valid, first_broken_index) - first_broken_index is None when all_valid is True, else the 0-based index of the first row whose prev_hash doesn't match the prior row's hash, or whose own hash doesn't match its recomputed value. An empty list is trivially valid.

Deleting the newest rows leaves a valid chain: every prefix of a valid chain is valid (formal/AetherFormal/HashChain.lean, truncation_undetected). expected_tail_hash, the latest hash recorded somewhere else, closes that gap: a chain that does not end on it reports index len(rows).