Tester Guide (Pre-Alpha)

Formerly "Swap Key", the internal codename; protocol lineage v3.16 unchanged. This page mirrors docs/TESTER-GUIDE.md from the release package — the package copy travels with the exact build you run.

Welcome, and thank you for testing. This guide takes you from nothing to a completed swap, covers the local UI and the second-device watchtower, and tells you exactly how to report what breaks.

1. ⚠ Limitations banner — read this first, it is not optional

  • TESTNET/REGTEST ONLY — NO REAL FUNDS. Mainnet has no config variant by construction; that stays true until the external cryptographer review. Treat every coin this wallet touches as expendable test money.
  • ~HALF of swap attempts refuse and close through REFUNDS — by design. The role↔CSV pre-commitment fix is deferred to the cryptographer review; the interim convention makes convention-mismatched attempts refuse at the CSV-binding guard, and your funds come back automatically at the refund timelock (roughly 24–36 hours on testnet). A refunded swap is the safety system WORKING. Retry, don't panic.
  • Plaintext TCP peer transport on the MANUAL path — loopback/LAN/VPN interop only; assume anyone on the path can watch the (non-secret) negotiation, and that your partner sees your IP. Tor is now available and removes both (swap --make-onion, or discovery — section 6); Noise over clearnet is still post-pre-alpha.
  • Discovery is TESTNET-ONLY and TOR-MANDATORY, and it does not start out of the box. Finding a stranger on public Nostr relays works — proven live on 2026-07-27 — but the shipped relay list is one short of the signed quorum, so you must add the round's third relay before it will run at all. Section 6 has the exact config and the honest reason. A discovered swap has not yet been observed completing on chain; the meet-and-connect half has.
  • Software key custody — a BIP39/BIP32 encrypted file, not a hardware enclave. Your passphrase and 24-word mnemonic are the whole defense.
  • serve has NO auth — loopback only, and any local process can drive your wallet while it runs. Don't run it on a shared machine.
  • Claim-delay privacy holds are real: after a swap settles you may see holding the SL claim until height N for many blocks. That is the privacy posture working — leave the wallet running.
  • Onboarding delays are real: each newly onboarded coin becomes swappable only after its OWN randomized decorrelation delay, drawn per coin. status annotates each unit with its maturity time and height gate; a swap attempted early refuses cleanly and leaves the coin untouched, so retrying is free. (The current signed manifest sets the testing-period delay; the production-scale 24–72 h value returns after the testing round.)
  • testnet4 REORGS are routine. If a reorg detected: … HOLDING … line appears mid-swap, the wallet is deliberately pausing until the orphaned confirmation settles. A reorg can only ever DELAY an exit, never fire one early. Leave it running.

2. Install

Option A — the release package (switchbitcoin-prealpha-<version>-<git>-<platform>/): verify SHA256SUMS, put the folder somewhere convenient, and use the two binaries directly: switchbitcoin-cli (the wallet) and switchbitcoin-manifest (operator tooling — testers normally never need it). README-FIRST.txt points back here.

Option B — build from source (github.com/abysal32-arch/switchbitcoin; windows-gnu is the verified host): Rust toolchain + a MinGW gcc (Windows), then scripts/build-release.sh — it runs the full test gates, refuses to package a binary with a test trust root, and drops the package under dist/. Linux/macOS: same script, best-effort (untested hosts).

Check what you have — every bug report starts with this line:

switchbitcoin-cli version

It prints the build (crate version + git hash), the pre-alpha banner, and the pinned manifest trust root. Cross-check that root against the key published on the homepage. If you EVER see an UNSHIPPABLE BUILD banner, stop and report it — that binary trusts a public test key.

3. A Bitcoin node

You need your own Bitcoin Core 28 or newer (P2A/TRUC support), synced on testnet4 — or regtest for a purely local rehearsal: two wallets (two data dirs, two configs) on one machine against bitcoind -regtest, same quickstart flow with --network regtest, and you mine your own blocks.

bitcoind -testnet4 -server=1

There is deliberately no hosted RPC option: pointing your wallet at someone else's node hands them your addresses, your timing, and the power to lie to you about confirmations. Pre-alpha testing assumes your own node. Windows note: no -daemon on Windows builds — give bitcoind its own terminal. Cookie auth (the default) is the easiest hookup: point rpc_cookie_file at <bitcoind datadir>/testnet4/.cookie.

4. Quickstart (zero → first swap)

