The Kleo manual
From nothing to a signed message, in sixteen steps. The first three are once per machine; after that you are working, and the rest can be read as you need them.
Every command here is real. The regtest stack in step 2 builds a complete local chain, so you can practise without touching mainchain coins.
1. Build and run Kleo
Tagging a release builds installers for macOS, Linux and Windows, but they are not signed yet — so until they are, build it yourself and know what you are running. That takes Rust ≥ 1.85 (edition 2024), Node.js ≥ 20, and Tauri's platform packages (Xcode Command Line Tools on macOS, WebView2 on Windows, and libwebkit2gtk-4.1-dev,
libayatana-appindicator3-dev, librsvg2-dev, libdbus-1-dev, libsoup-3.0-dev and patchelf on Linux — the same list CI installs).
npm install
npm run tauri dev # run it with hot reload
npm run tauri build # bundle for this platform (dmg/app, msi, deb/AppImage)Just looking.npm run devopens the interface in a plain browser against a simulated node — no Rust, no chain. Add?offlineor?noseedto see those states.
2. Point it at a node
Kleo talks to plain-bitnames over JSON-RPC and defaults to
http://127.0.0.1:6002. Open Settings → BitNames node, enter a URL, press Test connection, then Save. The sidebar then shows block height, balance and peers, refreshed every five seconds.
There are two chains you might be on. Regtest is private to your own machine — perfect for trying everything out, useless for talking to anyone else. The Kleo signet is the shared test network: its blocks are made by kleoapp.xyz, which is what allowed the BitNames sidechain to be activated on it at all, and anyone who joins sees the same names, the same profiles and the same hub.
Or let Kleo do it. Node → A node of your own → Set one up for me downloads the BitNames build Kleo was tested against, checks it against a hash Kleo carries (upstream publishes none), puts it inside Kleo's own data directory, and runs it on loopback with its own datadir — then points Kleo at it. It borrows the mainchain view from grpc.kleoapp.xyz unless you say otherwise under Which chain…, where the network, its magic, the mainchain address, the peers and the ports all live.
That is the whole of what Kleo will do for you here. It will not offer to use somebody else's node, because the node holds the wallet.
The scripts below are bash. On Windows they run under WSL or Git Bash; Kleo itself is a native Windows build and does not need either.
For the private one, a single script downloads, builds and runs the whole stack:
scripts/regtest.sh install # once: ~2 min, mostly the enforcer build
scripts/regtest.sh start # funds the enforcer, activates BitNames, deposits 10 BTC
scripts/regtest.sh peer # a second node, so you can test two people
scripts/regtest.sh automine 10 # a block every 10 s, so registrations confirmFor the shared one there are two ways in. The whole stack, if you want to check the chain yourself:
scripts/signet.sh start # Bitcoin Core + enforcer + a BitNames node
scripts/signet.sh status # syncs from kleoapp.xyz; a young chain, so it is quickOr one daemon instead of three, borrowing somebody else's view of the mainchain:
scripts/signet.sh light # just the BitNames node, on :6004A BitNames node reaches the mainchain only through the enforcer's gRPC, so that part can be someone else's while your wallet stays yours. What you lend out is bounded and worth stating plainly: whoever serves the mainchain can lie to you about the mainchain — deposits, withdrawal bundles — and can never sign anything, read a message, or touch a name, because none of your keys are there. Ours is grpc.kleoapp.xyz:50051, and it publishes the enforcer's read-only ValidatorService and nothing else.
Either way, set Settings → BitNames node to the node's address, and both Chat hubs and Public profiles to https://hub.kleoapp.xyz.
Why Kleo has a signet of its own. A BitNames sidechain has to be activated, and activation is a vote cast by whoever mines the coinbase. On the public signet nobody could cast it, so BitNames could never have been switched on there at all. On ours it took six blocks. The chain's identity is its signet challenge, and the BitNames network alongside it runs under its own magic, so the two never meet the public signet by accident. The Kleo signet has the whole procedure.
Why a node of your own, when there is a hub? Because the two hold different things. A hub holds what is already signed — profiles, mail, addresses — so it can be anybody's. A node holds you: signing and decryption happen there, over an RPC that exposes wallet secrets and therefore never leaves your machine.
If it will not connect. plain-bitnames refuses to start its RPC without a healthy enforcer and bitcoind. If your node splits public and private RPC, point Kleo at the private address — wallet and crypto methods live there. The RPC has no authentication and exposes wallet secrets, so keep it on loopback; for a remote node, put it behind an HTTPS proxy and use Kleo's basic or bearer auth.
3. Set up your wallet
Open Your names. If the node's wallet has no seed, Kleo offers to create one: twelve words to write down, then a three-word check before it continues. Restoring an existing wallet takes the same phrase.
Check your backup compares a phrase you have written down against the wallet without changing anything — worth doing the day after, when the handwriting is cold.
There is no recovery. The phrase is the wallet. Lose it and the names registered to it are gone, because nobody else holds the keys. A node's seed can only be set once.
4. Register a BitName
In Your names → Register, type the name you want. Kleo checks whether it is free, then runs the two-block dance: a reservation that commits to the name without revealing it, and a registration in a later block that reveals it and publishes your keys. Progress is shown as it goes, and it resumes if you close the app.
- The name itself never appears on-chain — only
blake3(name). - Registration publishes your signing key and encryption key with the name, so others can find them.
- On regtest, mine or wait for blocks (
scripts/regtest.sh mine 2) for it to complete.
5. Sign something, and verify someone
Open Messaging. It is two panes that never swap places: plaintext on the left, cyphertext on the right. The toggle between them says which way the message is going, and the work happens as you type — there is no button to press, though ⌘/Ctrl+Enter still runs it at once if you would rather insist.
With the toggle on encrypt/sign, type on the left, put your name under From, leave Sign message ticked, and the signed block appears on the right. Going the other way, flip to decrypt/verify and paste into the right-hand pane: Kleo resolves the claimed signer on-chain, compares the key, checks the signature over the exact text, and returns a verdict in a sentence, not a symbol.
Interoperable on purpose. The signature covers only the message text — line endings normalised to LF, trailing whitespace removed — so any other BitNames tool can check it withverify_signature(sig, key, "arbitrary", text). TheSigner:header is only a hint; Kleo re-checks it against the chain.
6. Encrypt to a name
Put a recipient's BitName under To in the same workbench. Kleo looks up the encryption key that name publishes and shows it to you as a chip, written
name (1739-0029) — the name, and the registration the chain knows it by. Leaving Sign message ticked puts your signature inside the envelope, so only the recipient learns who wrote it; their side checks that inner signature automatically.
- Several people at once.
To: bob, carolproduces one block each, joined
together. Send the lot: each of them opens theirs, and Kleo looks past the blocks that are not yours when you paste one in.
- Or nobody in particular. Tick Password and the message is encrypted
to a password instead of to a name — Argon2id and XChaCha20-Poly1305, with the settings carried in the block. Anyone you share the password with can read it, which is the whole point; it proves nothing about who wrote it unless you also sign.
- A name with no published encryption key cannot be written to, and Kleo says which.
- If a saved contact's keys have changed, encrypting is blocked until you review the change.
**Unticking Sign message is a feature.** The result carries no author at all — not an anonymous one, none — so nobody can tell from the message who wrote it.
7. Work with files
Files takes the same four operations to disk. Drop files in, or choose them.
- Sign writes a detached
<file>.bnsig. It covers the file's BLAKE3 hash, so renaming does not break it; verifying reports both whether the signature is good and whether the contents still match. - Encrypt writes
<file>.bnenc. Each file gets a one-off key and only that key is sealed to the recipient, which keeps large files fast. The name, size and any signature travel inside the encrypted part. - Decrypt restores the original name and never overwrites an existing file.
- Checksums writes a BLAKE3
.b3sumsfile in the usual<hash> <name>layout; checking one reports every file as unchanged, changed or missing.
8. Publish a profile
A profile is how a name becomes a phone-book entry: display name, bio, website, links and ways to be paid. The owner commits on-chain to the BLAKE3 hash of the profile's canonical JSON; the document itself is served by any host. Kleo re-hashes what it receives and compares.
Publish from Your names → Public profile…, to a hub or to your own:
kleo-hub --node http://127.0.0.1:6002 \
--listen 0.0.0.0:7777 --public-addr 203.0.113.7:7777
# or push a document to somebody else's hub
kleo-hub publish --node http://127.0.0.1:6002 \
--to http://127.0.0.1:7777 --name alice alice.jsonLooking someone up is private. Web links open in your browser, http and https only, and remote images are never loaded — so reading a profile cannot be tracked. Addresses on-chain are chosen by their owner, so Kleo will not contact loopback or private-network addresses unless you allow it in Settings.
9. Start a conversation
Chat needs one thing first: your device has to be findable. At the bottom of the Chat pane, press Publish it for <your name>. That writes this machine's endpoint into your profile under chat.endpoint, covered by your on-chain commitment, and takes one block like any other profile change.
Then type any BitName into Write to a BitName… and send. Kleo reads their endpoint from their profile, opens an encrypted link straight to their machine, and delivers a message signed by you and sealed to them.
- A message is judged by its signature, never by the link it came in on. An envelope claiming to be someone it is not signed by is shown as unverified.
- A message from a device the sender does not publish still counts as theirs if the signature holds — Kleo just tells you where it came from.
- History is stored locally in the same encrypted vault as your contacts, and a conversation can be deleted outright.
- Forward secrecy switches itself on when both names publish a prekey — which yours does, the moment you publish your endpoint. Each message then gets a key of its own, used once and dropped, so a BitName key obtained later opens nothing that was already said. The header says forward secret when a conversation has it.
When they are not online. Add a hub under Settings → Chat hubs and the message waits there, still sealed, until they collect it. A hub can withhold or delay; it cannot read, alter or forge. Kleo ships with none configured, so nothing is used until you choose it.
10. Run a hub, or use one
A hub is the small server that holds what a direct link cannot: profiles, mail for people who are offline, where a machine can currently be reached, and channel posts. One binary, four roles, each switchable.
kleo-hub --node http://127.0.0.1:6002 --listen 0.0.0.0:7777 \
--public-addr 203.0.113.7:7777 --hub-name myhub --retention-days 30- Leaving mail is signed by the sender's BitName — that is the whole spam barrier, so there are no accounts.
- Collecting it is signed by the recipient's, over a statement carrying the time, so a captured request goes stale.
- A hub is itself a BitName, so it can be looked up and pinned like a person.
Why this is safe to share. Everything a hub serves is signed by the key it describes — profiles by the name's on-chain key, address records by the endpoint key itself, envelopes by the sender. The worst a hub can do is fail to help, which is why Kleo can ship a default list and still let anyone swap it out. Running a Kleo hub puts one on a server.
11. Fund the wallet, and pay by name
BitNames is a sidechain: coins move in by paying a special address from a Bitcoin wallet, and out through a withdrawal bundle Bitcoin settles later. Wallet does both, and pays either an address or a BitName — Kleo looks up where that name takes payments, from its profile or from the chain.
Every payment is quoted first. BitNames addresses are base58 with no checksum, so a mistyped character is still a valid-looking address. Kleo shows the address and the total back — amount plus fee — and sends only what you confirmed. A withdrawal is reported as started, never sent.
12. Charge strangers, or pay to be heard
My PayMail is both halves of this: what people paid to reach you, and the price they have to pay. Set it under Your intake fee and it goes on-chain, so a sender sees it before writing instead of being silently dropped. It is the same number the directory shows on your name — there is only one of it.
Writing to someone who charges, Kleo offers to send the message as the payment: a transfer to the address holding their name, with your message — signed and sealed to them — inside the memo. The cost is quoted before anything is signed. Once they reply, writing is free.
PayMail is for the first message. It is written to the chain and sent to everyone, which is what hides where either of you is, and it costs a fee every time. Once you have their attention, carry on in Messaging: cheaper, and it says less about you.
- Contacts, and anyone you have replied to, are never charged.
- An allow list invites someone through for free, once or always.
- Memos are public and permanent, which is why Kleo encrypts what goes in one.
12b. Run a hub
Settings → Run a hub starts one inside the app. A hub is the opposite of a node: it holds what is already signed — profiles, sealed envelopes, the addresses machines are on — and can read none of it, so the worst it can do is fail to help. That is why one can be anybody's, including yours.
- It listens on
0.0.0.0:7777by default, because a hub exists for other
people to reach. Behind a router that means forwarding the port and publishing that address rather than the one it sees.
- Use it for my own mail and profiles is a separate tick: running a hub and
relying on it are different decisions.
- It runs in-process, so there is nothing extra installed and it stops when
Kleo does.
13. Keep a backup
Your recovery phrase restores the wallet and every name it holds. It restores none of what Kleo keeps privately: who you saved and the keys you pinned for them, who you blocked or invited, the channels you follow, and every conversation. Those live only on this machine until you write them somewhere.
Settings → Backup puts the lot in one file, encrypted with Argon2id to a passphrase you choose — not to this machine's keychain, which is the point: that key never leaves the machine that made it.
- Restoring only ever adds: contacts you already hold keep the keys you pinned here, and messages are matched by id, so importing twice changes nothing the second time.
- A wrong passphrase and an altered file are reported as the same thing, because authenticated encryption cannot tell them apart.
Write the passphrase down. There is no way to open a backup without it, and no way to ask anyone for help: nobody else has a copy of anything in that file.
14. Start a channel
A channel is a BitName. In Channels, turn one of your names into one and choose who may post: only you and the authors you list, members of a private group, or anyone with a BitName.
- Broadcast posts are public and signed, so any hub can hold them and every reader checks them against the chain.
- Private groups seal a group key to each member's BitName and encrypt posts under it. Changing the membership makes a new key, so whoever was removed reads nothing written afterwards.
- Communities are open to any BitName holder, with published moderator and ban lists — a ban is the channel's decision, applied by every hub and reader alike.
Following is nobody's business. Following a channel is local: nothing is published, so a channel never learns who reads it.
15. Look someone up, and watch the chain
Lookup is every registration on the chain, in one list: sort by when it was registered or by name, search, and pick one to see the keys it publishes, the address that controls it, what it charges for a PayMail, and whatever it says about itself. Right-click a row for its away message or to write it a PayMail, and the composer is right there in the card.
Most rows will not have a name on them, and that is the chain being honest: a BitName is stored as blake3(name), so Kleo can only put words to a row when something has told it the words — a name you hold, a contact you saved, or a name you have typed into the search box. Searching for ash hashes it and finds the row; from then on the row says ash.
Activity is the same data read as a log: registrations, paid messages going by, your own transactions and the jobs you started. It decides nothing.
Refresh chain, at the bottom of the sidebar, is what reads the chain. Nothing polls it: reading every name is the heaviest call Kleo makes, so it happens when you ask, and when you open Lookup or Activity. The button says what the reading found — 2 changes · just now, or nothing new.
It also shows what changed — a rotated key, a new profile, a new price — which the chain itself cannot tell you: a name holds its current value and no history at all. Kleo gets it by comparing each reading of the name set with the last one and writing down the differences, so those events carry the time Kleo saw them, and say "seen" rather than pretending to know when they happened. Two things follow, and the feed says both: the first reading is a baseline (a chain that was already there is not news), and nothing that changed while Kleo was closed is in the list. "Watching for changes since …" says how far back the record goes.
Everything else has no time at all — nothing stamps a BitName with one — so it is ordered by where it sits on the chain.
16. Make it look like something else
Settings → Appearance has five: Follow the system (light or dark, as this computer asks), Light, Dark, and two that borrow a Linux desktop's look — Breeze, which is KDE Plasma's (flat, cool grey, a cyan highlight, three-pixel corners), and Adwaita, which is GNOME's (warm off-white, a blue accent, rounder and softly lifted). Both of those follow the system's light/dark preference too, as the desktops they come from do.
The choice lives on this computer only; it is about the window, not the identity, and nothing about it is published.
Next
- How Kleo works — the cryptography, and the flow of a message
- Testing from the terminal — two people, two wallets, both directions
- The network protocol — message by message