Repository navigation
v0.2.0
Pre-releasePre-release: this is a 0.x version and may change without notice.
Added
-
A Linux x86_64 AppImage of the server in the release,
re-psn-vX.Y.Z-x86_64.AppImage, beside the archives: the same binary and
examples as one runnable file, built byscripts/build-appimage.shwith appimagetool 1.9.1 and the type-2 runtime 20251108
(both downloaded by the release workflow and checked against pinned SHA-256 hashes), smoke-tested in the workflow, listed in
SHA256SUMS. Unsigned; needs FUSE or--appimage-extract-and-run. Seedocs/releasing.md. -
docs/img/social.png(1280x640), drawn by the art generator for the repository's social preview. It is uploaded by hand in the
repository settings and no page uses it. -
tools/art/make_art.pydraws the README banner and terminal images (docs/img/) in the gruvbox-material palette
(docs/art/palette.json) with JetBrains Mono pinned by SHA-256. Terminal images come only from transcripts of real runs
(docs/art/transcripts/, recorded bytools/art/capture.sh). CI renders them again and fails on any byte that differs. -
AGENTS.md: the commands, layout and rules for AI coding agents working in the repository. -
A
VERSIONfile with a test that the code, this file and the README agree on it,scripts/bump-version.sh(one bump, every place,
--dry-runshows the diff),scripts/release-notes.sh(notes from this file) and
scripts/check-release.sh, run byscripts/release-build.sh. Seedocs/releasing.md. -
Scores go through a storage interface, and
-db/-db-file(RE_PSN_DB,RE_PSN_DB_FILE) pick the backend. Onlyoff(the default)
andmemoryexist, so scores are still kept in memory; database settings stop the server at start, and the setting is never printed. -
docs/connecting.md: the ways players reach a server (LAN, port forwarding, a public server with-relay, Tailscale, ZeroTier, a
reverse proxy), with diagrams and links to the overlays' documentation. re-psn has not been run over Tailscale or ZeroTier. -
A features list:
docs/features.tsvholds one row per feature with its status and evidence, andtools/features.pygenerates
docs/features.mdfrom it. CI runspython3 tools/features.py --check, which fails on a stale page, a malformed row, or
evidence that does not exist. -
Release archives include an
examples/folder:re-psn.env.examplewith every setting, a systemd unit, and Traefik, nginx and HAProxy
configs that pass the real client address.scripts/check-deploy.shchecks the env file against the server's flags and runs in
scripts/release-build.sh. Only the Traefik config has been run. -
Reverse proxy support:
-trusted-proxiesand-proxy-protocol(PROXY protocol v1/v2 on the game TCP port), and forwarded
headers for the status page log, both off by default. The real client address then counts for-max-conns-per-addr, the
name take-over rule,public_ipand the log. UDP is not proxied. Seedocs/running.md. -
Every flag can be set as a
PSNR_<FLAG>environment variable (a flag wins), and-tcp-addr/-udp-addrgive the two
game sockets their own addresses or ports. A bad value stops the server at start. Seedocs/running.md. -
Invite-only rooms (protocol 0.4):
SET_ROOM_ACCESS,JOIN_BY_CODEandGET_ROOM_ACCESSgive a room an 8-character code and take it
out of search and the status page (which counts "unlisted rooms"). Wrong codes are rate-limited. HELLO_ACK flags bit 1 says the server
has them. Seedocs/api.md. -
Logging with levels:
-log-level(debug, info, warn, error),-log-format(text or json) and-log-file. Every refusal
(connection or request limit, name taken, status page token) is now logged at warn level;-vis the same as
-log-level debug. Tokens and query strings are never logged. Seedocs/running.md. -
One player per name on a server, compared case-insensitively. A HELLO with a
name someone else holds gets ERROR 8 (name taken); from the same IP address
it is the same player reconnecting, and the old connection is closed. The C
client reports the refusal throughpsnr_connect_error(). -
Firewall:
Setup.cmdoffers to add a Windows Firewall rule for the server
(TCP 36100, one UAC prompt);setup.shprints the ufw/firewalld command when
a firewall is on.docs/running.mdcovers the server's and players' rules. -
NAT traversal. The server listens on UDP 36100 too. A player's P2P socket
sends it a PROBE (with a token from HELLO_ACK), and the server records the
public endpoint the probe came from. Member entries now give each viewer the
address that works from where they are: that public endpoint from another
network, the member's LAN address from the same one. Clients built against
0.2 still work; without a probe nothing changes. The C client builds and
reads the packets (psnr_probe_packet,psnr_punch_packet,
psnr_probe_reply,psnr_is_control); the caller sends them from its own
P2P socket. Protocol indocs/api.md, "UDP". -
The relay (
-relay, off by default), for players who can't reach each other
directly. Streams into a player behind a router go through the server
(STREAM_CONNECT / STREAM_OFFER / STREAM_ACCEPT / STREAM_READY), and ROUTE
pushes say which members need it. Datagrams go through the server's UDP
port (RELAY) when punching doesn't get through, as with a symmetric NAT.
HELLO_ACK gains a flags byte saying whether the relay is on. The C client
wraps and unwraps relayed datagrams and opens and accepts relayed streams.
In the lab, the symmetric-NAT scenario now passes, and every scenario also
runs a host-to-joiner stream. -
lab/: a NAT lab in Docker. A psnr server on a pretend internet, headless
players behind NATing routers, and three scenarios: two homes (passes), a
symmetric NAT (fails until psnr has a relay), and two players in one home
(passes, over LAN addresses). CI runs it.
Fixed
docs/running.mdsaid the P2P port is UDP only; titles use TCP on it too.
Changed
- The release workflow builds its notes with
scripts/release-notes.shfrom the CHANGELOG.md entries for the version inVERSION
(ending "Next release: when ready"), and on a tag fails before building unless the tag isv+VERSIONand
scripts/check-release.sh --tagpasses. A manual run still builds everything and publishes nothing. README.mdis rewritten: a fork notice crediting sp00nz, an overview with a session diagram, an example from real local runs,
the available features, install, a settings table checked against the flags, and thePSNR_*migration note.ROADMAP.mdis rewritten around the feature ids (Done, Now, Next, Later, Not planned), keeping upstream's scope section; the
features list gains planned and later rows for storage, high availability, metrics, the apps' themes and the docs, and
tools/features.py --checkalso fails when a roadmap item sits under a section that does not match its status.deploy/README.txtis nowdeploy/README.md(examples/README.mdin the release archives); the docs share one layout, and the deploy and Docker READMEs gain Mermaid diagrams.- The docs use the fork's name, re-psn, and credit sp00nz as the author of the original psnr.
client/psnr.ckeeps its name. - Settings are read from
RE_PSN_<FLAG>variables. The oldPSNR_<FLAG>names still work as aliases: a flag wins, then
RE_PSN_<FLAG>, thenPSNR_<FLAG>. An old name logs a warning at start naming the old and new variable (never the value), and
if both are set and differ the new one is used, with a warning.deploy/psnr.env.exampleis nowdeploy/re-psn.env.example,
with the old names in a comment block;scripts/check-deploy.shchecks both lists and refuses a livedeploy/re-psn.envor
deploy/psnr.env. The compose file, Docker README and proxy examples use the new names. - The server binary is
re-psn(re-psn.exeon Windows): release archives arere-psn-vX.Y.Z-<os>-<arch>(.tar.gz, .zip) with
re-psn-*.sha256files and SHA256SUMS, and the release workflows look for those names. Nopsnrcopy is shipped. The image, the
compose service and theSetup.cmd/setup.shlaunchers (Start re-psn.cmd,start-re-psn.sh) and Windows firewall rule
(re-psn; the oldpsnrrule is removed) use the new name.deploy/psnr.serviceis nowdeploy/re-psn.service: it starts
/usr/local/bin/re-psnand reads/etc/psnr/psnr.env, then/etc/re-psn/re-psn.env(both optional, the later one wins); the
state directory stays/var/lib/psnr.scripts/check-deploy.shchecks the unit's binary and both env paths. The Go module path,
the C API, the UDP magic and the wire format are unchanged. - The architecture and NAT lab diagrams are Mermaid (GitHub renders them), and
docs/architecture.mdgains a diagram of the optional
reverse proxy path and diagrams of a relayed connection (a VPS example) and of how a relayed stream is set up. The text there that said
there is no hole punching and no relay is corrected. - PROXY protocol v2 headers are now checked for family and transport: unassigned values, and a datagram or unspecified transport on the
TCP game port, close the connection (a LOCAL header still carries no client and is consumed to its declared length). The nginx example
is labelled v1 (proxy_protocol on;sends v1), anddocs/running.mdsays Cloudflare Spectrum's PROXY protocol is an Enterprise feature. - The Dockerfile, compose file and
Dockerfile.dockerignoremoved intodocker/, with a README. Build from the repository root with
docker build -f docker/Dockerfile ., or rundocker compose -f docker/docker-compose.yml up -d.lab/run.shand Dependabot follow. - The Docker image no longer forces
-http 0.0.0.0:36101without a token: the status page stays on localhost inside the
container untilPSNR_HTTPandPSNR_HTTP_TOKENare set (the compose file shows how). - Protocol additions that Simpsons Arcade's Matching2 use needs. Clients
built against 0.1.0 no longer match.- CREATE_ROOM and JOIN_ROOM carry the member's own data, and every member
entry returns it. - SET_ROOM_DATA
which2 sets the room's flags, which searchers see. - ROOM_MSG pushes say which member the message was sent to.
- CREATE_ROOM and JOIN_ROOM carry the member's own data, and every member
docker/docker-compose.host.yml: an opt-in compose file for host networking on Linux, for runtimes that rewrite source addresses.docker/docker-compose.ymlkeeps its ports and defaults and gains a commentedRE_PSN_RELAYexample;docker/README.mdsays what relay is and when to use host networking.- The README status line is now a row of badges in the gruvbox-material colours, and
docs/status.pngis a new screenshot of the real
status page (tools/art/capture-status.sh; it is not part oftools/art/make_art.py --check, a browser screenshot is not reproducible).
Security
- UDP PROBE and RELAY packets are accepted only from the IP address of the player's TCP connection, so a stolen or guessed
token, or a forged source address, can no longer point a player's endpoint at a third party. A probe'slocal_ipis kept
only if it is private, carrier-grade NAT, link-local or the probe's own source. Players whose UDP leaves from another address
(some multi-WAN or carrier NAT setups) are refused;-udp-any-source(PSNR_UDP_ANY_SOURCE) turns the check off. See
docs/running.md.
Files
Each archive holds one self-contained server binary (no installer, no runtime needed), LICENSE, NOTICE.md and an examples folder (settings file, systemd unit, proxy configs).
The x86_64 AppImage holds the same Linux binary and examples as one runnable file; it needs FUSE to mount, or run it with --appimage-extract-and-run. It is not signed.
Check a download with sha256sum -c SHA256SUMS.
aa4699afdfb2e71fd9cbb2fc22ee6f74a84706a578838087c5831757616d4936 re-psn-v0.2.0-windows-amd64.zip
8b70d58a0be561188e4259a0243cc09c8bd7d8aa5ee5d40a05c97447f1f46cae re-psn-v0.2.0-linux-amd64.tar.gz
534380e2fae9fe198e6fd5079d8fbe5cfad05acea4b9fbeb6199022d9d273e30 re-psn-v0.2.0-linux-arm64.tar.gz
7b7580f1d5314abdcfba26310f34b46be03bfd3cb6af71b0ece3ca0b82c5b3eb re-psn-v0.2.0-x86_64.AppImage
Developed with AI agent assistance.
Next release: when ready