Testing Kleo from the terminal
Two people, two wallets, messages going both ways — without touching a window.
kleo-cli drives the same state and the same command functions the app does; sending and registering were lifted out of the Tauri commands so there is one implementation rather than two that can agree with each other while both are wrong. So what this exercises is what the window exercises.
Every command below has been run exactly as written.
cargo build -p kleo-cli
K=./target/debug/kleo-cliPart 1 — regtest
Private to your machine, blocks every ten seconds, throw it away when you are done. This is where feature work belongs.
The chain
scripts/regtest.sh install # once: downloads binaries, builds the enforcer
scripts/regtest.sh start # node 1 on :6002, hub on :7777
scripts/regtest.sh peer # node 2 on :6003, its own wallet and seed
scripts/regtest.sh automine 10 # leave this running, or nothing ever confirmsTwo nodes rather than one because chat is between two people: separate wallets, separate keys. One wallet opened twice proves nothing.
Start the peer on a fresh chain. The second node catches up over the sidechain's own peer-to-peer network, and on a chain that has already run for thousands of blocks it can fail to and give up:
Peer connection error: state error: utxo regular 973536582df3… 0 doesn't existIt does not recover: the node sits at the height it reached, with no peers, while the first node runs on. Everything already registered keeps working, because each node verifies against the chain state it has, but nothing new will ever confirm there. This is upstream's, not Kleo's. The cure is
scripts/regtest.sh reset and starting over — so do peer early, before the chain is long.
Two people
Each instance gets its own directory, which is its own settings, vault and keychain entry:
A=$PWD/.regtest/alice-side
B=$PWD/.regtest/bob-side
$K --kleo-dir $A config --node http://127.0.0.1:6002 --hub http://127.0.0.1:7777
$K --kleo-dir $B config --node http://127.0.0.1:6003 --hub http://127.0.0.1:7777config also turns on following private addresses, because on regtest every address is a loopback one and they are ignored otherwise.
Names
$K --kleo-dir $A register tom
$K --kleo-dir $B register jerryRegistration is two transactions with a block between them — reserve, then reveal — so this prints each stage and waits:
started: Reserving { txid: Txid("973536582df3…") }
Registering { txid: Txid("162ce2fd6d71…"), signing_key: Some(…), encryption_key: Some(…) }
Done { seq_id: "1739-0929" }The wallet lives on the node, not in Kleo, so the coins that pay for this are the ones regtest.sh already put there. Check either side with
$K --kleo-dir $A names.
Where each of them is
$K --kleo-dir $A publish-endpoint tom
$K --kleo-dir $B publish-endpoint jerryThis puts the device's address and a prekey in the name's profile, and commits it on-chain. Until it lands, nobody can reach you — and a Kleo that never published one looks perfectly healthy while nothing arrives.
Talking
$K --kleo-dir $A listen &
$K --kleo-dir $B listen &
$K --kleo-dir $B send --from jerry --to tom --text "hello"
$K --kleo-dir $A send --from tom --to jerry --text "hello back"Each listener prints MESSAGE from <name> as messages arrive, or
UNREADABLE from <name> with the reason above it. Then:
$K --kleo-dir $A history jerry
$K --kleo-dir $B history tomBoth sides should show the same two messages, one me and one them.
Send in both directions. One direction working proves less than it looks: only the side that starts a session echoes the prekey that names the recipient, so a fault in the reply path hides completely until you try it.
Mail for someone who is offline
kill %1 # tom stops listening
$K --kleo-dir $B send --from jerry --to tom --text "while you were out"
$K --kleo-dir $A listen # collects it on startupThe hub held it. It could not read it: the message was sealed to tom before it ever arrived.
Part 2 — the Kleo signet
A shared chain, so names and profiles are the same ones everyone else sees. Blocks come every sixty seconds, so a name takes a couple of minutes rather than twenty — register rob took 1m50s end to end. Still use regtest for feature work, where blocks are ten seconds; use this to check something is real.
A node
scripts/signet.sh light # one daemon; borrows kleoapp.xyz's mainchain
scripts/signet.sh statusA BitNames node reaches the mainchain only through an enforcer's gRPC, so that part can be someone else's while your wallet stays yours. What you lend out is bounded: whoever serves the mainchain can lie to you about the mainchain, and can never sign anything, read a message or touch a name.
The full stack — your own bitcoind, enforcer and node — is
scripts/signet.sh start instead, and syncs from the same place.
An instance pointed at it
S=$PWD/.regtest/signet-side
$K --kleo-dir $S config --node http://127.0.0.1:6004 --hub https://hub.kleoapp.xyz
$K --kleo-dir $S statusblocks should match what the chain is at. If it stays 0, the node has no peers — see below.
Coins
A name costs a little, and the signet's coins are not minted by you. The wallet lives on the node, so seed it and ask for some:
scripts/signet.sh rpc generate_mnemonic # keep this somewhere
scripts/signet.sh rpc set_seed_from_mnemonic '["<the words>"]'
scripts/signet.sh rpc get_new_address # send us thisThen the rest is Part 1 again:
$K --kleo-dir $S register rob # ~2 minutes
$K --kleo-dir $S publish-endpoint rob
$K --kleo-dir $S listen &and anyone else on the chain can write to rob. Names resolve publicly at
https://hub.kleoapp.xyz/v1/names/rob, which is a quick way to see whether a registration landed.
When it does not work
blocks stays 0 and list_peers is empty. BitNames peers over QUIC on UDP 4002, so a TCP check on that port always looks closed even when it is fine. Check the node was not bound to loopback: it has to be reachable.
A message arrives as UNREADABLE. The two ratchets have fallen out of step, which no retry fixes — one side has to start again:
$K --kleo-dir $A reset-session jerry
$K --kleo-dir $B reset-session tomThen send again. sessions shows the ratchet material on either side.
One node stops keeping up and has no peers. The second node has fallen out of sync and will not recover (see above). scripts/regtest.sh reset, then start, peer and register again on the fresh chain.
Nothing confirms, and everything looks healthy. On regtest, automine is not running. On a signet, nothing is bidding for sidechain blocks: the mainchain can advance for hours while the sidechain stands still, and no log says so. scripts/hub/kleo-mine is what does both.
One instance at a time per directory. The app and the CLI both write the vault, and the last writer wins. Stop the window before driving the same instance from a terminal.