Two different roles, and confusing them is expensive.
| what it does | what the network knows about it | |
|---|---|---|
| Relay | accepts sealed packets and hands them to whoever comes for theirs | an address and a key fingerprint |
| Mirror | keeps a public space available while its owner is offline | an ordinary reader-participant |
Relays do not form a network among themselves. Clients form the network on top of relays. Relays don’t know about each other, forward nothing to each other and never synchronize. This is not an unfinished feature — it is the design: the less a relay can do, the less you need to know about it.
Relay
What it can and cannot do
A relay is poste restante. It sees encrypted packets and mailbox addresses that rotate every few hours. It is not a participant: it doesn’t read the contents of private spaces, doesn’t sign events, doesn’t decide who is whose friend, and keeps nothing beyond its term (48 hours by default, 7 days at most).
Which yields the main thing for whoever runs one: losing a relay does not lose the conversation. The durable truth lives with the participants; a relay is only a meeting place.
Set it up
Build for the server:
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags "-s -w" -o terminal-relay ./cmd/terminal-relay
Install the binary, create a service user and a unit:
useradd --system --no-create-home --shell /usr/sbin/nologin quietrelay
install -o root -g root -m 755 terminal-relay /opt/quiet-relay/terminal-relay
[Unit]
Description=Quiet Spaces relay
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=quietrelay
Group=quietrelay
ExecStart=/opt/quiet-relay/terminal-relay --listen :7411 --data /var/lib/quiet-relay
Restart=always
RestartSec=2
StateDirectory=quiet-relay
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/quiet-relay
ProtectKernelTunables=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6
MemoryMax=768M
[Install]
WantedBy=multi-user.target
--data is not a cache
The --data directory holds the relay’s permanent identity key. Its
fingerprint (SPKI pin) is pinned by every client on first connection. Lose
the directory and the relay changes identity — and every client will
refuse to connect. That is intentional: a key change must be a refusal,
not a silent “we connected to something else”.
Keep it in your backups. Permissions 600, owned by the service user.
The fingerprint
The relay prints it at startup:
identity SPKI pin: gk0X84yjjmahVYaUO7snq/a/BecbtX4deDFp7hGkC/c=
Verify it from another machine over the wire, not only from the log: the log says what the process believes, the handshake says what will actually be offered to a client.
terminal relay show-identity YOUR.ADDRESS:7411
After that the fingerprint is compared by eye exactly once. The client remembers it and, if the key ever changes, refuses with a clear message.
Limits
The defaults are guard rails against abuse, not a bandwidth promise: 64
items per mailbox, 1 MiB per item, TTL ≤ 7 days, 4096 connections. All
adjustable by flags (--max-conns, --per-hint-mib, --total-gib,
--collect-rate, --write-rate, --fetch-rate).
The default port is 7411/tcp — open it.
Mirror
Why
A public space lives on a relay in windows, refreshed by its owner’s heartbeat. When the owner stays offline longer than the window, the space stops existing for everyone who hasn’t read it before.
A mirror republishes the owner-signed envelope byte for byte and holds the media it references. It adds nothing to anyone’s rights: it has no space key, can change nothing, and touches nobody’s mailboxes. It adds availability — and only that.
Set it up
A mirror is an ordinary node with keys, so it needs a passphrase.
Never pass it as a command-line argument: systemctl status prints a
service’s command line to anyone, and so does /proc. Only via an
EnvironmentFile readable by root alone.
install -m 600 /dev/null /etc/quiet-mirror.env
# one line inside: QP_PASSPHRASE=…
[Service]
User=quietrelay
Group=quietrelay
EnvironmentFile=/etc/quiet-mirror.env
ExecStart=/opt/quiet-relay/terminal-mirror node --data /var/lib/quiet-mirror --no-lan
Restart=always
StateDirectory=quiet-mirror
ReadWritePaths=/var/lib/quiet-mirror
Add a space:
terminal mirror add --link LINK --data /var/lib/quiet-mirror --passphrase … --seed
terminal mirror list --data /var/lib/quiet-mirror --passphrase …
Run these as the same user as the service. Running them as root
re-creates the directory’s files owned by root, and the service stops
starting with “permission denied”. If that happened:
chown -R quietrelay:quietrelay /var/lib/quiet-mirror.
--seed is consent, not a speed setting
Without it, the mirror holds the bytes but doesn’t answer anyone’s requests. With it, it answers — thereby announcing that it has this file, i.e. that it reads this. For a public catalog you run yourself, that’s exactly what you want; for someone else’s space, it is a deliberate decision.
A catalog pulls in what it references
If the mirrored space is a catalog (a directory of cards), the mirror
will pick up the spaces it lists too: open them, start mirroring them, and
inherit seed. A catalog whose entries are unavailable is not a catalog
being kept available.
Only spaces the node doesn’t already know are touched. mirror remove
clears the flags but keeps the space, so what you removed by hand stays
removed — the automation never re-evaluates what is already known.
By mirroring someone else’s catalog you agree to store and serve whatever its owner adds to it. That is a delegation of judgement, and it deserves to be made consciously.
How much disk
As much as the media of the mirrored spaces weighs, plus a little for
events. A demo catalog of three spaces is about 100 MB. terminal mirror list reports fact, not intent: media 12/14 means “promised to hold
fourteen, holding twelve”.
Worth knowing in advance
Every space has its own meeting place. A public space carries its relay address in a signed policy; the link carries the same. A reader comes exactly there, from whatever relay of their own. Your personal relay and a space’s relay are different things, and that is normal.
A space’s relay is fixed at birth. You can change your personal relay at any moment; the address of an already-created public space is a policy revision, not a setting.
Media does not sit on the relay as files. Events and manifests travel through the relay; bytes are fetched on demand from whoever holds them. That is exactly why a mirror makes sense: it is the one who holds them while the owner sleeps.
Silence is not a diagnosis. A correctly configured relay that nobody
has visited looks exactly like a misconfigured one. Verify with the
fingerprint and terminal relay show-identity, not with the absence of
complaints.