Kleo's threat model
What Kleo proves, what it merely claims, and what it leaks. Every promise on the website should be checkable against this document; where the two disagree, this one is right, and the website is a bug.
NETWORK.md describes the machinery this document judges: who talks to whom, what is signed, and what each message looks like on the wire.
What Kleo is
A desktop client for the plain-bitnames sidechain. Kleo holds no keys and performs no cryptography of its own: your node's wallet signs, encrypts and decrypts, and Kleo decides what to ask it and how to report the answer. So the security of anything here rests on three things — your node, your machine, and the chain — in that order.
Who you are trusting
| You trust | For what | If it is dishonest |
|---|---|---|
| Your node | the chain's contents, your keys, every signature check | everything below fails; a lying node can report any key it likes as someone's. Run it yourself, on loopback. |
| Your machine | the vault key in the OS keychain, plaintext on screen | anyone with your unlocked machine reads every conversation. Kleo has no second lock (see gaps). |
| plain-bitnames | Ed25519, ECIES over X25519, the consensus rules | out of Kleo's hands; Kleo adds no cryptography of its own. |
| The chain's miners | that a name's published keys are the owner's | a reorg or censorship attack on the sidechain could withhold or reorder key changes. |
You do not have to trust a hub, a resolver, a relay, or anybody who forwards a message. That is the whole design, and the rest of this document is about how far it actually goes.
What is proved
- A signature identifies a name. Verifying re-reads the signer's key from the chain
via your node and checks the signature over exactly the message text. A key that no longer matches is reported as a mismatch, never as a pass.
- A message can only be read by its recipient. Encryption uses the encryption key
that name publishes on-chain, with the node performing ECIES.
- A profile is the owner's. The owner commits on-chain to the BLAKE3 hash of the
profile's canonical JSON. Kleo re-hashes whatever a server returns and compares. A host can withhold or serve a stale copy; it cannot alter one.
- An endpoint record is the machine's. Address records in a hub's directory are
signed by the endpoint key they describe, and checked on the way out.
- A mailbox deposit cost a BitName. Leaving mail is signed by the sender's on-chain
key; collecting it is signed by the recipient's, over a statement carrying the time, so a captured request goes stale.
- A paid message was paid for. The payment is the message: a transfer to the
address holding the recipient's name, with the message in its memo. The chain is the receipt.
- A channel post is its author's. Signed over a statement naming the channel, the
author and the time, so it cannot be moved, re-attributed or edited.
- A group post is unreadable to a hub. Posts are encrypted under a group key sealed
to each member's BitName.
- A sealed message stays sealed. In a ratcheted conversation the message key is
derived, used and dropped, so neither side's stored state opens what came before it — and the recipient's BitName key opens none of it at all.
- A password message opens only with its password. Argon2id derives the key from
the password and the block's own salt; XChaCha20-Poly1305 encrypts the text, so an altered block fails to open rather than opening wrongly. Nothing about it is published and nothing about it involves a name.
What is only claimed
- That a name is the person you mean. BitNames are first-come, first-served.
alice
is whoever registered it. Kleo pins the keys of saved contacts and flags changes, but it cannot tell you that a name belongs to the human you have in mind — confirm that the way you would confirm a phone number.
- That a hub is honest about what it holds. It can withhold, delay or lose anything.
Nothing tells you it has: a missing message looks like no message.
- That the price a name publishes is one it will honour. Someone can take your
payment and never reply. The payment proves you paid, not that you were heard.
- Any claim inside a profile. A verified profile means the owner published those
words, not that the words are true.
- That an exported transcript is what was said. Kleo keeps the text it verified,
not the armor it verified, so an export is a record, not a proof.
- That a name in the directory is one anybody uses. Lookup lists every registration
the chain holds, including names nobody has claimed in public and rows Kleo cannot put words to. A row is a registration, and nothing more.
What leaks
- Your IP address, to any hub you use, any peer you talk to directly, any relay that
carries a connection, and any profile host you fetch from. Kleo does not bundle Tor or a proxy.
- Who is talking to whom, roughly. A hub holding mail for
boblearns that someone
sent bob something, when, and how big. A direct connection tells the other side your address. Today chat also leans on iroh's default discovery and relays, which are Number Zero's servers unless you configure your own (Settings → Chat hubs).
- That a payment was made, and for how much. Paymail is a transaction on a public
chain; only its memo is encrypted. Amounts and timing are public forever.
- Which BitName you are asking about, to whoever serves the profile you fetch —
unless you use a shared hub, which is why Kleo asks the configured one first.
- Everything about a channel's traffic to the hubs holding it: who posted, when, and
(for broadcast and community channels) exactly what.
- The plaintext of a name is not on-chain — the chain stores
blake3(name)— but
Kleo, hubs and anyone you talk to plainly see the names you use.
What Kleo downloads
Kleo can fetch a BitNames node for you, which means downloading a program and running it. What that rests on, stated plainly:
- The file is checked before it can run. Upstream publishes no checksums
and no signatures, so Kleo carries the SHA-256 of the exact build it was tested against, verifies the bytes in memory, and only then writes them — to a temporary name inside Kleo's own data directory, made executable and renamed into place. A mismatch installs nothing.
- There is no "latest". The version moves when Kleo moves, deliberately:
fetching whatever is newest would mean running a binary nobody here has run and whose hash nobody has checked.
- Upstream's build is still trusted. Kleo checks that it received the
build it expected, not that the build is honest. A compromised release would be a compromised node, and a node holds your wallet.
- The mainchain view may be somebody else's. A node Kleo starts borrows
grpc.kleoapp.xyz unless told otherwise. Whoever serves it can lie about Bitcoin — deposits, withdrawal bundles — and can never sign anything, read a message, or touch a name.
- A hub Kleo runs is not a trusted thing at all. It holds signed records
and sealed envelopes and can read neither, which is why it runs in-process with no extra install and why other people checking it against the chain lose nothing by using it.
Known gaps
These are real, and none of them is hidden behind a setting:
- Forward secrecy only where there is a prekey. A conversation between two names
that publish a chat.prekey runs a Double Ratchet: each message has its own key, so obtaining a BitName key later opens nothing that was already said. Everything else — a name with no prekey, the workbench, files, paid messages, channel posts — is still encrypted to a long-lived key, and someone who obtains that key and kept the ciphertext can read it. The ratchet is Kleo's own implementation of a well-known design, not an audited library.
- A lock is optional, and off by default. Without one, the vault key lives in the
OS keychain and Kleo opens whenever the machine is unlocked. With one (Settings → Lock), that key is wrapped under a passphrase and taken out of the keychain, and locking forgets the key and everything read with it rather than hiding it — so a locked Kleo holds no more than a closed one. What it does not protect: anything already on screen, and a machine compromised while Kleo is open. A backup file is protected the same way, and is only as strong as its passphrase.
- Deleting is local. Deleting a conversation clears your copy. It does not reach
the other side, a hub, or a chain — and a paid message is on-chain permanently.
- Metadata is not defended. No cover traffic, no mixing, no padding. Sizes and
timings are what they are.
- A malicious node is fatal. Kleo's checks are only as honest as the node answering
them. Remote nodes are supported for convenience, not safety.
- Unsigned builds. Until releases are signed and reproducible, a binary you did not
build yourself is a binary you are trusting someone about.
- Blocking is local. A blocked name is never told, and nothing stops them sending;
Kleo drops what arrives, after decrypting it.
- Attachments are written to disk. Files arriving in a conversation are saved under
Kleo's data directory in the clear, so whoever can read your disk can read them.
- A password is as weak as it is. A password-encrypted message is protected by
Argon2id at interactive settings and nothing else — no name, no chain, no second factor. A guessable password is a readable message, and sharing it with one more person is sharing the message with them.
- Activity shows public chain data, including other people's. Paid messages are
payments carrying a memo, so the chain shows that one went by and what it was worth to anyone who looks. Kleo reads none of them it cannot open, but it does not hide that they exist — the chain does not.
- The record of changes is partial, and local. Kleo notices a key rotation or a
new price only by comparing readings, so anything that changed while it was closed is missing, and two changes between two readings look like one. The file holds every name rather than a selection, so it says nothing about who you talk to — but it does record when Kleo was watching, in the clear, in your data directory.
Reporting something
If you find a problem with any claim above, please report it as an issue describing the class of problem. Kleo is pre-release: there is no reward programme and no coordinated disclosure process yet.