KARST RUN A RELAY ← BACK TO OVERVIEW

FOR OPERATORS

Your machine, carrying other people’s ciphertext.

A relay is one Rust binary. It accepts sealed messages, keeps them briefly, and hands them to whoever holds the key to the box. Below is exactly what it will store, what it will cost you, and the five commands between a fresh machine and this:

The short way

There is an interactive installer. It builds the binary, asks the handful of questions the steps below spell out, writes a service file and preserves your relay-id when you re-run it after a pull.

# clone, then: scripts/install-node.sh

It prints the same relay address and relay-id the banner above shows. Read the rest of this page if you want to know what it did — not because you have to do it by hand.

What your machine will be holding

Hosting other people’s traffic is a commitment you should be able to describe precisely. Here is the whole inventory — not a summary of it.

MANIFEST · EVERYTHING A RUNNING RELAY KEEPS
What lands on the diskHow long it staysCan you read it?
Sealed messages Ciphertext addressed to a drop-box. You hold the envelope, never a key to it. Until fetched, or the TTL sweep Until fetched, or TTL NO — ciphertext
Drop-box addresses Opaque 32-byte labels, blinded per relay and rotating. They do not resolve to a person, and a second operator’s copy of “the same” box looks different. With the message With the message NO — unlinkable
Prekey bundles Public keys clients publish so strangers can start a conversation. Public by design. Until republished, RAM only Until republished YES — but public
The node list Other relays’ addresses and ids. Infrastructure, not users. While running While running YES — public
Your own keypair The relay-id clients pin. Lose it and every client must be told a new one; leak it and someone can impersonate your relay. Permanent — back it up Permanent YES — protect it
Request logs There are none. The request path contains no logging statement, and a test in the repository fails if one appears. Nothing is written NOTHING TO READ

The same thing, by hand

What the installer does, step by step — each one showing what you type and what changes on the machine afterwards.

  1. Build it

    One crate, no services to install, no database.

    # Rust 1.75+ and a C toolchain are the only build deps git clone https://github.com/bytesoffreedom/karst cd karst/impl cargo build --release -p relay

    You now have target/release/relay, a single binary you can copy anywhere.

  2. Start it once, locally

    Run it before you expose anything. The last block it prints is the pair a client needs.

    ./target/release/relay karst-relay … listening on 0.0.0.0:9444 │ relay-id 9f3a1c…c7d1

    The relay-id is stable across restarts — it is derived from a keypair on disk, not from the address. Back that file up.

  3. Tell it where it can be reached

    A relay only advertises itself if it has a routable address to advertise. On loopback or 0.0.0.0 it stays silent rather than publishing something nobody can dial.

    export KARST_RELAY_ADVERTISE=relay.example.org:9444 ./target/release/relay

    Your relay now appears in the node lists of relays it gossips with — and only ever with the address it declares about itself.

  4. Join the network

    Point it at one relay you already know. Discovery spreads from there; each new relay is dialed and asked directly before it is believed.

    export KARST_RELAY_PEERS=other.example:9444@a41f…9b02 ./target/release/relay

    Peers are a starting point, not a trust list. Nothing a peer says about a third relay is stored until that relay says it itself.

  5. Keep it up

    Nothing here needs a scheduler, a cron job or a reverse proxy. A unit file is enough.

    # /etc/systemd/system/karst-relay.service [Service] Environment=KARST_RELAY_ADVERTISE=relay.example.org:9444 ExecStart=/opt/karst/relay Restart=always

    Restarts are cheap: queued mail is in memory by default, so a restart drops what had not been fetched yet. Clients resend; nobody is told a comforting lie about delivery.

Everything you can configure

The whole surface. There is no config file to learn.

KARST_RELAY_ADVERTISE The routable host:port to list yourself as. Skipped when it is loopback or 0.0.0.0 — you are simply not discoverable then.
KARST_RELAY_PEERS addr@relay-id, comma-separated. Seeds the node list clients learn from.
KARST_RELAY_TLS_CERT
KARST_RELAY_TLS_KEY
PEM pair. Setting both serves the WebSocket-over-TLS carrier, so the relay looks like an ordinary HTTPS endpoint. Optional: Noise already encrypts everything underneath.
KARST_RELAY_QUOTA Per-capability request and byte budget. The default is sized for chat; raise it only if you know why.

What it costs you

Said before you start rather than discovered afterwards.

Bandwidth, mostly

Every message is padded to a constant size, so traffic scales with message count, not with what people write. A small VPS carries a lot of conversation.

You are hosting strangers

You cannot read any of it, and you cannot choose what it says. Decide whether you are willing to host content you will never see, in your jurisdiction, before you advertise.

Your posture is a claim

“This relay logs nothing” is true of the code and unprovable from outside. Clients are told to treat it as a claim. Run it in a way you would be comfortable defending.

Your infrastructure may log

The relay writes nothing, but a reverse proxy, conntrack or a provider’s netflow might. That is outside the binary and outside anyone’s ability to verify remotely.