A shielded payment, built with the network cut and carried across Murmur:
Mined in testnet block 4,550,000 after one broadcast.
Murmur carries a real shielded Zcash transaction and a sealed note from device to device with no internet. Whoever reconnects first broadcasts it.
Payer writes:
A relay sees:
Merchant reads:
Illustration of one sealed note. The ciphertext shown is a placeholder, not real output.
Three numbers, each traced to a file in the project's evidence folder.
A shielded payment, built with the network cut and carried across Murmur:
Mined in testnet block 4,550,000 after one broadcast.
The transaction crossed the carrier between two processes:
5,222 bytes in, the same SHA-256 out.
Run from the project folder with Node 18.3 or newer:
52 tests pass. One needs a funded wallet.
Why Murmur
Private money shouldn't depend on a network someone can switch off.
Murmur carries a real shielded Zcash transaction and a sealed note from device to device.
Whoever reconnects first broadcasts it, and the payment confirms on chain.
Offline build, sealed notes, spend policy and payment requests: what a payment needs to move without a network.
The payer's wallet creates, proves and signs a shielded transaction with no connection at all. Only the final raw transaction leaves the device, never the partly built file that can hold viewing keys.
See the offline logNotes and receipts are sealed to one recipient. The sender's key is mixed into the key derivation, so a forged sender is rejected. Verified in tests.
Try the simulationCaps per payment and while offline, plus an optional recipient list. The policy bounds double-spend risk. It does not remove it.
See the limitszcash:ztestsapling1…?amount=0.00003&memo=…
A merchant shares a ZIP 321 request. The payer's command reads the address, amount and memo from it, then builds the payment. One was paid on the testnet in block 4,550,881; the memo did not show up on chain.
See the commandsReal commands from the project's CLI. The carrier needs only Node; payments also need the patched zcash-devtool. Testnet only.
Pick a role. The window shows the command that role runs.
Six devices in your browser, with the packet loss you choose. It follows the same flooding, hop limit, duplicate drop and retransmit rules as the real carrier, rewritten for this page. Select a device to see what it can read.
The payer sends the transaction and a sealed note.
Devices forward with a hop limit and retransmit lost packets.
The gateway broadcasts once and the merchant opens the receipt.
Press Send payment to run.
Select a device to see what it can read.
Each bar is 300 simulated runs with four retransmit rounds. A run counts as delivered when the merchant received both sealed packets and the gateway broadcast exactly once.
Run it yourselfLines copied from the project's evidence folder.
{"kind":"sent","txid":"5110a458…","bytes":5222}
Payer · evidence/04
Mined: 4550000 (2026-10-28 6:21:42)
Wallet sync · evidence/04
{"kind":"broadcast","ok":true}
Gateway · evidence/04
probe offline ok · create offline ok · prove offline ok · sign offline ok · combine offline ok · extract offline ok
Network namespace · evidence/03
Failed to sign Sapling spend 0: MissingProofGenerationKey
An early failure, fixed · signing now uses the proven file
{"kind":"receipt","amountZat":1000,"policyHash":"b94caa29…"}
Merchant, receipt opened · evidence/04
1: dns error · 2: failed to lookup address information: Temporary failure in name resolution
Inside the no-network namespace · evidence/05
{"kind":"ack","of":"receipt","from":"fdc4b11c…","sender":"fdc4b11c…"}
Acknowledgement with the sender's key · evidence/05
Three runs, all logged. A builds a transaction offline. B carries one across and confirms it. C pays a ZIP 321 payment request end to end.
The build ran in a network namespace with no connection. A lookup of the wallet server failed there first, as required. Then every step of the transaction build succeeded.
A payer process built the transaction and sent it with a sealed receipt. A separate gateway process received both, broadcast the transaction once and sent back a sealed acknowledgement. Both ran on one machine over UDP loopback, so this run did not use a radio.
cbc99ff1fc8dbabf0ec230ed1d1c021117b37265f7f15c75168e32241bb0b70f
SHA-256 printed by the payer and by the gateway. Identical, so the bytes crossed unchanged.The merchant made a ZIP 321 request. The payer's wallet built the payment with every devtool step inside a no-network namespace (a probe there fails with a DNS error), handed it to a gateway over loopback UDP, and the gateway broadcast it once. The acknowledgement and the receipt carry the sender's key.
The carrier has no dependencies and needs Node 18.3 or newer. From the project folder:
node --test test/*.test.js
A real shielded transaction can be built with the network cut.
evidence/03-acceptance-aThe transaction crossed the carrier unchanged and was confirmed on the testnet.
evidence/04-e2e-loopbackDevices cannot open sealed notes meant for someone else. The sender's key is in the derivation, so a forged sender and old-format packets are rejected.
test/mesh.test.jsA merchant shares a ZIP 321 payment request and the payer's command reads it. One was paid on the testnet and mined in block 4,550,881: built offline, handed over on loopback.
evidence/05-zip321-e2eDelivery under loss: 93% of runs at 30% loss and 72% at 40%, four retransmit rounds, 300 seeds each.
Simulation on the home pageThe memo in a payment request. It was passed to the build, but did not show up in either wallet's view of the confirmed transaction. Whether it was dropped or just not displayed is not determined.
evidence/05-zip321-e2eAn offline transaction expires about 50 minutes after the wallet's last sync: the default expiry is 120 blocks since NU7, at 25-second blocks. One built against a stale tip carried expiry 4,550,721 and was refused at height 4,550,785. Sync before going offline.
Observed on the testnetOffline signing through the devtool's PCZT path cannot spend change notes (SaplingVerify(WrongFvkForNote)). The native send can. The real run paid from an account holding an externally received note.
evidence/05-zip321-e2e/attempt-1-change-note-sign-failureA laptop and a phone exchange a message over a real Wi-Fi link.
scripts/acceptance-b.mdRelaying through several real devices.
Only two devices were availableBluetooth transport. It needs a native app.
Out of scopeSender authentication is not proof that a key belongs to a person, and a relay can still drop, delay or replay a genuine packet.
Threat modelA payer can still spend the same notes elsewhere before broadcast. The spend policy limits the loss. Nothing here makes offline settlement trustless.
Threat modelAnyone watching sees packet sizes, timing and neighbors, and that a shielded transaction exists. Not the payer, the recipient or the amount.
Threat model