thread-relay 0.2.0

A self-hostable reference presence relay for the Thread — anyone can run one (presence-wire-v0.1).
Documentation

thread-relay

A self-hostable reference presence relay for the Thread. Run one anywhere and a world can point its presence.relay at it to become shared and multi-user.

Presence on the Thread is federated: a world names its own relay(s) in its manifest, so this is one relay among many — no global chokepoint, and presence keeps working no matter which operator disappears. This binary is the reference anyone can run; the standard is the wire format, not any single server.

Run it

# from source
cargo run -p thread-relay --release

# with config
THREAD_RELAY_ADDR=0.0.0.0:4000 \
THREAD_RELAY_AOI=80 \            # area-of-interest radius in metres (0 = unlimited)
THREAD_RELAY_TICK=15 \          # suggested client send rate, advertised in `welcome`
thread-relay

Clients connect to …/thread/:worldId; occupants are grouped by that world id.

Docker

docker build -f crates/thread-relay/Dockerfile -t thread-relay .
docker run -p 4000:4000 -e THREAD_RELAY_AOI=80 thread-relay

TLS

The relay speaks plain WebSocket (ws://) and stays lean; terminate TLS (wss://) at a reverse proxy. Caddy, two lines:

presence.yourdomain.com {
    reverse_proxy 127.0.0.1:4000
}

Now worlds can use wss://presence.yourdomain.com/thread/<worldId>.

Verify it conforms

The relay certifies against the same checker any browser author would run:

thread-conformance --relay ws://127.0.0.1:4000/thread/plaza

How it scales

  • Area-of-interest culling (THREAD_RELAY_AOI) — a pose is only fanned to occupants within the radius, so a world with thousands present still only exchanges with the handful nearby. This is the main scale lever.
  • Stateless in spirit — the relay holds only ephemeral occupant positions; a restart just means clients reconnect. Nothing durable to lose or back up.
  • Bigger worlds shard by area (run an instance per cell); cross-relay federation for a single huge world is a later addition (the wire is designed to allow it).

Conformance notes

Per presence-wire-v0.1, a conformant relay assigns occupant ids on join, stamps every pose with a server ts, maintains the occupant list, and fans out within area-of-interest. This reference relay does all of that. It runs open (no Passport verification) by default for dev and self-hosting; a production deployment verifies Passports against the issuer's jwks.json (extend on_join, or gate at the proxy).

Identity still crosses the wire in the open posture: the relay reads each presented Passport's sub / name / avatar claims (unverified) and carries them in welcome.occupants[] and a join broadcast to the room, so browsers can fetch co-travelers' descriptors and render them — names and looks — from their own Passports. Anonymous travelers are plain { id }, as before.