Desktop · macOS, Linux, Windows · MIT
Kleo brings back the best part of GnuPG's Kleopatra — the Notepad, where you sign, verify, encrypt and decrypt text inline — and builds the rest around it: messages, files, a wallet and channels, all addressed by human-readable BitNames on a Bitcoin sidechain, with every check run against your own node.
The app
Nine views, one window. These are real captures of the running interface against a node, not mockups — which is also why the block height differs between them.
Signer hint, and Kleo re-checks it against the chain rather than believing it.
Download
Version 0.1.2, built by CI from the v0.1.2 tag for all three platforms. They
carry no code-signing certificate yet, so your system will object — and it is right to.
Until they are signed, the honest options are to build from source, which takes two
commands, or to open them anyway knowing exactly what that means.
| Platform | File | Getting past the warning |
|---|---|---|
| macOS Apple silicon & Intel |
Kleo_0.1.2_universal.dmg 26 MB |
Right-click the app and choose Open, or run xattr -dr com.apple.quarantine /Applications/Kleo.app. Gatekeeper blocks a double-click because nobody has vouched for it. |
| Linux x86-64, glibc 2.35+ |
Kleo_0.1.2_amd64.deb 16 MB Kleo_0.1.2_amd64.AppImage 89 MB |
Install the .deb, or chmod +x the AppImage and run it. Built on Ubuntu 22.04, so it runs on anything that old or newer. No signature to check either way. |
| Windows x86-64 |
Kleo_0.1.2_x64-setup.exe 8 MB Kleo_0.1.2_x64_en-US.msi 12 MB |
SmartScreen will warn: choose More info, then Run anyway. Either installer works; the .msi suits managed machines. |
Checksums are at /downloads/SHA256SUMS. They tell you the file arrived intact, which is worth having and is not the same as knowing who built it — that needs a signature, and there is not one yet.
shasum -a 256 -c SHA256SUMS
Building it yourself is the only way to know what you are running, and the manual covers it. Signed, reproducible builds are the last thing on the roadmap for exactly this reason.
What it is
Kleo is a desktop client for the plain-bitnames sidechain. It never reimplements the chain: your node holds the keys and does the cryptography, and Kleo is the surface that makes it usable by hand.
Notepad
One text pane addressed by name. Verifying checks the signature and that the key is the one that name publishes right now. Decrypt also checks any signature sealed inside.
⌘/Ctrl+Enter runs the suggested action · Undo for every transformation
Your names
Create or restore a wallet, register a BitName through its two-block commit → reveal, and replace your signing or encryption keys later without losing the name.
12 words to write down, then a three-word check
Contacts
Save a name and Kleo remembers the keys it published. If they change, the contact is flagged, encrypting to it is blocked until you review, and verified signatures say so.
XChaCha20-Poly1305, key in the OS keychain
Profiles
Publish a display name, bio, links and ways to be paid. Kleo re-hashes whatever a server returns and compares it with the commitment on-chain. A host can withhold a profile; it can't alter one.
RFC 8785 canonical JSON · BLAKE3 · bitname_commit
Files
Sign a file into a small .bnsig that travels beside it, encrypt one to a name, or write BLAKE3 checksums for a whole folder.
.bnsig · .bnenc · .b3sums · up to 256 MB
Chat
Write to a name and the message goes straight to their machine over an encrypted peer-to-peer link — signed by you, sealed to them. When they're away, it waits at a hub, still sealed.
iroh QUIC · forward secret · files up to 150 KB · blocking
Channels
Which makes its name unique and costly, and puts its rules — broadcast, private group, or open community — under the same on-chain commitment as everything else. Groups seal a key to each member; a hub holds ciphertext.
published moderators and bans · following is local
Wallet
Receive, pay a name or an address, and move coins to and from Bitcoin. Every payment is quoted before it is signed, and the history says what each transaction was.
deposits and withdrawals through the BIP300 enforcer
Backup
A recovery phrase brings back the wallet and its names. It brings back nothing Kleo keeps privately — contacts, pinned keys, blocks, invitations, conversations. One file carries those, encrypted to a passphrase rather than to this machine's keychain.
Argon2id · restoring only ever adds, never overwrites
Paid messages
Publish what a stranger pays to reach you, and their first message arrives as the payment — the message sealed inside its memo. Contacts and anyone you've replied to are never charged.
upstream paymail · on-chain price · allow list
What it checks
A message, a profile and a chat line are all judged the same way: re-derive the keys from the chain as your own node reports it, then check the signature. Where something doesn't line up, Kleo says so in words rather than showing a padlock. What it proves, what it only claims, and what it leaks is written down in full — including the gaps — in its threat model.
| Outcome | What Kleo says | What it means |
|---|---|---|
| Verified | Good signature from alice | The key is the one that name publishes on-chain, and the text hasn't changed since it was signed. |
| Key mismatch | Claims to be from alice, but it isn't signed with alice's key | Either they rotated keys after signing, or someone is impersonating them. Never shown as trusted. |
| Unknown name | Claims to be from alice, but no such BitName exists | The signature is intact, but nobody has registered that name — so it proves nothing about who wrote it. |
| Bad signature | Bad signature. Do not trust this message. | The text was edited after signing, or the armor is damaged. Ask for it again. |
| Who | Can | Can't |
|---|---|---|
| A profile host kleo-hub |
Refuse to serve a profile, or serve an old one it kept. | Change a field, publish for a name it doesn't hold a signature from, or make Kleo show anything unverified. |
| Anyone, later holding a stolen BitName key |
Read old messages that were encrypted to that key — the Notepad, files, paid messages. | Open a forward-secret conversation: those message keys were used once and thrown away. |
| A hub mail, addresses, channels |
Withhold, delay or lose mail; see who collects it and when. | Read an envelope, forge a sender, invent a channel post, or point you at a machine of its choosing. |
| A chat relay iroh |
See that two endpoints are talking, and drop packets. | Read a message, alter one, or make a message look like it came from someone else. |
| A node you don't run | Lie about the chain, which is why Kleo defaults to loopback. | Be silently trusted: point Kleo at your own node and every verdict is yours to check. |
Manual
Fourteen steps, in order. The first three are once per machine; after that you're working, and the rest can be read as you need them. Every command in it is real — the regtest stack in step two builds a complete local chain, so you can practise without touching mainchain coins.
Start
Rust, Node and Tauri's packages; then a chain of your own in one script, or the shared signet in one daemon.
steps 1–3
Use
Register a BitName, then sign, verify, encrypt and decrypt — text in the Notepad, or files on disk.
steps 4–8
Talk
Publish where you are, write to a name, charge a stranger, keep a backup, start a channel.
steps 9–14
Run a node
Kleo is a client. It needs something to ask — pick whichever suits what you're doing.
Real
Bitcoin Core 31.1 (checksum-verified), bip300301_enforcer built from source, and plain-bitnames 0.17.2 — all in a gitignored .regtest/.
scripts/regtest.sh install | start | demo-names | automine 10 | status | stop | reset
Fast
An in-memory stand-in serving the RPC subset Kleo uses, with the same wire formats, the same cryptography and the same error texts. Four demo names: alice and bob are yours, carol isn't, dave has no encryption key.
cargo run -p kleo-devnode
Yours
Any plain-bitnames node with its mainchain stack running. Point Kleo at its private RPC address and keep that address on loopback.
http://127.0.0.1:6002 by default
A window is a poor place to find out why a message did not arrive. kleo-cli
drives the same state and the same command functions the window does — the sending and
publishing paths were lifted out so both call one implementation, rather than two that can
agree with each other while both are wrong.
cargo build -p kleo --bin kleo-cli
kleo-cli --kleo-dir DIR status # node, names, prekeys, sessions
kleo-cli --kleo-dir DIR sessions # the ratchet material in detail
kleo-cli --kleo-dir DIR history NAME # the stored conversation
kleo-cli --kleo-dir DIR reset-session NAME # drop one that fell out of step
kleo-cli --kleo-dir DIR publish-endpoint NAME
kleo-cli --kleo-dir DIR send --from A --to B --text "..."
kleo-cli --kleo-dir DIR listen # collect mail, print arrivals
--kleo-dir matches the app's KLEO_DIR. For an instance started
without it, pass --config-dir and --data-dir instead. It shares
that instance's vault and keychain entry, so run one at a time per directory — the app and
this both write, and the last writer wins. The first run raises a keychain prompt, because
it is a different binary from the app.
Testing Kleo from the terminal is the whole thing step by step: two people, two wallets, names registered, messages going both ways — on regtest in minutes, or on the signet where it is the real chain.
A hub is the part of Kleo that is awake when you are not: it keeps mail for people who are offline, answers where is this name reachable, stores channel posts, and hands out profiles. Uploads must be signed by the name's current on-chain signing key, which the hub checks against its own node — so only BitName holders can store anything, and it needs no accounts, quotas or moderation.
It is deliberately not something you have to trust. It cannot read your mail, which is sealed to the recipient before it arrives; it cannot forge a profile, which is checked against the chain; it cannot invent an address for you, because endpoint records are signed by the endpoint they name. What it can do is withhold, delay, and see who talks to whom — which is the honest cost of asking someone to hold your mail, and the reason more hubs is a contribution rather than a risk.
One script puts one on a fresh Ubuntu server:
scripts/deploy-hub.sh root@your-server example.com
That installs Bitcoin Core and plain-bitnames, builds the enforcer and the hub, writes the systemd units and configures Caddy for HTTPS. It joins the Kleo signet rather than starting a chain of its own — a second chain would not be a second hub but a second world, where nobody's names resolve but yours. Alongside the JSON-RPC the app uses, it answers a small HTTP API:
GET /health # ok
GET /v1/names/{name} # keys and verified profile
GET /v1/names/{name}/pay # payment methods
GET /v1/commitments/{hex} # a document by its hash
The whole procedure, the ports — note that BitNames peers over UDP, so a TCP check on 4002 will always look closed — and what to do when it goes wrong are in Running a Kleo hub.
Roadmap
Each phase ended in a runnable, tested build, checked against a real plain-bitnames regtest. All twelve are in, plus vault backup, forward secrecy and a lock for the app itself. What remains needs a certificate or a decision, not a design.
Shipped
Node and Notepad, identity and profiles, files, direct messages, hubs, everyday chat, the wallet, paid messages, channels, release engineering.
Since
One encrypted file for everything Kleo keeps privately; a Double Ratchet between names that publish a prekey; a passphrase that takes the vault key out of the keychain.
Next
Signing and notarization, an update channel, reproducible builds — and fees to post in busy channels, plus rotating prekeys on a schedule.
Questions
No — there is no OpenPGP anywhere in Kleo. The hard part of PGP was never the algebra, it was the keyring: finding a key, believing it, noticing when it changed. A BitName answers all three, because the key is published by the name on a chain anyone can read.
You should. The node holds your wallet and does the cryptography, and its answers are what every verdict rests on. Kleo can point at a remote node behind an HTTPS proxy with auth, but then you are trusting whoever runs it to tell the truth about the chain.
The owner publishes new ones. Everyone who has that name saved sees the contact flagged, encrypting to it is blocked until they review, and old signatures start reading “isn't signed with the key they publish now” — which is the warning you want, rather than silence.
Profiles are public documents, so yes — that's their job. What a host can't do is change one: Kleo hashes what it receives and compares with the on-chain commitment, and shows nothing that doesn't match. Messages and files are a different matter; those are encrypted end to end and no host ever sees them.
Yes, if either of you has a hub configured. The message waits there — signed by you, sealed to them — until they collect it. A hub sees that someone sent them something, when, and how big; never what it said, and it cannot forge one. With no hub, both of you have to be online at once.
Writing to you at all costs a BitName, which has to be registered on a chain. On top of that you can publish a price for people you've never spoken to: their first message arrives as a payment carrying the message, and anyone you reply to writes free from then on. Blocking is separate, local, and never announced.
All of it. kleo-hub is one binary that hosts profiles, holds mail, answers where machines are, and keeps channel posts; point Kleo at yours and it never touches anyone else's. The defaults exist so a new install works, not because they are required.
In a conversation where both names publish a prekey, no: each message has its own key, derived, used once and dropped, so the BitName key opens none of it — before or after. Everywhere else — the Notepad, files, paid messages, channel posts — a message is encrypted to a long-lived key, and someone who obtains that key and kept the ciphertext can read it. Kleo says which kind of conversation you are in. The ratchet is Kleo's own implementation of a well-known design, not an audited library, and the threat model says so.
The recovery phrase brings back the wallet and its names. Everything else Kleo holds — contacts, the keys you pinned, blocks, invitations, followed channels, conversations — comes from a backup file you made, encrypted to a passphrase. Without one, the names come back and the history does not.
For anything addressed to a BitName, whatever plain-bitnames uses, done by your node: Ed25519 signatures with the arbitrary domain separator, ECIES over X25519 with AES-256-GCM, and BLAKE3 for hashing. Kleo adds three things its node cannot do for it: XChaCha20-Poly1305 for the local vault, Argon2id for a backup's passphrase, and the message ratchet — X25519, HKDF-SHA256 and XChaCha20-Poly1305 — which exists precisely so that the node's long-lived keys stop being able to open old messages.