Writing/

How to Add a Tamper-Evident Audit Trail to n8n (Step by Step)

A step-by-step tutorial: add an independent, cryptographically signed audit trail to any n8n workflow with one Code node, verifiable offline from a public key.

If you are running business-critical automations, you probably rely on the execution logs of your integration engine. But in n8n, those logs are stored on a mutable database or server file that can be modified, deleted, or falsified. This tutorial walks you through how to add a tamper-evident audit trail to n8n — one that is cryptographically signed, independent, and verifiable offline — using a single, lightweight Code node. (For a higher-level overview of the approach, see AXR for n8n.)

By the end of this guide, your workflow will generate verifiable execution receipts that any external partner can audit without giving them access to your n8n instance.


Why Native n8n Logs Fall Short

n8n is an incredible platform for orchestrating webhooks, databases, and AI agents. It tracks every execution, showing you exactly which nodes ran and what data they processed.

However, n8n’s native execution log has major limitations when viewed from a security and GRC (Governance, Risk, and Compliance) perspective:

  1. It is Mutable: The execution history is stored in a standard relational database (like PostgreSQL or SQLite) or as plain files. Anyone with administrative access to the database or the underlying server can alter or delete these records.
  2. It is Unsigned: The log entries are not cryptographically signed. There is no mathematical proof that the logs were generated by the system itself and have not been altered after the fact.
  3. It is Tied to the Instance: If you need to prove to an external stakeholder, auditor, or customer that a specific workflow executed correctly, you must either give them access to your n8n instance or export a JSON file that they have no reason to trust.

A true audit log must be independent, immutable, and verifiable offline. It should not ask the auditor to “trust the server,” but rather to verify the mathematics.


The One-Code-Node Integration Pattern

To achieve this level of security without rewriting our workflows, we can use the AXR (Agent eXecution Receipts) pattern. Instead of routing all our logs to an expensive external compliance platform, we place a single AXR Code node at the end of our workflow.

This node performs a very specific task:

  1. It gathers the inputs and outputs from the key decision-making nodes in the workflow (e.g., geographic checks, pricing logic, transaction commits).
  2. It structures this data into a canonical JSON object (using RFC 8785 canonicalisation).
  3. It cryptographically signs the canonical object using a private Ed25519 key stored securely in the environment variables.
  4. It outputs a complete Execution Receipt containing the step-by-step claims and the cryptographic signature.

Here is what the physical n8n workflow layout looks like:

[Webhook Trigger] ──> [Geographic Check] ──> [Calculate Pricing] ──> [AXR Code Node] ──> [API Response]

The AXR Code node is completely self-contained. It requires zero external dependencies, running pure JavaScript inside n8n’s native environment.


Designing for Resilience: Fail-Open and Out-of-Band

When adding security or auditing layers to a live production workflow, your first priority must be: Do not break the business.

If your auditing system goes down, or if an API key expires, you should not prevent real customers from completing their transactions. To prevent this, the AXR integration is designed around two core principles:

1. Fail-Open Execution

If the AXR Code node fails to locate the private signing key, or if an unexpected exception occurs during serialisation, the node is designed to fail open. It will catch the error, write a loud alert to the standard output for your monitoring tools to flag, and emit an unsigned placeholder block.

The workflow continues to execute, the transaction is processed, and the customer receives their response. You get a loud alert about the audit gap, but your revenue path is never blocked.

2. Out-of-Band Anchoring

Calculating an Ed25519 signature in JavaScript takes less than a millisecond. It introduces virtually zero latency to your live execution path.

However, we also want to prevent log deletion by anchoring our receipts into a Merkle tree and committing them to a public registry. If we did this synchronously during the live execution path, we would introduce several hundred milliseconds of network latency and make our checkout process depend on an external server’s availability.

Instead, we run anchoring out-of-band:

  1. The n8n Code node writes the signed receipt to a local JSON Lines (.jsonl) file on the server.
  2. A lightweight background script runs on a regular cron schedule (e.g., hourly).
  3. This script reads the newly appended receipts, compiles them into an RFC 6962 Merkle tree, and commits the signed root to an external, append-only backend (such as OpenTimestamps, Rekor, or RFC 3161 timestamping).

The live user experience remains blazing fast, while our audit trail remains cryptographically secure.

Store the receipts outside n8n’s execution data. n8n prunes execution history by default (EXECUTIONS_DATA_PRUNE with EXECUTIONS_DATA_MAX_AGE), so if you stash receipts in the execution context or a Data Table, the cryptographic chain survives but the run context that ties each receipt to its execution can be aged out from under you. Write the .jsonl to durable, append-only storage — a bind-mounted volume or dedicated path — before you rely on pruning. The chain then lives independently of n8n’s retention settings.


Anatomy of an n8n Execution Receipt

Let’s look at what the AXR Code node actually outputs. Here is an illustrative step receipt for a pricing decision, in AXR’s canonical shape:

{
  "axr_version": "0.2.1",
  "agent_id": "axr:agent:eco-clean:v5.1",
  "step": { "name": "Calculate Pricing", "kind": "side_effecting" },
  "io": {
    "input_hash":  "sha256:9f2c…b41a",
    "output_hash": "sha256:1d7e…0c88",
    "decision":    "PRICE_CALCULATED"
  },
  "logic_version": "5.1",
  "logic_hash":    "sha256:a3b9…77e2",
  "previous_receipt_hash": "sha256:5c10…d9f3",
  "timestamp": "2026-06-14T08:34:12Z",
  "signature": "ed25519:K8f…/Qb2Aw=="
}

This single block contains everything an auditor needs:

  • The Identity (agent_id): which workflow, at which version, produced the receipt.
  • The Decision (io): hashes of the exact input and output the step consumed, plus the decision it reached.
  • The Code Fingerprint (logic_hash): the SHA-256 hash of the code (or n8n workflow JSON) that actually decided. Change the workflow by a single byte and this hash changes — version labels are testimony, code hashes are evidence.
  • The Chain Link (previous_receipt_hash): binds this receipt to the one before it, so a deleted step breaks the chain.
  • The Signature (signature): the Ed25519 seal over the canonicalised receipt. Edit any field and it no longer verifies.

How to Verify the Audit Log Offline

Because the receipts rely on standard cryptography, anyone holding your public key can verify the integrity of the audit trail offline. They do not need access to n8n, your database, or your network.

The CLI Approach

You can verify a whole log with the zero-dependency AXR verifier — exit code 0 means valid, 1 means a problem was found:

# offline, zero dependencies
node axr-verify.js receipts.jsonl public-key.pem sth.jsonl anchors.jsonl

If even a single byte of a receipt has been modified — or a receipt was deleted from the sequence — the signature no longer verifies and the Merkle root no longer matches the signed tree head, and the verifier exits non-zero.

The Pure-Python Approach

Because AXR avoids proprietary formats, a fully independent verifier exists in pure Python — its own canonicaliser and Ed25519 — and it must agree with the Node verifier byte-for-byte:

python3 axr_verify.py receipts.jsonl public-key.pem sth.jsonl anchors.jsonl

This is the power of “proof over promises” — and the deeper reason agent logs aren’t evidence. By handing your stakeholders a public key and a raw log file, you give them the tools to verify your system’s integrity independently. For the full mechanics, see the offline verification walkthrough.


Step Up Your Workflow Assurance