UTXOrder is a client-side, watch-only archive and unsigned-transaction builder for long-term Bitcoin SV (BSV) holdings. It is the hot half of a two-machine cold-storage system: it never holds a private key or a seed.
- Track long-term UTXOs in buckets, with SPV BEEF proofs you can verify against a local header archive.
- Build an unsigned transaction, hand it to an offline signer (e.g. ElectrumSVP) by QR, scan the signed transaction back, and broadcast — the keys never touch this machine.
- Receive, export/import, and print a Cold Holdings Record for offline reference and inheritance.
Everything runs in your browser (IndexedDB / localStorage). No server account, no backend, no telemetry, no keys, no seeds.
- Live: https://utxorder.sendbsv.com
- For real funds, run a verified release locally (see Verifying UTXOrder) —
don't trust a remote host with a funds tool.
localhostis a secure context, so the camera (QR scanning) still works:# after verifying + unzipping a release bundle: python3 -m http.server 8000 # then open http://localhost:8000
Every release is a self-contained static bundle with a GPG-signed SHA256SUMS,
so you can confirm the code you run is exactly what was published — and match it to
the open source in this repo.
1. Import the maintainer release key (the fingerprint published here is the trust anchor):
BSVanon (UTXOrder Release Signing) <support@sendbsv.com>
fingerprint: 1FF8 9BA3 647A C5B2 4BBB 7920 5D7A 63F8 5C14 3F8B
# import the copy shipped in this repo (recommended), or fetch from a keyserver:
gpg --import pubkeys/BSVanon.asc
gpg --recv-keys 1FF89BA3647AC5B24BBB79205D7A63F85C143F8B
2. Verify the manifest signature, then the bundle hash:
gpg --verify SHA256SUMS.asc SHA256SUMS # must say: Good signature (BSVanon)
sha256sum -c SHA256SUMS # must say: <bundle>.zip: OK3. Unzip and run locally (see Run it). The app footer shows the build commit — confirm it matches the release, which is published under a signed git tag:
git tag -v vX.Y.Z # must say: Good signatureIf the signature, the hash, or the commit don't line up, do not use that copy.
web/— Next.js app (App Router,output: 'export'static site). This is all you need to run UTXOrder.web/scripts/build-release.sh— builds a verifiable release bundle +SHA256SUMS.docs/— local-only maintainer notes (ignored from the public repo).
Node.js 20+, npm 10+.
cd web
npm ci
npm run dev # dev server at http://localhost:3000
npm run build # static export -> web/out/
npx vitest run # tests- No keys, ever. UTXOrder is watch-only: it derives addresses from an account
xpub (
m/44'/0'/0', depth-checked) and refuses an xprv. Signing happens on a separate, offline machine. - A scanned signed transaction is cryptographically verified to match the built
plan — same inputs, outputs, fee, and sighash
0x41— before the Broadcast button unlocks. A mismatch is a hard stop. - Online access is limited to fetching proofs/headers, scanning an xpub's addresses, and broadcasting. A failed address query aborts the scan (fail-closed) rather than silently under-reporting a balance.
- For real funds, run a verified release on a host you control.
Watch-only still leaks metadata. Scanning an xpub asks an indexer about every derived address, so an indexer could see your whole wallet by source IP, and any on-path network observer sees the same TLS connections. UTXOrder reduces — but cannot eliminate — this, with three in-app layers:
- Jitter spaces out every request (address scans and proof/BEEF fetches alike).
- Multi-indexer spread. Address lookups are sharded deterministically across two independent indexers (WhatsOnChain + BananaBlocks, with Bitails as a failover), so in normal operation no single indexer sees more than its fixed slice of your wallet per scan. If a provider fails or is rate-limited, UTXOrder fails over to a backup — which then sees a little more than its slice — rather than under-report a balance (correctness over privacy in that edge).
- Address chaff. Each real address is queried alongside decoy addresses — a per-device, slowly-rotating crowd of other people's funded addresses (harvested from recent blocks, never via address lookups) plus random empties — so your real addresses don't stand out, and the crowd is stable across scans (so re-scanning can't subtract it). Each decoy is used at most once per scan; a very large scan can exhaust the crowd, after which its remaining addresses are queried with less (or no) decoy cover.
These are defense-in-depth. What actually protects you matters more:
- Run behind Tor or a dedicated VPN circuit, or point UTXOrder at your own node / local indexer. Source-IP correlation dominates every in-app trick; the network layer is where real privacy comes from.
- Scan less, cache more. Re-scanning the same xpub over and over is the biggest self-inflicted leak. UTXOrder keeps archived proofs locally (IndexedDB), so rescan deliberately — not on every visit.
Outbound hosts. If you place UTXOrder behind a Content-Security-Policy connect-src
or an egress firewall, allow: api.whatsonchain.com, arc.gorillapool.io (fee rate),
bananablocks.com, and api.bitails.io.
Open BSV