Skip to content

Latest commit

 

History

History
132 lines (102 loc) · 5.62 KB

File metadata and controls

132 lines (102 loc) · 5.62 KB

UTXOrder — BSV Cold Vault + Cold-Signing

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.

Run it

  • 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. localhost is 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

Verifying UTXOrder

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: OK

3. 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 signature

If the signature, the hash, or the commit don't line up, do not use that copy.

Repository

  • 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).

Development

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

Security model

  • 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.

Network privacy

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:

  1. 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.
  2. 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.

License

Open BSV