Signing Monero transactions offline isn't easy to begin with, and even the official method is complex and risky enough that it's not really recommended.
But there's another thing about the official flow that's bothered me for a while: it doesn't actually build the transaction offline. The transfer command runs on the view-only wallet, online - destinations and amounts get decided right there. The offline side just signs what's already built. That technically satisfies the definition of "offline signing," but it never sat right with me.
What should "offline" actually mean?What should "offline" actually mean?
To me, the point of offline signing - especially in situations where internet access is restricted, or Monero itself is restricted, or you genuinely need extra anonymity - should be making a transaction using as little of the internet as possible, even if you can't cut it off entirely.
If a wallet has to stay online the whole time (even a view-only one), and everything except the actual signature happens there, "offline" doesn't mean much anymore. I think the wallet should stay offline through every step of signing, not just the last one.
So what does a transaction actually need, live?So what does a transaction actually need, live?
The real question is: what data does a transaction genuinely need pulled live from the chain?
Turns out the only part that needs live blockchain data is decoy selection - and even that doesn't need a wallet. It just needs the global index(es) of the output(s) you're about to spend. Everything else is just the algorithm plus public data from a node, enough to build a 16-member ring.
I pulled that exact logic - the gamma distribution and the handful of constants that shape it - out of Monero's own source (wallet2) and turned it into a standalone tool. You give it the global index of the output you're spending and a node address, and it hands you back a ring.json.
Every other step - picking outputs, building the transaction, signing it - can now happen fully offline.
The toolchainThe toolchain
There's no official way to plug that ring.json straight into a transaction, so I built a second tool, assembler, that takes the ring.json, the destination address and amount, and your offline wallet, and outputs a signed.tx file. From there you just broadcast that file from any internet-connected device - the transaction is on its way.
Four tools total, in order:
- export_outputs + select_transfers (offline) - these require the wallet files to be transferred over right after a full sync, with no activity since (same requirement applies to
assembler), otherwise the same outputs could get selected twice and the network will reject it as a double-spend.export_outputsdumps the wallet's full output history;select_transferspicks which ones to spend. - decoy_tool (online) - only needs a global index and a node address.
- assembler (offline) - takes the ring.json from decoy_tool and signs the transaction.
I've signed and broadcast transactions on stagenet repeatedly this way - all four tools work end to end.
Running these one by one by hand isn't realistic, so I also built lora_bridge_app, a CLI/GUI that automates the whole sequence - you just enter the details. It can move data between the two devices over LoRa (or simulate the exact same flow over TCP if you'd rather test that way). The online side never touches the wallet at all - the only thing it ever gets from the offline device is the global index, nothing else.
Where this standsWhere this stands
One more thing I should be upfront about: this hasn't been reviewed by anyone but me. It's been checked a few times against the source for logic errors, but that review was AI-assisted, not human - so it could genuinely use more eyes before anyone trusts it with real statistical/anonymity guarantees. What's been checked and fixed is written up in each repo's README, under "Review Notes."
I'd especially appreciate someone looking hard at the decoy selection logic. I haven't signed anything on mainnet yet, and I won't vouch for it until I'm fully confident. So: please don't use this on mainnet.
Repos:
- Tools: decoy_tool · assembler
- Everything automated: lora_bridge_app (GUI AppImage is ready to download; CLI needs building from source, README covers that)
If you know the Monero codebase, decoy_tool especially is worth a look - it builds in seconds, so testing or filing a PR if something looks off is easy.
Curious what you all think about the project.