KKleo

Running a Kleo hub

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. It is ordinary infrastructure, and the whole design is arranged so that it never has to be trusted.

What a hub can and cannot do

It cannot read your mail: messages are sealed to the recipient's key before they are handed over. It cannot forge a profile: profiles are checked against the commitment on-chain. It cannot invent an address for you: endpoint records are signed by the endpoint they describe. It cannot put words in your mouth: everything it serves carries the sender's signature.

What it can do is withhold, delay, and see who talks to whom and when. That is the honest cost of asking someone else to hold your mail, and it is why running more hubs is a contribution rather than a risk: the more there are, the less any one of them sees.

A hub holds no private key of yours and no secret of the network's. Losing a hub loses stored mail that has not been collected. It loses no names, no coins, and no identities.

Before you start

You need a fresh Ubuntu server (2 GB RAM is enough for a hub; the enforcer build wants more like 4 GB or some swap), a domain, and the ability to open ports. Then point four records at the server:

RecordPurpose
example.com, www.example.comthe site, if you want to serve one
hub.example.comthe hub itself, over HTTPS
grpc.example.comoptional: a mainchain view for light clients

and open these ports:

Port
38333/tcpmainchain peers
4002/udpBitNames peers — UDP, because it is QUIC
443/tcpthe site and the hub
50051/tcplight clients, only if you serve them

That 4002 is UDP catches people out. A TCP port check on it will report "closed" on a hub that is working perfectly.

Doing it

scripts/deploy-hub.sh root@your-server example.com

That installs Bitcoin Core and plain-bitnames, builds the BIP300/301 enforcer and kleo-hub, writes the systemd units, and configures Caddy. It is idempotent — run it again to update. It prints what is left to do at the end, which is DNS, ports, and starting the services once the chain has synced.

The one thing it deliberately does not do is create a chain. It joins the Kleo signet. See "Why you are not making your own chain" below.

Then:

ssh root@your-server 'systemctl enable --now caddy'
ssh root@your-server 'systemctl start kleo-enforcer kleo-bitnames kleo-peer kleo-hub'

curl https://hub.example.com/health          # -> ok

Check the thing that matters

If you serve grpc.example.com, check it refuses what it should before you tell anyone the address:

curl -X POST -d '{}' \
  http://grpc.example.com:50051/cusf.mainchain.v1.WalletService/GetBalance

This must answer 403. The enforcer serves WalletService,

MiningService and BlockProducerService on the same port as the

ValidatorService a light client needs, and between them they can spend your coins, mine your blocks and propose sidechains. Caddy is configured to forward one path prefix and refuse everything else. If that check returns anything but

403, stop and fix it.

The proxy is plaintext, because bitnames has no TLS option. For public chain data that is a fair trade: someone in the middle can lie to a light client about the mainchain — which is the trust a borrowed mainchain already implies — and can still neither sign nor spend anything.

Why you are not making your own chain

BitNames is a sidechain, and a sidechain has to be activated by whoever mines the mainchain's coinbase. Whoever creates a signet controls that, which is precisely why Kleo has a signet of its own instead of using the public one.

It also means a second chain is not a second hub — it is a second world. The names on it would be nobody's but yours, and no Kleo pointed at the real chain would ever see them. So deploy-hub.sh joins the existing chain, and refuses a data directory that was built for a different one rather than half-working.

If you do want to stand up an independent network — a fork, a test, your own deployment for your own people — that is docs/SIGNET.md, and it is a longer road with four specific traps in it.

Keeping it alive

systemctl status kleo-bitcoind kleo-enforcer kleo-bitnames kleo-peer kleo-hub
journalctl -u kleo-hub -f

kleo-peer exists because the mainchain arrives over gRPC but sidechain blocks come from BitNames peers. With no peers the node stops learning anything new without complaining, so that service checks once a minute and re-dials.

To update, re-run deploy-hub.sh: it rebuilds the hub from the copied source and restarts nothing you have not changed.

When it goes wrong

Block not found on disk, or Block magic mismatch. The data directory was built for a different signet. A signet's challenge sets its network magic and the magic is written into the block files, so this is not repairable — delete the directory and sync again. The deploy script refuses this case up front, but a hand-run node can still get into it.

Deposits rejected with scriptpubkey. Stock Bitcoin Core will not relay a BIP301 deposit, because it pays to OP_DRIVECHAIN. acceptnonstdtxn=1 is in the generated config for exactly this reason; consensus is untouched.

The BitNames node has no peers and never syncs. Check 4002/udp is open, not 4002/tcp, and that kleo-peer is running.

The node is busy fetching mainchain ancestors that do not exist. It is talking to the public signet's BitNames network — same software, different mainchain. The network magic is what separates them; the units set

--network-magic, so this means it was started by hand without it.