Tamper-evident, cryptographically verifiable audit trail for n8n agent workflows

I run a geo-cluster booking workflow on n8n in production (real customer traffic), and I kept running into a question I couldn’t answer cleanly:
after the fact, how do I prove what the workflow actually decided on a given run? The execution log is on my own server — I can edit it, so it proves nothing to a customer or an auditor who doesn’t trust me.

So I built AXR: a way to attach cryptographically signed, chained “receipts” to the decision-relevant nodes in a workflow. Each receipt records what the node received, what it decided, and a signature over
that record. Receipts chain together — delete one and the chain breaks, change a byte and the signature fails. Anyone with the public key can verify the whole log independently, with no access to my server.

It’s a single Code node at the end of the workflow (plus small “AXR Mark” nodes after non-Code nodes like Calendar/HTTP). Zero dependencies, MIT-licensed. The n8n config it needs:

NODE_FUNCTION_ALLOW_BUILTIN=crypto,fs

The part that might be most useful to others here: deploying this on my own live workflow surfaced four pre-existing bugs I hadn’t caught - including one where every run was firing all three response branches
(success + error + conflict) regardless of outcome, so a rejected booking still sent a confirmation email. The signed record contradicted what the workflow was actually doing, which made the bug obvious.

You can verify a log right in your browser (offline, nothing uploaded):

Repo + the n8n integration guide:

Happy to answer questions about the integration. Curious whether others running agents in production have hit the same “what actually happened?” problem — and how you handle it today.

1 Like

The signing-and-chaining approach using the native crypto module is something I hadn’t seen done in n8n before - the fact that receipts chain together means you can detect gaps or edits without any central authority, which is exactly the property you’d need for a customer-facing audit.

One practical note for production deployments: since n8n prunes execution logs by default (EXECUTIONS_DATA_PRUNE + EXECUTIONS_DATA_MAX_AGE), the receipts need to be written to external storage - a Postgres table, object storage, or a Data Table - before the execution record is cleaned up, otherwise the chain survives but the execution context that links it to the run is gone. Worth calling that out in the integration guide if it isn’t already.

1 Like

Welcome @chrisconen!

The chained receipt approach is clever - using the Code node’s crypto module to create a Merkle-style chain means you don’t need any external service, and the fact that deploying this surfaced four real bugs in your booking flow is a strong proof of usefulness. A good next angle if others want to extend this: adding an AXR Mark node right before Error workflow trigger paths so failed runs also get a signed receipt, making the audit trail cover both the happy and error paths.

1 Like

This is a genuinely important production note. You’re right that the receipts have to land in durable external storage before n8n’s pruning runs, otherwise you keep the chain but lose the execution context that ties it to a run. The pilot writes to an append-only JSONL file on a bind-mounted volume (outside n8n’s execution data entirely), but you’ve identified exactly the trap someone using the Data Table or relying on execution history would hit. Adding this to the integration guide - with credit to you.

This is a great extension, and it closes a real gap - right now the happy path and explicit rejections get receipts, but a hard failure (node throws, workflow errors out) leaves no signed record, which is arguably the most important case to have evidence for. An AXR Mark node on the Error Trigger path is exactly the right place to capture it. I’d like to write this up as a documented pattern with credit to you, if that’s alright.