TrustNet
Audit · Draft

A consent trail you can prove, not just assert

How TrustNet makes every consent, access, and revocation decision tamper-evident — and lets your auditor verify it independently, offline, without trusting our database or our word.

Document: TrustNet-Audit-v1 Updated: 2026-10-06 Audience: DPO · Security architect · Auditor

1The problem

A consent record that cannot be independently verified is a liability, not an asset. Most systems store audit trails in the same mutable database as everything else, and ask auditors to trust that nothing was changed. TrustNet's audit log is built so that trust is not required.

1.1 A log you have to take on trust

A conventional audit table can be edited or deleted by anyone with database access — accidentally, or on purpose. Nothing in the record itself reveals that it changed, so proving its integrity means proving a negative.

1.2 Consent that cannot be evidenced

Under GDPR Art. 7(1), you must be able to demonstrate that a data subject consented, and to what. If the trail is incomplete, or the consent wording and purpose it references are not versioned, "the user agreed" is an assertion, not evidence.

1.3 The audit trail as a second copy of PII

A log that records raw claim values turns the audit system into a second, unmanaged copy of personal data — a honeypot, and another breach surface. The trail should prove what was disclosed without becoming the disclosure.

1.4 Erasure versus accountability

GDPR Art. 17 (erasure) pulls against Art. 5(2), 7 and 30 (accountability). If the identifying data and the proof are the same bytes, honouring a deletion request destroys the evidence. TrustNet separates the two so erasure never edits a proof.

1.5 Tampering that is only noticed too late

Even a hash-chained log leaves one gap: the operator that holds the log can rewrite the chain and recompute the hashes. Without an external witness, a determined insider can rewrite history and make it self-consistent. This is threat T4 in TrustNet's threat model, and the reason for the daily external timestamp.

2What "tamper-proof" means

Tamper-proof here is a precise, testable property — not a claim of physical impossibility. Any retroactive insertion, deletion, reordering, or modification of the trail is detectable, by an outsider from the tree alone, and by TrustNet's own operator from an externally timestamped anchor.

2.1 Append-only, enforced

Audit and consent rows admit no UPDATE or DELETE outside an audited erasure workflow. This is enforced by a database trigger and by an INSERT-only writer role — two independent layers, not a convention.

2.2 A hash chain, not a promise

Every event is canonicalized and hashed into a Merkle log. Changing any leaf changes its hash, which changes every hash up to the root, which breaks the proofs that a verifier holds. A signed, externally timestamped tree head pins the whole tree to a point in time.

2.3 Two threats, two defences

  • Tampering by an outsider — prevented from going unnoticed by Merkle inclusion and consistency proofs plus the signed tree head.
  • Tampering by TrustNet itself — caught by the external RFC 3161 timestamp over the daily meta-root; rewriting the log cannot forge an external authority's timestamp, and past anchors are immutable history.

3How it works

3.1 From event to anchored leaf

There is one write primitive. Each event is canonicalized with JCS (RFC 8785) and hashed into a leaf; leaves are combined into a Merkle tree; the tree head is signed; and every tree head is rolled into a daily meta-root that is timestamped by an external authority.

flowchart LR
    E["Event: consent, access, revocation"] --> C["Canonical JSON (JCS, RFC 8785)"]
    C --> H["Leaf hash (SHA-256, RFC 6962)"]
    H --> T["Per-RP Merkle tree"]
    T --> S["Signed tree head (Ed25519, key in Vault)"]
    S --> M["Daily meta-root over every tree"]
    M --> A["External RFC 3161 timestamp"]
          

3.2 The Merkle tree and inclusion proofs

Each relying party (RP) gets its own tree, so an auditor's bundle is self-contained for that RP. An inclusion proof lets a verifier recompute the root from a single leaf and a handful of sibling hashes — without seeing the rest of the tree, and without the values behind any other leaf.

flowchart BT
    L0["leaf 0: rp.registered"] --> P0["interior hash"]
    L1["leaf 1: consent.granted"] --> P0
    L2["leaf 2: disclosure.read_batch"] --> P1["interior hash"]
    L3["leaf 3: consent.revoked"] --> P1
    P0 --> Root["Root hash"]
    P1 --> Root
    Root --> STH["Signed tree head"]
          

3.3 Signed tree heads and external anchoring

A tree head is emitted on every change and at least daily, signed with a dedicated Ed25519 key held in a vault — separate from the OIDC and webhook keys, so its blast radius is independent. The anchor job builds one meta-tree over every tree's current head, timestamps the meta-root, and stores the per-tree inclusion proofs. The signature proves who; the external timestamp proves when.

3.4 What tampering breaks

There is no way to alter a recorded event and keep every proof consistent. The breakage is what a verifier checks: a proof that no longer verifies against a previously seen head is a finding, not a silent pass.

flowchart TB
    X["A leaf is edited, removed, or reordered"] --> Y["Its hash changes"]
    Y --> Z["Hashes up to the root change"]
    Z --> C1["The inclusion proof for that leaf fails"]
    Z --> C2["The consistency proof against the previous tree head fails"]
    C2 --> C3["The root no longer matches the timestamped anchor"]
          

