Run a host of your own
The relay above dies with the terminal. A host is just a relay that never sleeps, so the difference is a service unit, a push allowlist and TLS — not different software. loot serve is in the CLI you already installed.
Start it once by hand
--allow takes a pubkey in either form you can actually hold: the OpenSSH line loot whoami --pubkey prints, or its 64-hex equivalent. Repeat it once per pusher.
loot serve --dir /opt/loot/relay --addr 127.0.0.1:4000 \ --allow "$(loot whoami --pubkey)" # in another terminal — the relay reports the lists it parsed: curl -s http://127.0.0.1:4000/info
/info echoes your key back as 64 hex underallowed_pubkeys. That is the same key in the other encoding, and it is the form to paste into the unit file below — a systemdExecStart splits on whitespace, and the OpenSSH line contains spaces.
⚠ Pass --allow at least once. An empty push allowlist means an open relay: every validly signed push is accepted, and every read is served to anyone who can reach the relay, internal content included, because a relay holds the internal keys (ADR 0011). With a list, only listed keys may read as well as push. It opens the grant gate with it — the grant allowlist defaults to the push one, and --allow-grant replaces that default rather than adding to it. Open is the right posture for a throwaway on 127.0.0.1 and the wrong one for anything reachable; the startup banner names which posture you got, every time. Only omitting the flag opens the relay: an --allow whose key went missing — a truncated paste, an unfilled template variable — refuses to start rather than inverting what you asked for.
Make it survive a reboot
# /etc/systemd/system/loot-relay.service [Unit] Description=loot relay (zero-knowledge push/pull host) After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/loot serve --dir /opt/loot/relay --addr 127.0.0.1:4000 --allow <64-hex> Restart=on-failure RestartSec=2 # The relay never needs to read outside its data dir; keep it contained. NoNewPrivileges=true ProtectSystem=strict ReadWritePaths=/opt/loot/relay ProtectHome=true PrivateTmp=true [Install] WantedBy=multi-user.target
Then systemctl daemon-reload && systemctl enable --now loot-relay. This is the unit that runs relay.millerbyte.com, hardening included — the relay writes nothing outside its data dir, soProtectSystem=strict costs it nothing.
Bind to 127.0.0.1 and terminate TLS in front of it — nginx and Let’s Encrypt, with only nginx exposed. The relay speaks plain HTTP and should never be the public listener.
What you are signing up for
⚠ The store only grows, and has no ceiling. A relay never forgets a change, so everything it holds stays reachable — and nothing reachable may be reclaimed. The only drains are a gc over torn writes and a reap of undelivered grants, which is genuine garbage rather than history. A relay is measured, not capped (ADR 0048): watch the volume, because it will not level off on its own.
⚠ A relay is not blind to everything. It can never read restricted content, or embargoed content before its reveal — those keys are never sealed to it. It can read internal content: those keys ride along so that peers pulling from it get readable files at all (ADR 0011). Run a relay for people you would hand internal files to, or mark what matters restricted.
What this is not: a relay is not the forge. No accounts, no web UI, no browser read path, and no key retirement — a relay gates pushes and reads on its push allowlist alone, so rotating a key on this site does not stop that key pushing to somebody’s relay. Hosting on loot.millerbyte.com is invite-only by construction; this is the path that needs no invitation.