A little always-on helper
that can never read a thing.
Haven works beautifully with nothing extra. But a relay makes it even more reliable β a small, always-on program that holds your circle's encrypted messages for friends who are offline, and hands them over later. It only ever sees scrambled bytes. It's optional, and it's free to run on hardware you already own.
Think of it as a mailbox
When you post something and a friend is offline, their copy has to wait somewhere until they come back online. A relay is that waiting room: a sealed mailbox that holds the encrypted envelope and delivers it the moment your friend reappears β even if you've gone offline by then.
Every envelope in the mailbox is locked end-to-end to your circle. The relay holds the envelopes; it has no key to open them.
And a switchboard
Sometimes two phones are both online but can't reach each other directly because of how home routers work. A relay can act as a switchboard, passing the encrypted traffic between them β perfect for live calls and big file transfers.
Same rule applies: it forwards sealed bytes it cannot decrypt. It's a pipe, not a reader.
Optional β but lovely to have.
Haven runs fine without any relay. Here's when running one is worth it.
Offline delivery
You and your friend are rarely online at the same time. A relay holds your post until they're back, so nothing's missed.
Better reliability
Behind a stubborn home router, or on a flaky connection? A relay smooths over the rough spots for calls and large files.
Your infrastructure
Run it on an old laptop or a $35 Raspberry Pi and your circle depends on no outside company at all. Fully yours.
Set up your own relay in two commands.
The haven-relay binary does everything: it links to your circle and serves both
jobs β the live switchboard and the sealed mailbox β straight off your machine's disk, over
Haven's own encrypted network. No cloud, no S3, no ports, no domain, no config.
Install it (one line)
On any always-on machine you own β old laptop, Raspberry Pi, home server, free cloud VM:
curl -fsSL https://wemiller.com/apps/haven/relay/install.sh | sh
Grab a relay link from the app
In Haven, go to You β Advanced β Relay β βAdd a relay.β It shows you a relay link
for your circle (it starts with haven-relay://circle#β¦). Copy it.
Go live (paste the link)
On the relay machine, paste your link in:
haven-relay run --link "haven-relay://circle#β¦"
That's it. It prints a QR + the link (so you can re-add it in the app any time), creates
its own identity, makes a ~/.haven-relay/store folder, and starts serving
your circle's sealed mailbox over Haven Net. Leave it running.
Restarting later? Just haven-relay run with no arguments β it remembers your link.
Deep dives, one click away.
Want it always-on, on specific hardware, or hosted in the cloud? Open whichever fits you.
Keep it always-on (a real background daemon)βΊ
macOS β one command (installs a launchd agent, starts at login, auto-restarts):
sh setup-macos.sh # daemonize a relay already on your PATH
sh setup-macos.sh --link "haven-relay://circle#β¦" # attach to your circle, then daemonize
# stop:
launchctl bootout gui/$(id -u)/com.haven.relay
rm ~/Library/LaunchAgents/com.haven.relay.plistLinux β systemd user service (haven-relay.service ships in the relay/ folder):
mkdir -p ~/.config/systemd/user
cp haven-relay.service ~/.config/systemd/user/
loginctl enable-linger "$USER" # keep running after logout
systemctl --user enable --now haven-relay
systemctl --user status haven-relay # the relay's node id is in the logDead-simplest (cron at reboot):
( crontab -l 2>/dev/null; echo "@reboot $HOME/.local/bin/haven-relay run >/dev/null 2>&1" ) | crontab -Self-host on a Raspberry PiβΊ
A Pi is the perfect always-on relay: tiny, silent, and yours. The install script auto-detects your Pi and downloads the right static binary:
- 64-bit Raspberry Pi OS (Pi 3/4/5) β
aarch64-unknown-linux-musl - 32-bit Raspbian (Pi 2/3/4) β
armv7-unknown-linux-musleabihf - Pi Zero / Pi 1 β
arm-unknown-linux-musleabihf
curl -fsSL https://wemiller.com/apps/haven/relay/install.sh | sh
haven-relay run --link "haven-relay://circle#β¦"Then keep it alive with the systemd-user or @reboot cron
recipe above. Because the relay needs no inbound ports, your Pi works behind a normal
home router with zero port-forwarding or firewall fiddling.
Run it on a VPS or a free cloud tierβΊ
Don't want to leave a machine on at home? Any always-on box works, and several are free forever β the relay needs no public endpoint, so even free tiers behind NAT are fine:
- Oracle Cloud Always-Free β $0 forever, a generous Arm VM (use the
aarch64binary). - Fly.io β ~$0 on the free allowance; outbound-only, no public service needed.
- Google Cloud
e2-microβ free in one region; small but plenty for a relay. - A $5/mo VPS (Hetzner, RackNerd, etc.) β if you want a dedicated box. Still no public host required.
Install + run is identical to the easy path. To automate it, drop the
two commands into the host's own provisioning β a cloud-init runcmd:, a Docker
CMD ["haven-relay","run"], or an Ansible play that templates the systemd unit.
Storage backends β local disk, rclone, or S3βΊ
Local disk is the default and needs nothing. If you'd rather park the sealed blobs elsewhere, the relay can use any of rclone's ~70 backends or a plain S3 endpoint:
haven-relay run --link "β¦" # local disk (default)
haven-relay run --link "β¦" --rclone-remote mydrive:haven # any rclone backend
haven-relay run --link "β¦" --s3 --s3-port 8333 # rclone serve s3 of a local dir
haven-relay run --link "β¦" --no-storage # connection relay only
haven-relay run --config relay.json # everything from a fileImportant: rclone owns the provider auth (it reads its own
rclone.conf). Neither the relay nor the Haven app ever holds a provider OAuth or
refresh token. The sealed blobs are encrypted regardless of where they land.
The classic way β self-host or rent an S3 bucketβΊ
The original model: point Haven at any S3-compatible bucket. Spin one up locally with
rclone serve s3 (rclone is MIT-licensed, one cross-platform binary):
curl -fsSL https://wemiller.com/apps/haven/relay/install.sh | sh -s -- --bucket # Docker (any OS)
# or, from the relay/ folder:
sh install.sh --bucket # Docker
sh install.sh --bucket --native # native rclone binary (no Docker)Or rent a managed bucket β you still hold the keys, the provider only ever sees sealed blobs:
- Cloudflare R2 β no egress fees, generous free tier
- Backblaze B2 β very cheap storage
- AWS S3 β the original
Then in the app: You β Advanced β Storage β Custom S3 bucket, paste the endpoint / bucket / keys, and turn on βVolunteer as tribute.β To reach it from outside your home network, expose the port via a router port-forward, Tailscale, or a small VPS β the traffic is already sealed, so plain HTTP is fine (HTTPS is nicer).
Build from sourceβΊ
The binary lives in the monorepo at core/haven-relay. It composes the existing
haven-net (iroh transport, the local-disk blob mailbox, the S3-over-iroh tunnel)
and p2pcore (crypto/identity) crates β it reinvents nothing.
cd core
cargo build --release -p haven-relay # single static binary at target/release/haven-relay
cargo test -p haven-net -p haven-relay # forwarding + blob-store + tunnel + link tests
# cross-compile, e.g. a fully static Linux binary or Windows:
cargo build --release -p haven-relay --target x86_64-unknown-linux-musl
cargo build --release -p haven-relay --target x86_64-pc-windows-gnuExactly what a relay can and cannot see.
Total honesty β because trust is the whole product.
What it can see
- + The public node ids of frames it forwards (routing data that's already public)
- + A random message id and hop count, used only to prevent loops and replays
- + That a connection or upload happened, and the IP that made it (briefly, to move bytes)
- + The opaque byte size of a sealed frame or blob
What it cannot see
- Γ The content of any post, comment, message, or media β all sealed end-to-end
- Γ Any content key or circle secret β relay links carry zero keys, by design
- Γ Which sealed blob is which post, or who's in a circle by name
- Γ Anything that would let it impersonate a member or read history
Your media, on storage you control.
Haven keeps your media in one of exactly two places β both yours, neither ours. A Haven relay (the free app on a PC, Mac, or a Raspberry Pi you leave running β or a friend's) holds it on its own disk, or you point Haven at your own S3-compatible bucket (AWS S3, Cloudflare R2, Backblaze B2, MinIO). Either way your media is end-to-end sealed before it's stored, so the relay or bucket only ever holds ciphertext β and Haven never holds a single one of your credentials.
- A Haven relay β any official Haven app can host it, or run the tiny
haven-relayon a Pi/server. Stores sealed blobs on its own disk; no cloud account. - Your own S3 bucket (S3 / R2 / B2 / MinIO) β endpoint + keys stored only in your device's secure store.
- Run several relays β media is mirrored across them and reads fall back, so a circle survives any one relay going down.
- We can't read, list, or revoke it β we hold no credentials and run no storage, by design.
Be the always-on home
for your circle.
One old laptop. Two commands. A more reliable Haven for everyone you love.