Run switchbitcoin-cli quickstart — it prints the numbered walkthrough (init → address → faucet → onboard → swap ticket) and every wallet command tells you the next one. Highlights and traps:

5. Connectivity (NAT & reachability)

A swap needs a live TCP link between the two wallets. On one machine (loopback) or one LAN this just works. Across the internet the maker (swap --make) runs a listener the taker must reach, and home routers block inbound connections by default.

6. Meeting a stranger (discovery) — TESTNET ONLY, TOR REQUIRED

Sections 4–5 assume you already know your partner: somebody hands you an skt1… ticket and you paste it in. Discovery is the other way in — you find a stranger on public Nostr relays and never exchange a single byte outside the protocol. Both paths are first-class; the manual one is not being retired and is still the lowest-metadata option there is (6.5).

6.1 What it actually is

Nothing about the swap goes near a relay. The relays carry the "want to meet?" notes; everything from the handshake onwards runs over Tor directly between the two wallets, through the same settlement code the manual path uses. The settlement layer cannot even tell it was discovered.

6.2 Before your first run — three prerequisites

1. Tor. Discovery hard-refuses without a bootstrapped Tor, and there is no clearnet fallback, ever. The package does not ship a Tor binary — get the pinned one yourself:

https://archive.torproject.org/tor-package-archive/torbrowser/15.0.19/tor-expert-bundle-windows-x86_64-15.0.19.tar.gz

Verify before extracting — this hash is the one the project measured, and torproject.org's own signed and unsigned sums both agree with it:

6ac067402c7b4a3dc37887ed3754b3914b67fdc220c966190683e9ccf91abf0f

Extract, find tor.exe (it reports Tor version 0.4.9.11), and point the config at it:

[tor]
mode = "external"
exe_path = 'C:\path\to\tor.exe'
socks_port = "19050"
control_port = "19051"

The ports deliberately avoid 9050/9051 so a Tor you already run is never co-opted. If you run two wallets on one machine, give the second one different ports (e.g. 19052/19053) or its Tor refuses to bind. If your network genuinely blocks Tor, bridges and pt_dir are in the config for that — but a Tor that won't bootstrap is much more often a firewall on your own box.

2. Relays — read this, it is the one thing that will stop you. Discovery publishes to every configured relay and needs a quorum of them to accept the write. The quorum comes from the signed manifest (quorum_q, currently 3), never from a flag.

The curated list this build ships holds two relays. That is not an oversight: every free ws:// onion relay on the canonical public list was probed over live Tor three times each, and only two answered — eleven of the thirteen are dead, and paid relays are structurally unusable here forever (they authorize a pubkey, and every offer is signed by a fresh throwaway key, so there is no identity to buy standing for). Re-measured 2026-07-29, unchanged.

So a wallet running the shipped list unmodified refuses to start discovery, loudly and on purpose:

discovery: fewer relays are configured than the signed quorum_q — configure at
least q relay endpoints (the publish quorum could never be met)

For this testing round, add the round's third relay to reach quorum:

[discovery]
relays = "ws://oxtrdevav64z64yb7x6rjg4ntzqjhedm5b5zjqulugknhzr46ny2qbad.onion;ws://gnostr2jnapk72mnagq3cuykfon73temzp77hcbncn4silgt77boruid.onion;ws://bbapfngyskmej5jqfnlhxadx7ir6a3r2nh7yx2wvaokjxc5cipe6uvad.onion"

