security
    New
    2026-06-18

    Audit-Chain Anchoring

    Anchor the per-workspace SHA-256 audit chain to an external append-only ledger (immudb, Azure Confidential Ledger, or a WORM file) so tampering is detectable by a third party — even against the platform operator.

    audit
    immutable-ledger
    tamper-evidence
    immudb
    azure-confidential-ledger
    compliance

    Why anchoring

    The per-workspace audit log is a SHA-256 hash chain — altering any row breaks verification from that row forward, so it is tamper-evident. But it is not, by itself, tamper-proof against the platform operator: someone with database write access could rewrite a row and recompute every following hash. Anchoring closes that gap by publishing each workspace's current chain head hash to an external append-only ledger the operator cannot retroactively edit. Once a head is anchored, any later forgery is detectable by a third party, and the ledger's own cryptographic receipt proves the anchor was not backdated. Available on Enterprise and Sovereign plans (immutable_ledger feature).

    Audit log
    The audit log this anchoring protects — a per-workspace SHA-256 hash chain with end-to-end verification and CSV export.
    Audit Log
    Detail panel (opens when you click a row).

    Ledger Backends

    Select a backend with the LEDGER_BACKEND env var; anchoring is disabled when it is empty. A Celery Beat task anchors every LEDGER_ANCHOR_INTERVAL_HOURS (default 1h), and you can anchor on demand via the API or CLI.

    `LEDGER_BACKEND`LedgerHow the anchor is written
    `immudb`immudb (tamper-proof Merkle tree)Verified set; the response carries an inclusion proof
    `azure_cl`Azure Confidential Ledger (hardware-backed CCF)Append transaction; a signed receipt is returned
    `file`Append-only JSONL on a WORM volume (air-gapped / sovereign)One JSON line appended per anchor

    API & CLI

    Each stored anchor keeps the external ledger's transaction id and the verbatim receipt, so verification does not depend on trusting the platform database. The anchored record pins head_hash (the most recent entry hash) — because each entry's hash covers the previous one, fixing the head fixes every entry before it.

    bash
    1# Anchor the current chain head (ops/admin)
    2curl -X POST "https://sovereign.yourcompany.com/api/v1/audit/anchors?workspace_id=ws-prod" \
    3  -H "Authorization: Bearer $PAT"
    4
    5# List past anchors + their ledger receipts
    6curl "https://sovereign.yourcompany.com/api/v1/audit/anchors?workspace_id=ws-prod" \
    7  -H "Authorization: Bearer $PAT"
    8
    9# Recompute the live head and compare to the latest anchor
    10curl -X POST "https://sovereign.yourcompany.com/api/v1/audit/anchors/verify?workspace_id=ws-prod" \
    11  -H "Authorization: Bearer $PAT"
    12
    13# Same, from the CLI (verify-anchor exits 2 on mismatch)
    14oss audit anchor ws-prod
    15oss audit verify-anchor ws-prod

    Third-Party Verification

    An external auditor who does not trust the operator obtains the anchor independently from the external ledger (not from the platform DB) — for immudb, fetching the anchor key returns the value with an inclusion proof against its Merkle tree; for Azure Confidential Ledger, the signed CCF receipt is verified against the ledger's identity. The auditor then re-fetches the audit entries, recomputes the chain head, and confirms it equals the independently-obtained anchored head. A mismatch proves the log was altered after anchoring.

    Edit this page on GitHub