KKleo

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 dev opens the interface in a plain browser against a simulated node — no Rust, no chain. Add ?offline or ?noseed to 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 confirm

For 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 quick

Or one daemon instead of three, borrowing somebody else's view of the mainchain:

scripts/signet.sh light     # just the BitNames node, on :6004

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

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 with verify_signature(sig, key, "arbitrary", text). The Signer: 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.

together. Send the lot: each of them opens theirs, and Kleo looks past the blocks that are not yours when you paste one in.

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.

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

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.json
Looking 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.

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

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.

people to reach. Behind a router that means forwarding the port and publishing that address rather than the one it sees.

relying on it are different decisions.

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.

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.

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