Appearance
Host your own roam server
Host your own, like in the old days: no cloud required. A roam (driving the same place with friends: the phone's Postcards) normally runs through our server. You can run your own instead: nip-roam, one small program with no account, no database and nothing kept. Run it on a computer at home for a LAN party, or on a server for friends far away.
Download
Get nip-roam for your computer from the downloads:
| macOS (Apple silicon and Intel) | nip-roam-<version>-macos-universal |
| Linux, x86-64 | nip-roam-<version>-linux-x86_64 |
| Linux, ARM (a Raspberry Pi 4 or 5 with a 64-bit system) | nip-roam-<version>-linux-arm64 |
| Windows | nip-roam-<version>-windows-x86_64.exe |
SHA256SUMS.txt beside them holds their checksums (shasum -a 256 -c SHA256SUMS.txt --ignore-missing). Use the server from the same release as the game. Each one says which roam protocol it speaks, and a game and a server that speak different ones say which of them to update.
Run it
sh
chmod +x nip-roam-*-linux-x86_64 # (macOS and Linux: make it runnable once)
./nip-roam-*-linux-x86_64On macOS the first start is blocked because the file isn't signed. Right-click it, choose Open, or run xattr -d com.apple.quarantine nip-roam-*. On Windows, run the .exe and allow it on private networks when the firewall asks.
It listens on port 47200 and prints the addresses your friends on the same network can use:
level=INFO msg="nip-roam listening" addr=[::]:47200 version=0.9.3 protocol=1 relay=ws ...
level=INFO msg="players on your network: Postcards, Server: Your own" address=192.168.0.22:47200Stop it with Ctrl+C. When the server stops, the roams on it end.
In the game
- Open the phone's Postcards, then Server, then Your own server, and type its address:
192.168.0.22:47200, or just192.168.0.22(47200 is the default port). The game remembers it. Our server switches back. - Start a roam. On your own server its code comes with the address, for example
K7QM4X@192.168.0.22:47200. C copies both. - Friends choose Join with a code and paste all of it. A code with an address joins on that server, whichever server they chose themselves.
Friends' invites ("Wish you were here") go through our server, so on your own server you share the code and the address yourself.
Your own server needs no friends list and no key. It gets nothing of yours but the name you play under (Me), your game's version and your region, plus everything a roam sends to the others while it lasts. It hands your game a random token of its own, so you can join again if your connection drops.
Over the internet
Friends outside your network reach your server by your public address. Forward TCP port 47200 on your router to the computer that runs it, or run it on a server with a public address. Then tell friends CODE@your.address:47200.
TLS: for the browser
The downloaded game connects over plain WebSocket (ws://) and needs nothing more, on a LAN or over the internet.
The browser player (the site's Play page) can only reach a server with TLS (wss://): a page on https can't open plain connections. For it, give the server a certificate for its name:
sh
./nip-roam -tls-cert /etc/letsencrypt/live/roam.example.org/fullchain.pem \
-tls-key /etc/letsencrypt/live/roam.example.org/privkey.pemEveryone then uses https://roam.example.org:47200 as the server's address, in the downloaded game too: with https:// it speaks TLS, and without it plain. The browser player only reaches the server you open it with: https://nowhereinparticular.pl/play/?roam=roam.example.org:47200 (its Postcards then use that server). A certificate from Let's Encrypt (certbot) is free. An IP address won't do: browsers want the certificate's name.
Behind a proxy that does TLS itself (Caddy, nginx, Traefik), run nip-roam without TLS and tell it the address players use, so the relay's links point there: -public-url https://roam.example.org. The proxy must pass WebSocket upgrades on /roam/v1/relay/.
Limits and load
The server lets in a new roam or a new player only while it has room for them. It measures its CPU, its memory, what it sends a second (bytes and messages) and how late its timers fire, and counts the roams and players, all against limits sized to the computer it runs on (a container's limits where it has them). When it's full, the game says so plainly: "The server is full right now: try again in a few minutes", or "This roam is full". Nobody already in a roam is dropped, and someone whose connection dropped is always let back in. New roams stop at 85 % of each limit (the reserve), so the roams already open have room for friends who join later.
| Flag | Environment | Default | |
|---|---|---|---|
-listen | NIP_ROAM_LISTEN | :47200 | the address and port |
-tls-cert, -tls-key | NIP_ROAM_TLS_CERT, NIP_ROAM_TLS_KEY | TLS (wss), for the browser | |
-public-url | NIP_ROAM_PUBLIC_URL | the address players use, behind a proxy | |
-origins | NIP_ROAM_ORIGINS | the site's | browser pages allowed to use it (*: any) |
-roam-size | NIP_ROAM_ROAM_SIZE | 8 | players in one roam (up to 255) |
-max-rooms | NIP_ROAM_MAX_ROOMS | half the players | roams open at once |
-max-players | NIP_ROAM_MAX_PLAYERS | 64 per CPU, up to 2048 | players in all roams |
-max-cpu | NIP_ROAM_MAX_CPU | 80 | % of the CPUs it may use |
-max-memory | NIP_ROAM_MAX_MEMORY | half the memory | MiB |
-max-out | NIP_ROAM_MAX_OUT | 48 per player | KiB a second it sends |
-max-messages | NIP_ROAM_MAX_MESSAGES | 240 per player | messages a second it sends |
-max-lag | NIP_ROAM_MAX_LAG | 100 | ms its timers may run late |
-reserve | NIP_ROAM_RESERVE | 85 | % of each limit new roams stop at |
Use -1 for no limit. For a small LAN party, -max-rooms 1 keeps it to one roam. A flag given wins over its environment variable. ./nip-roam -help lists them all.
Health: http://<server>:47200/healthz shows the load as JSON: status is ok (new roams welcome), busy (friends may still join the roams open, no new roams) or full (only reconnects), with why, the roams and players, what it measures and its limits. It always answers 200, for monitoring. /readyz answers 503 while it takes no new roams, so a container platform or a load balancer can send new players elsewhere. Roams live in one server's memory: run more than one only with each under its own address.
As a service (Linux, systemd)
ini
# /etc/systemd/system/nip-roam.service
[Unit]
Description=Nowhere in Particular roam server
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/nip-roam
Environment=NIP_ROAM_MAX_ROOMS=4
DynamicUser=yes
Restart=on-failure
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
[Install]
WantedBy=multi-user.targetsh
sudo install -m 755 nip-roam-*-linux-x86_64 /usr/local/bin/nip-roam
sudo systemctl enable --now nip-roam
journalctl -u nip-roam -fIn a container
The source has an image file (services/api/Dockerfile.roam: a static binary on distroless, non-root). Build and run it with Docker, Podman or Apple's container:
sh
docker build -f services/api/Dockerfile.roam -t nip-roam services/api
docker run -d --name nip-roam -p 47200:47200 -e NIP_ROAM_MAX_ROOMS=4 nip-roamOr, without building anything, the downloaded Linux binary on any small image:
sh
docker run -d --name nip-roam -p 47200:47200 -v "$PWD/nip-roam-<version>-linux-x86_64:/nip-roam:ro" \
debian:stable-slim /nip-roamInside a container it reads the container's CPU and memory limits, and sizes its own limits to them.
Privacy
A roam on your server is yours: the game sends your server what it would send ours for a roam, and nothing reaches us. The server keeps nothing on disk, and its log has no players' names. If you run it for people other than your friends, tell them what you do with it. See the privacy policy.