Two things you are owed about that third one, plainly: it is ours — a small relay run by the project, not an independent public one, disclosed rather than quietly folded into the curated list; and it runs continuously for this round, on one desktop, with no SLA. A supervisor restarts it if it dies and brings it back at logon, so a crash or a reboot heals itself within about half a minute — but a power cut or a network outage at that end does not, because there is no second machine. If your scan reports fewer relays than you configured, the wallet names the one it could not reach (relay unreachable: ws://…); if the named one is ours, that is an outage on our side, not a bug in your wallet. Tell us and try again later rather than filing it.

Adding it makes your list CUSTOM, and the wallet will tell you so. The design's anonymity property is that every wallet publishes to the same list; a distinctive list is itself a fingerprint. For a hand-picked round where everyone runs the same three, that trade is acceptable and stated.

3. testnet4 and a current manifest. Discovery refuses off testnet (discovery operates only against testnet signed params — refusing to start), and an offer whose signed params differ from yours is refused before any contact is made — see 6.4. Check all three at once — this spawns nothing and touches no network:

switchbitcoin-cli discover doctor

It prints your network, the quorum, every configured relay, whether your list is CURATED or CUSTOM, your Tor config, and the gate verdict.

6.3 The walkthrough

One of you offers, the other scans and takes. Neither of you sends the other anything by hand — that is the whole point, so don't paste the offer reference from the maker's terminal into the taker's. The taker must read it from its own scan.

Maker — publish an offer and serve the swap that comes back:

switchbitcoin-cli discover offer --serve --ttl-mins 60 --wait-secs 2400

Tor comes up (about 65 s the first time, ~6 s afterwards), the relays connect (10–25 s over Tor), then a proof-of-work mint — 20 bits, seconds, not minutes — and the wallet waits for a taker.

Taker — look at what's on offer, then take one:

switchbitcoin-cli discover scan
switchbitcoin-cli discover take <offer_ref> --wait-secs 1200

Each acceptable offer prints a 64-hex reference plus its expiry, and you get a tally of what was refused and why. Those refusals are healthy — the scan is supposed to discard most of what it sees. Add --meet-only to the take for a rehearsal that stops at the rendezvous ticket and settles nothing (no coins, no lease) — good for a first run.

From there the take is published, the maker accepts it and arms one ephemeral onion, the reply comes back encrypted with the skt1… ticket inside, and the taker dials that onion over Tor. Allow up to a couple of minutes for the dial — a fresh onion's address has to propagate through Tor's directory first. Once it connects, everything from the handshake on is the ordinary swap you already know, including the ~50 % by-design refund refusal from the limitations banner.

StepExpect
Tor bootstrap, first run / later runs~65 s / ~6 s
Relays connected (3, over Tor)10–25 s
PoW mint + publish (offer, take, or reply)4–8 s
Your take seen by the makerup to ~15 s (the poll cadence)
Rendezvous onion dialable9–46 s, occasionally more

6.4 What leaks, and what never does

A relay operator sees: that an encrypted note of a certain size class exists, roughly when, that it carries proof-of-work, and that it came from a Tor exit. It does not learn who published it (a fresh key each time, over Tor), your home IP, the contents, the ticket, the onion address, or any transaction id. No txid ever touches a relay — enforced by a test that inspects every relay-bound byte of a full swap.

Anyone holding our source sees more than a relay does — this is the part to be clear about. Offers are encrypted to a published key, so the encryption gives uniformity and opacity to a casual observer, not secrecy. Someone with the source can decrypt an offer and read its network, tier, params digest, expiry, and its throwaway maker key. That is acceptable because an offer contains nothing private by construction — but do not treat a published offer as confidential. Takes and replies are different: those are encrypted to real per-party keys, and the ticket and onion inside them stay sealed.

Your counterparty learns what the protocol always revealed after you connect — but not your IP address. On the manual path over clearnet, they see it. That is a genuine improvement.

What is weaker than it sounds: the anonymity set. The property that protects you is that every wallet publishes to the same full list, so no one list identifies you — but with a curated population of two (plus the round relay), that set is small today. Treat discovery as unlinkable-in-design and small-in-practice for now.

Never on a relay, and enforced by test: the ticket, the onion, any txid, any escrow outpoint, your session id, your IP.

6.5 The manual path is still the zero-metadata option

If you and your partner already have a private channel, use it. Trading an skt1… ticket by hand publishes nothing, anywhere — no relay sees a note, no proof-of-work is minted, no public infrastructure is touched at all. Discovery's job is meeting a stranger; it necessarily leaves a little public residue that the manual path does not. swap --make / swap --take are supported forever. You can also have both: swap --make-onion gives you a Tor rendezvous with a hand-delivered ticket — no relays, no clearnet IP.

6.6 Traps worth knowing before they surprise you

7. The local UI (optional)

switchbitcoin-cli serve

then open SwitchBitcoin-Wallet.html (shipped in the package) in a browser. It talks to http://127.0.0.1:3316 — loopback only, no auth (limitation banner applies). Onboarding, tickets, and live swap state all work from the page; everything the UI does is also in the CLI.

When a backend is connected the page shows a live status strip: the permanent pre-alpha banner with build version, manifest version + id (with a LOUD warning on the fingerprintable v0 baseline), refund reality explained rather than shown as a bare "failed", claim holds rendered as privacy holds until a named block, alarms shown prominently, and the active/max swap count. The page renders /status verbatim — it never invents state. If a surface looks wrong, switchbitcoin-cli status / manifest show say the same thing.

The UI has no discovery or Tor surface yet. /status carries both (a discovery object with the gate verdict and relay-list classification, and a tor object), but this page does not render them, so a wallet whose discovery is refused looks identical in the UI to one that is fine. That is a gap in the page, not in the wallet — use switchbitcoin-cli discover doctor and switchbitcoin-cli diag for discovery state until the page catches up.

8. Signed parameter manifests

Settlement parameters arrive on a signed, versioned manifest — never from your config file. Two commands matter to a tester:

9. Backup, restore, and the second-device watchtower

This is the best fund-safety demo you can run (order matters — rehearsed end-to-end on the shipped package, 2026-07-23):

  1. Start a swap on the primary and kill it mid-swap: once the setup txids have printed (funding is on the wire), Ctrl-C / kill the process. The "device" is now dead with a funded escrow.
  2. switchbitcoin-cli backup wallet.skbak on the dead primary. The order is load-bearing twice over: backup refuses while the wallet is running, and the bundle must POSTDATE the swap — the pre-armed refund is minted at negotiate time, so a bundle taken before the swap has nothing to guard.
  3. Move the bundle to a second device (or second directory + own config), switchbitcoin-cli restore --from wallet.skbak.
  4. switchbitcoin-cli watch on the second device — it prints watchtower armed: guarding 1 escrow(s). If it says no guardable swaps in the store, your bundle predates the swap: re-backup, re-restore.
  5. Watch the tower fire your pre-armed refund at CSV maturity — funds come back with the primary dead (dead-device refund FIRED, then on a later pass pre-armed refund CONFIRMED — escrow reclaimed, then the tower stands down by itself).

What a watchtower can and cannot do: it can only ever broadcast your own pre-armed refund (single-signed at negotiate time, pays your own key) and a CPFP fee-bump of that refund — it holds no session key, never signs completions, never negotiates. It cannot steal and cannot grief; two devices firing the same refund is idempotent (same bytes, same txid).

10. Troubleshooting (keyed to the real strings)

You seeIt means / do this
swept escrow carries the wrong refund CSV (extract-and-race guard); refund The ~50% role/CSV convention mismatch (banner item 2). The swap closes through refunds. Retry a fresh swap.
swap routed to the refund exit: … then refund path resolved Normal refund closure. Funds return at CSV maturity; leave the wallet running (or recover later).
holding the SL claim until height N Privacy hold, not a hang. Keep running; on regtest, keep mining.
reorg detected: … HOLDING … A testnet4 reorg orphaned a confirmation your live swap depends on. The wallet holds until it re-confirms — the safety system working. Keep running; report only if it never clears.
another process holds this swap store (single-instance) Two processes on one data dir (often serve + swap, or watch on the primary dir). One wallet process per dir.
wallet onboarding is incomplete — run switchbitcoin-cli init first init never finished (mnemonic retype/Phase-0). Re-run init.
keystore: wrong passphrase or corrupted file Wrong passphrase (retype) — or real file damage: restore keystore.bin from backup / init --restore from the mnemonic.
this command needs a node: add a [node] section to switchbitcoin.toml Fill [node] in the config (section 3).
deposit not found or not confirmed — wait for a confirmation The faucet tx hasn't confirmed, or the <txid:vout> is wrong (vout is the output INDEX paying your address).
coins become leasable after their decorrelation delay … / timelock/deadline invariant violated: … onboarding delay The onboarding privacy delay. NOT a bug and nothing is stuck (no lease, no broadcast) — status shows each unit's maturity; wait and re-run swap.
handshake: peer runs different signed params (manifest mismatch) You and your partner are on different manifest versions. Both run manifest show, ingest the current manifest, retry.
manifest REFUSED: … signature does not verify The file isn't signed by the pinned operator root (corrupt download, wrong file, or the retired-key v1). Re-fetch the round's manifest.
ALARM (manifest open): ProvisionalFallback/RollbackDetected… The stored manifest was tampered/rolled back and quarantined; the wallet fell back to the compiled baseline. Re-ingest the current manifest and REPORT IT.
no taker reached … before the accept timeout / could not reach the peer at … Reachability (NAT/firewall) — section 5. Nothing was locked; fix the address and retry.
chain reconcile failed — fix the node/data dir before swapping The node is unreachable/out of sync. Fix [node]/bitcoind first; refusing to swap in that state is deliberate.
FEE WEATHER WARNING: live N sat/vB exceeds baked … NOT a hang and NOT an error — the swap proceeds; if a Setup or settlement then stalls, the reserve-CPFP backstop fee-bumps it automatically. On testnet4 this is usually spurious — an estimator with no data, not congestion. Note it, don't stop for it.
ALARM — RefundStalledBelowFeeFloor: … Congestion + no usable reserve for the fee bump. The refund still fires; only confirmation waits. If it names the watchtower device, onboard a deposit there.
standing down (completion-supersedes) The counterparty's completion confirmed, so the refund is moot. Correct behavior.
ALARM: … quarantine / unreadable swap record A sealed record failed authentication. ALWAYS report this one with diag output.
error: unknown flag … Typo protection — flags are strict on purpose. switchbitcoin-cli help.

Discovery (section 6) — the exact strings

Most of these are the scan doing its job. A scan pass that refuses things is healthy: it is supposed to discard almost everything it sees.

You seeIt means / do this
discovery: fewer relays are configured than the signed quorum_q … The shipped list is two relays and the signed quorum is three. Add the round's third relay — section 6.2. This is the single most likely thing to stop you.
discovery requires a bootstrapped Tor — refusing to start … No Tor, or it didn't bootstrap. Check exe_path, that nothing else holds your SOCKS/control ports, and that a force-killed wallet didn't strand a Tor there. discover doctor tells you which.
discovery operates only against testnet signed params — refusing to start You're not on testnet. Discovery is testnet-only by construction; settlement and manual peering are unaffected.
discovery: offer's signed params differ from this wallet's manifest — refused before any contact Different signed manifests. Nothing was dialed, nothing leased — the refusal happens before any contact. Both run manifest show, ingest the round's manifest, rescan.
discovery: offer/take/reply has expired in a scan tally Normal. Offers carry a TTL and abandoned ones linger until it runs out.
discovery: wrap does not carry the required proof-of-work … Someone published without paying the PoW. Dropped. Your own mint takes seconds — if yours ever takes minutes, report it.
discovery: this offer is in its take-and-vanish cooldown … Someone took this offer and never showed up. The maker reopens the window after the cooldown. Wait, or take another.
the rendezvous window closed with nobody on it (take-and-vanish #N) — this offer waits 600s The maker's side of the row above. Somebody took your offer and never dialled. Your offer is fine and keeps serving — it just won't open another rendezvous for 10 minutes, so one stranger cannot make you mint onions endlessly. Nothing was leased.
the rendezvous reply reached only N of the 3 relays quorum needs (maker), or no rendezvous reply before the deadline (taker) Known and unexplained — please report it if you hit it. The maker got your take but could not publish its answer to enough relays, so it abandoned that rendezvous. Nothing was leased or is owed on either side; a retry is a fresh take and costs only a new proof of work. We measured this in our own drills: offers reach every live relay reliably, rendezvous replies do not, and we do not yet know why. If it happens to you, note the time and which relays you had configured — that is exactly the data we are missing.
discovery: the reply's ticket does not name a v3 onion … A maker tried to hand you a non-onion address. Refused correctly, and you are not at risk. Report this one — it should not happen between honest wallets.
connect failed (host unreachable …); retry N/12 in 5s Normal for a fresh onion — the address has to propagate through Tor's directory first (9–46 s typical). If all twelve fail you get a clean refusal and nothing was leased.
the rendezvous dial failed: abort to refund … then Nothing was leased and nothing is owed … The maker's onion never came up, or they quit. Free-failure zone: no coins moved. A retry is a FRESH take (new PoW).
A scan finds no offers at all, repeatedly Check the round relay is up (section 6.2 — it is ours and runs during test windows) before assuming a bug. discover doctor first.

The package copy of the guide carries the complete table, including watchtower- and fee-bump-specific lines.

11. Reporting bugs

  1. Run switchbitcoin-cli diag and copy the WHOLE block. It is redacted by construction — no seed, mnemonic, passphrase, or RPC secrets — and names the exact build.
  2. Fill in docs/BUG-REPORT-TEMPLATE.md from the package (what you did, what you expected, what happened, diag output, your Bitcoin Core version, the failing command's full stderr).
  3. Send it through the channel you were onboarded with (pre-alpha testers are hand-picked; there is no public tracker yet).

Wallet output never contains secrets by construction — but if you paste anything else (configs, terminal scrollback from init), CHECK IT: the mnemonic is displayed once during init, and that's on you to keep out of reports.