A real model's write, escrowed before it landed
What I can honestly claim here, and only this: I put an escrow membrane in front of a real OpenClaw gateway as a before_tool_call plugin, watched a real LLM's tool call go through it, and confirmed the whole loop end to end, escrow, admit, commit, undo, with a byte-for-byte restore. That's it. I've read 0.077 percent of the OpenClaw source (29 of 37,659 files, counted cumulatively across three separate rounds of this work), the Escalate branch has never fired in a real run, and I haven't found one confirmed example of a ClawHub-distributed plugin using this hook. None of that changes what happened on 2026-09-01. All of it belongs in the same paragraph as the claim, not three screens down where nobody reads it.
The project behind this is gx (TraceFold, Apache-2.0), a layer that treats every effect an agent produces as something you escrow, gate, and can invert, rather than something you log after the fact and hope you can undo by hand. OpenClaw is steipete and vincentkoc's agent runtime, MIT-licensed, npm-distributed (204.8 MB unpacked at 2026.8.1), with a plugin hook called before_tool_call that fires before a tool's execute() runs and can block it outright.
Four ways to fail before you fail correctly
I want to write the failures first, because they're the part that actually shows how the system works.
The id was a filename, not an identifier. gx writes receipts to disk with underscores in the filename (gx1_smxcmcdm...json), because colons aren't safe in filenames on every platform. The identifier gx undo actually wants uses colons (gx1:smxcmcdm...). I copied the filename straight into the undo command and got VALIDATION_ERROR: not a gx1: id. The right string was sitting in an index file two directories over. I hadn't checked.
I trusted --offline to mean less than it means. I wanted a verification that touched nothing but the receipt itself, so I ran gx receipt verify --offline --project <bed> and got back valid:false, inclusion:unanchored. For a second I thought the undo receipt was broken. It wasn't: --offline refuses the project's local ledger too, not only the network. Drop the flag, keep --project, and the same receipt comes back valid:true, anchor:local-ledger. Reading --help first would have saved the round trip.
I guessed the checkpoint key file's shape. gx key list returns a base64 public key. I wrote it straight to a file and pointed --checkpoint-key at it: NOT_FOUND: no key for ed25519-.... There's no gx key export subcommand telling you the expected format, and I didn't dig further, because a fully offline path (no project, no local ledger, checkpoint file only) had already been proven once, in isolation, on a synthetic commit, in an earlier round. I'm recording the gap here rather than closing it.
The plugin's own hook registration had the wrong shape. This one only surfaced because I tried installing the plugin into a real OpenClaw gateway, not because I read the source. register(api) called api.on("before_tool_call", handler, { matcher: { tools: [...] } }), an object. OpenClaw's installer imports the plugin module and calls register() as part of installing it, and it threw: TypeError: tool hook matcher must be an array of tool names. The matcher wants a bare array, not an object with a tools key. I'd guessed that shape during an earlier design pass and never checked it against anything that could reject it. Installing it did that job for me.
What actually happened, in order
With the shape fixed, openclaw plugins install <dir> --link completed, register(api) ran without throwing, and openclaw gateway run came up with 14 plugins listed, gx-escrow sitting next to the bundled ones (anthropic, browser, memory-core, and the rest).
Then, in a session I picked back up after a restart, the actual event: openclaw agent --model deepseek/deepseek-chat, asked to write one specific line to a file, and the gateway log shows it happening.
[plugins] [hooks] running before_tool_call (1 handlers, sequential)
[plugins] hook fires on write -> /tmp/.../workspace/probe.txt
[gx-escrow] hook fires on write -> /tmp/.../workspace/probe.txt
A write tool call from a real model, deepseek-chat, resolved on their end to deepseek-v4-flash, went through before_tool_call, and gx's own receipt files back it up: an Admit verdict, a CommitReceipt, a key id that matches the bed's own signing key, timestamped in the same second as the log line.
I ran gx undo against that transformation. The file came back to line-zero\n, sha256 a221980...cb2d77e. I computed that hash independently with printf 'line-zero\n' | sha256sum before comparing the two, so the match isn't circular. The checkpoint's Merkle tree grew from tree_size:1 to tree_size:2, because undo doesn't erase the earlier commit, it appends a new transformation that happens to invert it. The undo's own receipt answers three of four questions I'd want to ask it (what was read, what was written, when) and is honest about the fourth: "by whose authority" comes back unknown, because the transformation carries the actor and the receipt currently doesn't pull that value across. That's a real gap in the receipt, not a rounding error I'm smoothing over on my way out.
What this is and isn't
This is one round trip, on one machine, through one route, DeepSeek, gateway-mediated, captured once. It isn't a claim that gx catches every agent mistake, or that the Escalate path (the one meant for effects the membrane can't reach) does anything yet, because it has never run for real. OpenClaw's own approval channels, Discord, iMessage, Matrix, and a few others, are a genuinely richer piece of UX than anything gx ships today; the escrow membrane doesn't try to compete with that, it sits earlier, at the point where a signed inverse still exists to write down.
Repo: github.com/TraceFold/tracefold, Rust, Apache-2.0. Not released as a package. A v0.1.0-alpha tag carries one Linux x86_64 tarball, built outside CI.