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.
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.holding the SL claim until height N for many blocks. That is the
privacy posture working — leave the wallet running.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.)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.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 (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.
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.
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:
init shows your 24-word mnemonic exactly once and makes you
retype it, then demands the Phase-0 acknowledgement (type ACCEPT).
Write the words down; the file keystore.bin + your passphrase is the
only other copy of your keys.switchbitcoin.toml and fill the
[node] section, or commands will refuse with this command needs a
node.onboard.onboard confirms, each coin matures after its own randomized
onboarding delay. status shows the unit as
PreEncumbrance/Unspent with a maturity annotation; a swap attempted
before both halves clear refuses cleanly and costs nothing.swap --make <host:port>, sends the printed
skt1… line; taker runs swap --take <ticket>. Both
sides stay running until SWAP COMPLETED or a refund resolution.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.
host:port.
--make 0.0.0.0:9735 binds every interface but the printed ticket cannot
carry a dialable address. Put the address your partner can actually reach in the
host:port you pass.--connect-retries, default 4,
~2 s apart), so the taker can start a moment before the maker's listener is up.
Nothing is leased or funded until both sides are connected and negotiating —
a failed dial is free: fix the address and run again.connection refused → the maker isn't
listening yet. timed out / no taker reached … → the
address isn't reachable from the other side (NAT/firewall).
handshake: peer runs different signed params is NOT a connectivity
problem — see section 7.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.
Settlement parameters arrive on a signed, versioned manifest — never from your config file. Two commands matter to a tester:
switchbitcoin-cli manifest show — what you're running. A fresh
wallet says version 0 with a WARNING: v0 wallets are a small, fingerprintable
anonymity partition. Fix it by ingesting the current round's manifest:switchbitcoin-cli manifest ingest <file> — e.g. the
v2.manifest shipped in the package and
published on this site (v1 was signed by the retired first operator key — since the
2026-07-16 key rotation it refuses with signature does not verify, by
design). Two wallets on different manifests refuse each other with
handshake: peer runs different signed params (manifest mismatch) — when
the operator publishes a new manifest, ingest it promptly.This is the best fund-safety demo you can run:
switchbitcoin-cli backup wallet.skbak (on your main device; refuses
while the wallet is running).switchbitcoin-cli restore --from wallet.skbak.switchbitcoin-cli watch on the second device.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).
watch holds the wallet's single-instance lock — it cannot share
a data dir with a running swap/serve. Second device
(or second dir from a restored bundle) is the design.swap/serve
from a stale restore without manifest ingest-ing the newest signed
manifest first.watch --once does one pass and exits — cron/Task-Scheduler
friendly.| You see | It 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. |
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. |
The package copy of the guide carries the complete table, including watchtower- and fee-bump-specific lines.
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.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).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.