3.5 Verifying a bundle offline

An RP can export its own trail as a self-contained bundle — a signed manifest, the events, the proofs, the anchors and the public keys — and verify it with an offline CLI. No network call, no trust in a live TrustNet service.

sequenceDiagram
    autonumber
    participant RP as RP owner or auditor
    participant V as trustnet-verify (offline)
    participant B as Export bundle
    RP->>V: trustnet-verify bundle.tar.gz
    V->>B: read manifest JWS, events, proofs, anchors, keys
    V->>V: recompute every leaf hash
    V->>V: check inclusion and consistency proofs
    V->>V: verify the Ed25519 tree-head signature
    V->>V: check meta-tree inclusion and the RFC 3161 token
    V-->>RP: VALID, or the exact findings
          

3.6 Privacy by construction

  • Claim names, never values. The log records which claims were approved or revoked — email, country — never the values behind them.
  • Pseudonymous. Events carry pairwise subject and actor identifiers, unlinkable across trees; raw identifiers never enter the hashed record.
  • Erasure-compatible. Correlation metadata lives in a separate, erasable sibling row. A GDPR Art. 17 erasure deletes that metadata; the PII-free hashed leaf — the proof — is never touched.
Values are never logged. A DPO audit export contains claim names, personas, clients, and timestamps — enough to prove consent, without becoming a second copy of the data.

4Proving compliance

The trail is not an internal logging convenience; it is the evidence layer for accountability. Each governing requirement maps to a concrete mechanism.

4.1 Regulation mapping

RequirementWhat it demandsMechanism
GDPR Art. 5(2)Demonstrate compliance; records must be reliableAppend-only Merkle log + signed tree heads + external anchoring
GDPR Art. 7(1)Prove consent was given, with the information shownVersioned consent wording and purpose; consent.granted leaf
GDPR Art. 15 / 20Produce the subject's record on requestPer-user export (Art. 15/20 artifact)
GDPR Art. 17Erase identifying data without destroying the recordPII-free hashed leaf + erasable correlation sibling
GDPR Art. 30Maintain records of processingConsent history + per-RP compliance export
eIDAS Art. 41Presumption of time accuracyDaily RFC 3161 timestamp (qualified-TSA path)
RFC 9162 / 6962Verifiable log constructionMerkle tree, inclusion and consistency proofs
RFC 8785Canonical serialization before hashingJCS leaf hashing

4.2 A worked audit

  1. An RP (or its auditor) requests its trail export. The request itself is written back into the same tree, so an export is never invisible.
  2. The export returns a self-contained bundle: the events, the Merkle proofs, the signed tree heads, the daily anchors, and the public keys.
  3. The auditor runs the offline verifier. It recomputes leaf hashes, checks each inclusion and consistency proof, verifies the Ed25519 tree-head signature, and checks the meta-tree inclusion against the external timestamp.
  4. A missing consent, an edited record, or a reordered event does not merely fail a test — it cannot be made to reproduce the signed, externally timestamped root. The verifier reports the exact finding.

4.3 What each audience receives

AudienceWhat they get
End userWho has their data, what they shared, when, and how to revoke it
RP / its auditorA self-contained, offline-verifiable bundle of every event in their tree
RegulatorA tamper-evident, externally timestamped trail that survives the RP's deletion and proves its own integrity

5Honest limits

We state the boundaries as plainly as the guarantees, because a compliance claim is only as strong as its caveats.

  • Completeness is scoped. Per-tree completeness is provable; completeness across trees is not claimed, and the verifier says so.
  • The external timestamp is a trust you still make — but a narrow, independent one. Tamper-evidence by TrustNet's own operator rests on the timestamp authority, not on TrustNet. Production migrates from a public TSA to a qualified EU timestamp provider.
  • Reads are corroborating. Post-consent access reads are batched and hash-committed; the disclosure of record is the consent decision itself.
  • Erasure reduces detail, never the fact. An Art. 17 erasure removes the correlation metadata and leaves the PII-free leaf — the proof that a consent once existed — intact.

6Glossary

LeafA single canonicalized, hashed audit event.
Tree headThe root hash of a tree at a point in time, signed with the audit key.
Inclusion proofThe sibling hashes needed to recompute the root from one leaf.
Consistency proofEvidence that a newer tree head extends an older one — no history rewritten.
AnchorA signed tree head timestamped by an external authority (RFC 3161).
JCSJSON Canonicalization Scheme (RFC 8785), applied before hashing.

7References

  • RFC 9162 — Certificate Transparency v2.0
  • RFC 6962 — Certificate Transparency (Merkle tree hashing)
  • RFC 3161 — Time-Stamp Protocol
  • RFC 8785 — JSON Canonicalization Scheme
  • GDPR Articles 5(2), 7(1), 15, 17, 20, 30; eIDAS Art. 41
  • TrustNet ADR-0012 (audit transparency-log backend), ADR-0013 (immutable leaf / erasable metadata)

This document is an indicative product description, not legal advice, and not final copy.