Overview
re-psn answers the matchmaking and leaderboard calls of a statically recompiled PS3 game, for a group running its own server: a LAN, a VPN, or friends connecting to one person's machine. The server keeps rooms and leaderboards and tells each player where the others are. Game traffic goes directly between players, and through the server only when two players cannot reach each other and the host has turned the relay on.
It is a fork of psnr by sp00nz: a Go server and a one-file C client, with no accounts and no public instance.
Version 0.6.0 (pre-release). 80 features: 43 available, 3 not yet verified, 9 partial, 15 planned, 9 later, 1 declined.
Unofficial; not affiliated with Sony. No game files, keys or firmware are in the repository or needed from it. Play only games you own.
Features
One row per feature. A status rests on a test or a recorded run in the repository; the features page has the evidence for each row.
Rooms and matchmaking
| Feature | Status | Since |
|---|---|---|
Rooms and search rooms-basicCreate, search, join and leave rooms modelled on sceNpMatching2. A search sees only the rooms of its own title, with each room's external data. Room messages, owner kicks, owner handoff when the owner leaves, and member pushes with each peer's address and P2P port. Upstream work. KICK_MEMBER removes a member but does not prevent rejoining. | available | 0.1.0 |
Room and member data rooms-dataExternal and internal room data (only the owner writes it), room flags that searchers see ( SET_ROOM_DATA which 2), and member data sent with CREATE_ROOM and JOIN_ROOM and returned in every member entry.External and internal data are upstream work (0.1.0). Room flags and member data are newer, so clients built against 0.1.0 no longer match. | available | 0.2.0 |
Invite-only rooms rooms-codesAn 8-character code per room ( SET_ROOM_ACCESS, JOIN_BY_CODE, GET_ROOM_ACCESS). The room leaves search and the status page, which counts unlisted rooms. A connection that sends too many wrong codes is closed.Protocol 0.4: HELLO_ACK flags bit 1 says the server has them. Codes are never logged. | available | 0.2.0 |
Private rooms rooms-privateMatching2 room passwords, private slots and join by room ID. A relay-only variant that keeps members' addresses hidden from each other is also wanted. The relay-only variant is not designed yet. | planned | not released |
Leaderboards
| Feature | Status | Since |
|---|---|---|
Leaderboards scores-boardsModelled on sceNpScore: each player's best score is kept, a tie goes to whoever scored first, boards are higher-is-better or lower-is-better, and ranks are read by range or by online ID. Upstream work. Kept in memory: a restart empties every board. Opt-in storage is planned (store-sqlite). | available | 0.1.0 |
Players
| Feature | Status | Since |
|---|---|---|
One player per name players-namesNames are compared case-insensitively. A HELLO with a name held from another address gets ERROR 8 (name taken); from the same address it is the same player reconnecting, and the old connection is closed. No passwords: a name is not authenticated. | available | 0.2.0 |
Friends, presence and invites players-friendssceNpBasic: a friends list (everyone on the same server), presence (online, title, room), invites and join-a-friend, player history and a block list. Not in the server today. | planned | not released |
Optional sign-in players-accountsOptional accounts next to the default of a name per player: a credential per server kept on the player's machine, sign-in through a generic OpenID Connect provider, or with Discord or GitHub. Not started. Needs the opt-in database (store-sqlite) first, and the scope in ROADMAP.md says no accounts or passwords, so that needs changing too. The default would stay a name per player. | later | not released |
Limits
| Feature | Status | Since |
|---|---|---|
Request and connection limits limits-requests-max-rooms, -max-player-rooms, -max-conns-per-addr, -max-req-rate (requests per second per connection) and a size cap on room data. A request over a limit is refused; a connection over the rate is closed. Each refusal is logged at warn level.These limits cover TCP. Main also has bounded UDP admission and drop counters; see the partial net-guardrails row. | available | 0.2.0 |
Logging
| Feature | Status | Since |
|---|---|---|
Log levels and formats logs-levels-log-level (debug, info, warn, error), -v as debug, -log-format text or json, and -log-file to append to a file (mode 0600). Connection and room refusals are logged at warn level; UDP drops use aggregate counters, with one warning per player for a mismatched probe. Tokens, room codes, peer keys and query strings are never logged, at any level (TestLogsHoldNoSecrets drives a session at debug level in both formats).Not checked: no test covers -log-file. The server does not rotate the file. | available | 0.2.0 |
Status page
| Feature | Status | Since |
|---|---|---|
Status page and /api/stats, with a token status-pageA read-only page and JSON on a separate listener, off unless -http names an address (127.0.0.1:36101 keeps it on localhost). -http-token requires a token (Authorization: Bearer, or ?token=), and the server warns at start when the page is reachable from elsewhere without one. Since v0.4.0 the page uses the project palette (dark, with the gruvbox-material light mode), Rooms/Leaderboards tabs, System/Light/Dark and browser-local text size, spacing and contrast-checked custom colours.The page and /api/stats are upstream work (0.1.0); the token is the fork's. | available | 0.2.0 |
Known games by name status-games/api/stats and the status page label rooms and leaderboards with the game's name and serials, for example [Killzone 3 (BCUS98234, BCES01007)], from a small table embedded in the server (four games so far); a game the table does not know keeps its communication ID. The protocol is unchanged.Not in v0.4.0. The table is never fetched at run time. | available | 0.5.0 |
Verified player count status-verifiedEach public room has verified (members whose UDP probe the server accepted on this connection) and the top level has verified_players; the page shows '(N verified)' beside a room's member count. The protocol is unchanged.Not in v0.4.0. A count of probes the server accepted, not a check of who the players are. | available | 0.5.0 |
Server state in /api/stats status-server-state/api/stats reports the version, whether the relay is on, the payload the relay has carried, the limits in force and the TCP connections refused (per address, request rate). Aggregate only; no address or player. The protocol is unchanged.Not in v0.4.0. Counts are since start. relay_bytes is payload, not interface traffic. | available | 0.5.0 |
Player-reported latency status-latencyHEARTBEAT may carry the player's own last round trip ( rtt_ms, 2 bytes); the server shows it next to each member as reported by the player. The C client and the app's Go client send it once a title opts in (recommended, off by default).Not in v0.4.0. Reported by the player and not verified; display only. The round trip includes the server's own time. | available | 0.5.0 |
Experimental admin API, read-only part admin-apiAn opt-in listener of its own ( -admin, -admin-read-token-file, TLS for a non-loopback address) with one route, GET /admin/v1/state, for a read token: version, uptime, relay, limits, TCP and UDP refusal counts and the ban count (always 0 for now), no address or player. Failed requests get a growing capped delay and one fail2ban-shaped log line; the token file is read again on SIGHUP.Experimental, first in v0.6.0. Read-only: no action, no ban, no limit change, and no app view yet; those are separate changes. Not run against a reverse proxy, on Windows, or on a container secret mount. | partial | 0.6.0 |
Metrics
| Feature | Status | Since |
|---|---|---|
Metrics endpoint metrics-endpoint/metrics in the Prometheus text format, off by default, with the status page's token rules: connections, rooms, refusals, UDP probes and relay traffic. No names, addresses, room codes or tokens in any label.Not started. Needs the counters from net-guardrails first. | later | not released |
Grafana dashboard and alerts metrics-dashboardExample Prometheus scrape config, alert rules and a Grafana dashboard in deploy/, written from the real metric names.After metrics-endpoint. | later | not released |
Reverse proxy
| Feature | Status | Since |
|---|---|---|
PROXY protocol and trusted proxies proxy-support-trusted-proxies (CIDRs or addresses) and -proxy-protocol (a PROXY v1 or v2 header required on the game TCP port from a listed proxy), and forwarded headers for the status page log. The real client address then counts for the limits, the name rule, public_ip and the log.Off by default. UDP is not proxied. See docs/running.md. | available | 0.2.0 |
Deployment
| Feature | Status | Since |
|---|---|---|
Settings file, service unit and Traefik example deploy-examplesdeploy/ holds re-psn.env.example (every setting, commented), a systemd unit and a Traefik config that sends PROXY v2. The release archives carry them as examples/.scripts/check-deploy.sh checks the env file and the unit against the server's flags. The Traefik run (Traefik 3.1 under Podman) is recorded in docs/running.md; no run of the systemd unit is recorded. | available | 0.2.0 |
nginx and HAProxy examples deploy-nginx-haproxyAn nginx stream block with proxy_protocol on; (PROXY v1) and an HAProxy config with send-proxy-v2.Not checked: neither config has been run against the server (docs/running.md says so). | not yet verified | 0.2.0 |
Caddy status-page example deploy-caddyA stock Caddy 2 HTTP reverse-proxy example in deploy/caddy/, copied into future release archives with the other deployment examples. Defaults to loopback and leaves game TCP/UDP direct.Written from the official Caddy documentation; not run or validated with Caddy. No layer-4 plugin is included. The existing HTTP token check is preserved and client-supplied CF-Connecting-IP is removed before forwarding. | not yet verified | 0.4.0 |
Optional fail2ban status-token jail deploy-fail2banA disabled systemd-journal filter/jail example for repeated HTTP status-token refusals. Uses JSON logs and a host TCP status-port action, with setup, firewall placement, validation and recovery instructions. Offline synthetic HTTP/logger and native fail2ban-engine checks do not establish live journal/firewall enforcement. Game TCP/UDP, proxy/CDN and bridged-container enforcement are outside the sample's scope; install and verify fail2ban separately. | not yet verified | 0.4.0 |
Docker and Podman deploy-containerA two-stage Dockerfile and a compose file in docker/, configured only by RE_PSN_* variables. The status page is off until RE_PSN_HTTP is set.A Podman 6.1 run is recorded in docker/README.md, and the NAT lab builds the image with Docker in CI. The v0.2.0 image is pushed to ghcr.io (amd64 and arm64, unsigned; arm64 not run). The server runs as a non-root user and the bases are pinned by digest. Opt-in host-networking compose files, one hardened for a shared test host, are for Linux only. Compose under Podman is not tested. | available | 0.2.0 |
Settings
| Feature | Status | Since |
|---|---|---|
Flags and RE_PSN_* variables settings-envEvery flag can be set as RE_PSN_<FLAG>; the old PSNR_<FLAG> names still work as aliases. A flag wins, then RE_PSN_, then PSNR_. An old name logs a warning that names the variables, never the value. -tcp-addr and -udp-addr give the two game sockets their own addresses. A bad value stops the server at start.No config file yet. | available | 0.2.0 |
Storage
| Feature | Status | Since |
|---|---|---|
Storage interface store-interfaceA storage layer with an in-memory backend, the leaderboards wired through it, and a -db (RE_PSN_DB) setting. Off by default, so nothing changes unless a host turns it on.Only off and memory exist, so scores are still kept in memory and lost on restart; other schemes stop the start. First step of opt-in storage (store-sqlite is next). | available | 0.2.0 |
SQLite storage (opt-in) store-sqliteAn SQLite backend with a pure-Go driver, so the release archives stay static, and migrations built into the binary. With it on, scores survive a restart. The connection string is never logged. Not started. The server's first third-party Go module. Player names and bans would follow with accounts. | planned | not released |
Backup and restore store-backupDocumented backup and restore for each database: a backup command or a copy procedure for SQLite; a dump command with timer and cron examples for PostgreSQL and MariaDB. How long backups are kept is up to the host. Not started. Comes with store-sqlite. | planned | not released |
PostgreSQL storage store-postgresA PostgreSQL backend for hosts who bring their own database; the server does not run or manage it. After store-sqlite. | later | not released |
MariaDB storage store-mariadbA MariaDB backend for hosts who bring their own database; the server does not run or manage it. After store-sqlite. | later | not released |
High availability
| Feature | Status | Since |
|---|---|---|
Health check and clean restarts ha-restartA data-free /healthz endpoint on the opt-in status listener, guarded by the existing HTTP token, and bounded shutdown that closes listeners and accepted client/relay sockets. A guide covers probe results, supervisors and what players lose on restart.Health is 200 only while both game serving loops are active, 503 during startup/shutdown or after a loop exits. Loopback and subprocess tests cover shutdown; systemd/container restarts and remote failover are not run. Rooms and scores remain in memory and are lost on restart. No database retry is needed until a network backend exists. | available | 0.4.0 |
Active/passive failover ha-failoverDocs and example files for a standby server: a floating address (keepalived) or a proxy's health checks, both servers on the same database, one active at a time. No election or replication inside the server. Not started. After ha-restart; the shared database needs store-postgres or store-mariadb. Live rooms are lost on failover. | planned | not released |
Several servers ha-multiSeveral servers behind one address, each room wholly on one server, with a routing layer that sends a new room or a join to the right one. On the back burner: revisited after ha-restart and ha-failover exist and one server is not enough. | later | not released |
Live room handover ha-handoverRooms that survive the loss of a server. Needs replicated live state; out of scope for a server of this size. | declined | not released |
Network
| Feature | Status | Since |
|---|---|---|
Address discovery and hole punching net-punchA player's P2P socket sends the server a UDP PROBE. The server records the public endpoint and gives each viewer the address that works from where they are: the public one from another network, the LAN address from the same one. Upstream work. The NAT lab runs it in Docker in CI. | available | 0.2.0 |
Relay net-relay-relay, off by default: streams into a player behind a router go through the server, and datagrams go through the server's UDP port when punching does not get through (as with a symmetric NAT).Upstream work. Uses the server's bandwidth; there is no per-player budget yet (net-guardrails). | available | 0.2.0 |
UDP source address check net-udp-guardPROBE and RELAY packets are accepted only from the IP address of the player's TCP connection. A probe's local_ip is kept only if it is private, carrier-grade NAT, link-local or the probe's own source. -udp-any-source turns the check off.A Podman run is recorded in docker/README.md. | available | 0.2.0 |
More UDP and relay guardrails net-guardrailsBounded per-source-IP UDP packet admission shared by probes, relay and invalid traffic, plus aggregate drop-reason counters in /api/stats. The token bucket tracks at most 4096 sources, expires idle entries and rejects new sources at capacity. Initial packet limiter only: default 1000 packets/second with the same burst, configurable with -max-udp-rate and 0 for off; players behind one NAT share it. Per-player relay byte/datagram budgets, payload and TCP stream limits, failed-token blocking and UDP fuzz coverage remain planned. A larger token needs a protocol version bump. | partial | 0.4.0 |
Connection checks net-checksA check for players and hosts that says where a connection fails (server reachable, HELLO accepted, UDP probe answered, NAT behaviour, peer reachable, relay available) and shows round trip, loss and whether each peer is up. Not designed yet. | planned | not released |
UPnP net-upnpOpen a player's P2P port on their router automatically. Not started. | later | not released |
Force the relay (testing) net-relay-force-relay-force (RE_PSN_RELAY_FORCE, off by default, needs -relay) sends every stream through the relay, even between players who could connect directly, so the relay can be tested from one network. Datagrams are not forced.Not in v0.4.0. For testing only: it uses the host's bandwidth. | available | 0.5.0 |
Per-title peer traffic off net-no-peer-titles-no-peer-titles lists communication IDs of titles that use no peer traffic: their players get no UDP probe reply, no relay for datagrams or streams, no relay route and no relay flag in HELLO_ACK, and still use rooms and scores. Other titles on the server are unaffected.Not in v0.5.0. A list of IDs, not a per-title profile file. Hole punching itself is client-side: it stops because the server never learns the endpoint. Tested on loopback with a client written for the tests, not with a real title. | available | 0.6.0 |
Capability report net-capsGET_CAPS (0x04) after HELLO returns CAPS (0x94): a bit set of what this server offers this player (rooms, scores, room codes, peer keys, the UDP probe, the relay, LAN discovery) and the server version. The probe and relay bits are per title, so a title in -no-peer-titles sees them clear. The C client has the constants and psnr_caps_parse.Not in v0.5.0. A server before this answers the request with ERROR 1. Tested on loopback with a test client and the C client; no real title asks for it yet. | available | 0.6.0 |
Peer keys (server side) net-peer-keysA client may ask with PEER_AUTH; each pair of members who both asked then gets one new random 16-byte key in a PEER_KEY push, for tagging their direct packets. Opt-in; clients that do not ask are unaffected.Not in v0.4.0. No client uses the keys yet, and nothing is tagged on the wire. The keys travel over plain TCP, so this gives no confidentiality and no protection from someone who can read a player's network traffic. | partial | 0.5.0 |
Public address for LAN players net-lan-public-addr-lan-public-addr IP (RE_PSN_LAN_PUBLIC_ADDR, IPv4, off by default) gives players on public addresses the host's address and the member's P2P port for a player who connected from a private, CGNAT or link-local address. The host forwards each such P2P port. Players on private addresses are unaffected.Not in v0.4.0. Off by default. Several LAN players behind one forward need distinct P2P ports; the relay covers a failed connection. | available | 0.5.0 |
LAN discovery (server side) net-lan-discover-discover (RE_PSN_DISCOVER, off by default) answers a 64-byte DISCOVER sent to the game UDP port with a 52-byte reply: TCP and UDP ports, relay flag, player count, name (-name) and version. Only private, carrier-grade NAT, link-local and loopback senders are answered, under the UDP rate limit.Not in v0.4.0. Off by default. Server side only: no client helper yet. A broadcast does not cross subnets or Wi-Fi client isolation. | available | 0.5.0 |
Medius profiles
| Feature | Status | Since |
|---|---|---|
Profile-specific transport and discovery medius-transportAn opt-in, disabled-by-default Medius-compatible frontend for a named title/build profile, starting with synthetic local transport and discovery. Unknown or unsupported profiles, versions, application IDs and messages fail explicitly. Not implemented. Candidate Killzone 3 BCUS98234 update 1.14, app ID 23044: compatibility not tested; not supported. | planned | not released |
Profile-specific sessions and lobby medius-lobbyLogical sessions across service sockets and profile-specific lobby operations, with only an explicitly unauthenticated synthetic local TicketLogin envelope. Not implemented; no PSN authentication or real tickets. | planned | not released |
Profile-specific data path medius-dataA data path considered only after the selected profile's transport, membership and authorization requirements are evidenced. Not implemented. The data plane, NAT and DME requirements are unknown; no psnr reuse is promised. | planned | not released |
Further title/build profiles medius-multititleConsider additional explicitly selected title/build profiles, each with its own evidence and failure rules. Not started; no general Medius or multi-title compatibility is claimed. | later | not released |
Client
| Feature | Status | Since |
|---|---|---|
C client client-cOne file with no dependencies and no threads: psnr_send returns a request id, and psnr_poll hands out replies and pushes; sends and automatic heartbeat writes can wait for socket writability. It also builds the UDP probe and relay packets.Upstream work. The integration guide documents ownership and timeout limits. TestCClient also compiles/runs the minimal example; it skips without a C compiler, and Linux CI requires execution. | available | 0.1.0 |
Go client package app-clientapp/internal/client: a Go client for the TCP protocol, for the desktop app. HELLO, heartbeat, create, search, join and leave rooms, record and read scores, and the member and route pushes, with typed messages and errors. re-psn-app check HOST:PORT uses it.Room data and messages, kicks, invite-only rooms, relay streams and UDP are not in it yet. | partial | 0.3.0 |
Desktop app, terminal UI app-tuire-psn-app with no arguments: a first-run choice of Play, Host or Both with the online ID and the server, a menu and content pane (one at a time under 70 columns), a Status screen with Rooms and Leaderboards tabs over the game protocol or an explicitly configured HTTP status source, a Settings screen that saves re-psn.ini, and a dark or light theme in the website colours. Built on Bubble Tea and Lip Gloss.Partial: no Host screen, room joining, launch profiles or theme presets yet. Linux desktop starts without a TTY can open a supported terminal; Windows uses its console. Synthetic startup tests ran; physical desktops/emulators remain unverified. The optional HTTP source adds uptime, connection counts, public-room members/age and board leader summaries with bounded authenticated fetches and game-protocol fallback. The game protocol still lacks those extra fields; neither source reports server version. Driven in tmux on Linux; not tried on Windows or macOS terminals. | partial | 0.3.0 |
Desktop client, terminal UI client-desktopA terminal program for players and hosts: launch profiles per game, a host mode, and a check of the connection. Not started. One executable per platform, with nothing else to install. | planned | not released |
Desktop client, graphical UI client-guiA local web UI served by the same program, and a graphical app on Windows, with the same screens as the terminal UI. Not started. After client-desktop. | planned | not released |
Colour themes app-themesColour presets in the desktop apps: the project palette by default, then gruvbox, catppuccin, kanagawa and melange. A preset is a data file shared by the apps and the README art. Not started. A preset ships only after its project's licence is read and credited in NOTICE.md. The terminal UI falls back to the terminal's own 16 colours and respects NO_COLOR. | planned | not released |
Appearance mode app-appearanceDark, Light or System (the default): presets carry a dark and a light variant where their project has both, and System follows the OS or the terminal background and switches while the app runs. Desktop presets and system-following remain planned. The server status page supports browser System/Light/Dark and local appearance controls; see status-page. | planned | not released |
Custom colours app-coloursEdit named colour slots (background, surface, text, muted, accent, good, warn, bad) as hex values, with a contrast warning below 4.5:1, import and export, saved per user and per mode. Not started. A bad value falls back to the default preset. | planned | not released |
LAN discovery in the C client client-lan-discoverpsnr_discover broadcasts one UDP request from its own temporary socket and returns the LAN servers that answer, with their ports, relay flag, player count, name and version.Not in v0.4.0. Finds only servers started with -discover. Tested against a fake responder and, by hand, against a real server on loopback; not a real broadcast across a network. Does not cross subnets or client-isolated Wi-Fi. The Windows build broadcasts to 255.255.255.255 only; CI compiles the client with MSVC but does not run it on Windows. | available | 0.5.0 |
Host connect policy in the C client client-connect-policypsnr_set_connect_policy registers one callback that the client asks before it opens a TCP connection: the connection psnr_connect and psnr_connect_timeout make (once per resolved address, with the host as typed) and each relay leg (psnr_stream_connect, psnr_stream_accept). A refusal skips that address, or returns -1 for a relay leg; refusing every address gives NULL and psnr_connect_error() is PSNR_E_POLICY. No callback means today's behaviour.Not in v0.5.0. Process-wide, set before the first connect; not covered: psnr_discover and the title's own P2P socket. IPv4 only, like the client. Tested against a real server on loopback; each check fails with its hook removed. | available | 0.6.0 |
Desktop app
| Feature | Status | Since |
|---|---|---|
Find servers on the LAN app-lan-discoverFind on LAN in the desktop app ( f in Settings, Ctrl+F while typing the server): the Go client broadcasts one 64-byte UDP request from a temporary socket, collects the 52-byte replies for about 1.5 s, drops anything with the wrong length, type or nonce, and lists name, address:port, players, relay and version; the chosen address fills the server field. Typing host:port still works.Not in v0.4.0. Finds only servers started with -discover. Tested against a fake responder and, by hand, against a real server on loopback; not a real broadcast across a network. IPv4 and the local subnet only. server = auto in re-psn.ini is not supported. | available | 0.5.0 |
Server view in Status app-server-viewWith a status URL set, the Status screen has a read-only Server tab (Tab cycles Rooms, Leaderboards, Server): version, uptime, relay on or off, the limits in force (0 shown as none), TCP refusals and UDP drop counters, and the payload carried by the relay with a bytes-per-second rate computed between two refreshes. A counter that goes down is shown as server restarted and the rate baseline starts again. Fields an older server omits are shown as not reported by this server, never as 0 or off. Nothing is changed on the server and the token is never shown. Not in v0.4.0. Needs a server that reports the fields (newer than v0.4.0); older servers show not reported. Relay bytes and the rate are payload carried by the relay, not network bandwidth. Checked by hand against a real server on loopback. | available | 0.5.0 |
Testing and CI
| Feature | Status | Since |
|---|---|---|
Fuzz tests test-fuzzGo fuzz targets for the frame parser, the request handlers and the PROXY header parser. Their seed inputs run with every go test.CI runs the seeds only; longer fuzzing runs are by hand. The UDP packets have no fuzz target yet. | available | 0.2.0 |
CI checks ci-checksgofmt, go vet, go test -race on Linux (with the C client) and go test on Windows, govulncheck, gitleaks over the full history, the NAT lab in Docker, and CodeQL for Go and the workflows. Actions are pinned and permissions are read-only by default.Dependabot proposes version updates. A focused local loopback runner requires native C-client and Go app-client conformance checks to execute; synthetic loopback only. | available | 0.2.0 |
More CI checks ci-moreCandidates: the C client outside Linux, the race detector on Windows, and a scan of the container image. The list is not settled. The header of ci.yml says what CI does not check today. | planned | not released |
Release
| Feature | Status | Since |
|---|---|---|
Release archives and checksums release-archivesscripts/release-build.sh builds static binaries for windows/amd64, linux/amd64 and linux/arm64 into re-psn-vX.Y.Z-<os>-<arch> archives and one SHA256SUMS listing them. A pushed version tag makes a draft pre-release; a second workflow checks a published one.Published as the v0.2.0 pre-release (2026-11-06): the release workflow and the published-release check both passed. The Windows and arm64 binaries are cross-built and not run. | partial | 0.2.0 |
Linux AppImage release-appimagescripts/build-appimage.sh packs the linux/amd64 server binary and its examples as re-psn-vX.Y.Z-x86_64.AppImage, with appimagetool 1.9.1 and the type-2 runtime 20251108, both downloaded by the release workflow and checked against pinned SHA-256 hashes. A smoke test compares its -h and version with the binary's, and it is listed in SHA256SUMS.Published in the v0.2.0 pre-release (2026-11-06); the release workflow built it on a runner and passed its smoke test. It was also built and run on one x86_64 Linux machine, with FUSE and with --appimage-extract-and-run. x86_64 only, unsigned. | partial | 0.2.0 |
Install scripts install-scriptscripts/install.sh (Linux amd64 and arm64, POSIX sh) and scripts/install.ps1 (Windows amd64; written for PowerShell 5.1 and 7, tested on 7 only) install the server for the current user from a GitHub release, newest or pinned, after checking the download against SHA256SUMS. No sudo or administrator rights, no profile edits; --dry-run, --dir, --appimage, -AddToPath.Partial: release assets ( install.sh, install.ps1, listed in SHA256SUMS) since v0.4.0; v0.3.0 does not carry them. The Linux tests run under dash, bash and BusyBox ash with a fake release, and the published v0.4.0 install.sh installed that release on Linux amd64 into a temporary directory; install.ps1 has not installed from a published release. The PowerShell fake-release tests have passed under PowerShell 7 on Linux and Windows CI; PowerShell 5.1 remains untested. No macOS builds, so no macOS support. | partial | 0.4.0 |
Release process release-processWritten steps for cutting a release (tag, notes from CHANGELOG.md, checking the draft, publishing), a check that the version in the code and in CHANGELOG.md agree, and a dry run of the release workflow before the first tag. VERSION, the version check (TestVersionAgrees), scripts/bump-version.sh and scripts/release-notes.sh are built and docs/releasing.md has the steps; the release workflow builds its notes with the notes script and checks that a tag is v + VERSION, and the first tag, v0.2.0 (2026-11-06), ran the whole path: the dry run, the tag step and the draft job passed, and the draft was published by hand after its assets were read back. No fixed release day: while things are worked out, pre-releases (v0.x, marked pre-release) come whenever there is something worth trying. | available | 0.2.0 |
Signed releases release-signingSign re-psn.exe so Windows does not warn about an unsigned program, and sign the checksums.The exe and SHA256SUMS are unsigned today. | later | not released |
Desktop app archives app-archivesThe release build makes re-psn-app-vX.Y.Z-linux-amd64.tar.gz, -linux-arm64.tar.gz and -windows-amd64.zip from app/ (static, no cgo, version set from the tag), each with LICENSE, NOTICE.md and the app README, listed in SHA256SUMS. A release has ten assets.Not in v0.4.0. install.sh --app and install.ps1 -App install it (tested against a fake release only). The release workflow has not built them on a tag; the Windows and arm64 binaries are cross-built and not run. | available | 0.5.0 |
Documentation
| Feature | Status | Since |
|---|---|---|
Features list docs-featuresdocs/features.tsv holds one row per feature with its status and evidence, and tools/features.py generates this page. CI fails when the page is stale, a row is malformed, or evidence is missing. | available | 0.2.0 |
Mermaid diagrams docs-diagramsThe architecture, reverse proxy, relay and NAT lab diagrams are Mermaid, which GitHub renders. No generated images to keep in step. | available | 0.2.0 |
Docs in Markdown docs-markdownEvery doc in Markdown with one layout, including the deploy and Docker READMEs with diagrams. The offline repo-check guard validates tracked local Markdown links and anchors in CI. | available | 0.2.0 |
Ways to connect docs-connectingA page on how players reach a server: same LAN, port forwarding, a public server with -relay, a VPN overlay (Tailscale, ZeroTier) or a reverse proxy, with what each needs, its limits, diagrams and links to the overlays' official documentation.Not tested: re-psn has not been run over Tailscale, ZeroTier or any other overlay. Overlay addressing was imitated with network namespaces only. | available | 0.2.0 |
README rewrite docs-readmeA README that says what re-psn is, with a quick start, the available features linking to this page, installation, configuration and credit to upstream. Example output comes from real local runs; the settings table was checked against the server flags. The banner comes from the art generator (docs-art). | available | 0.2.0 |
Art generator docs-arttools/art/make_art.py draws the README banner, a social preview image and terminal images in the project palette (docs/art/palette.json), with JetBrains Mono fetched from its tagged release and pinned by SHA-256. Terminal images are drawn only from transcripts of real runs (docs/art/transcripts/, recorded by tools/art/capture.sh). CI renders again and fails when a byte differs.The README shows the banner; the social image is uploaded by hand in the repository settings; its example stays text, and the terminal images are kept for the site. Images are dark only; the light colours are in the palette for later. Fonts are not committed. | available | 0.2.0 |
Project website docs-websiteA static page built from the features list, the version, the settings table and the changelog by tools/site.py: overview, features with status tags, roadmap, settings, install, connect, docs and credits. With site/terminal.js (no dependencies) the page becomes a terminal-style interface: menu, content and a command line with the commands features, roadmap, settings, install, connect, changelog, docs and more, suggestions while typing (a dimmed completion and a list of up to eight matches, taken with Tab, Right arrow, Enter or a tap), Tab completion, history, a dark and a light theme chosen with a System / Light / Dark switch in the header or the theme command (the same choice, remembered in the browser), colour from the full gruvbox-material palette with every text colour checked at 4.5:1 or more, and scrolling tabs and tappable chips on a phone (a phone starts in the plain view); the same page works without script, and a plain view link switches the interface off. No third-party loads, a content security policy that allows only this site, and the colours of the README art. pages.yml deploys site/ to GitHub Pages. Command output has a file header, line-number gutter, frame and syntax-style colours.Partial: the page is published at https://undecuple.github.io/re-psn/ and deploys from main while PAGES_ENABLED is true. Unit tests and optional headless-browser checks cover command typing, autocomplete/history, selection without gutter numbers, dark/light and system-theme changes, narrow layouts and static fallbacks. A real keyboard, screen reader and physical phone have not been tried. The font remains the visitor's monospace; self-hosting is not added. | partial | main |
Repository layout check repo-checkAn offline CI check of tracked root filenames and top-level directories, local Markdown links and heading/explicit anchors, and static repository input paths in workflows and Dependabot. Python standard library and Git only. Does not fetch external links, evaluate arbitrary shell/YAML expressions or validate generated artifact paths. | available | 0.4.0 |
Recomp developer integration docs-integrationA source-pinned guide for ps3recomp ports and direct C/C++ adapters: build/startup, callback ownership, message lifetime, failure handling and peer/relay boundaries. A synthetic minimal C example exercises HELLO and room search in the required native-client test. Runtime integration was inspected at the linked upstream revision; no actual title was built or run for the guide. Native example/conformance checks are synthetic loopback evidence, not gameplay or NAT certification. | available | 0.4.0 |
Roadmap
The same list grouped by status. No dates are promised; ROADMAP.md has the order and the scope.
Done (55)
- Rooms and search
- Room and member data
- Invite-only rooms
- Leaderboards
- One player per name
- Request and connection limits
- Log levels and formats
- Status page and /api/stats, with a token
- PROXY protocol and trusted proxies
- Settings file, service unit and Traefik example
- nginx and HAProxy examples not yet verified
- Caddy status-page example not yet verified
- Optional fail2ban status-token jail not yet verified
- Docker and Podman
- Flags and RE_PSN_* variables
- Storage interface
- Health check and clean restarts
- Address discovery and hole punching
- Relay
- UDP source address check
- More UDP and relay guardrails partial
- C client
- Go client package partial
- Desktop app, terminal UI partial
- Find servers on the LAN
- Fuzz tests
- CI checks
- Release archives and checksums partial
- Linux AppImage partial
- Install scripts partial
- Release process
- Features list
- Mermaid diagrams
- Docs in Markdown
- Ways to connect
- README rewrite
- Art generator
- Project website partial
- Repository layout check
- Recomp developer integration
- Known games by name
- Verified player count
- Server state in /api/stats
- Player-reported latency
- Force the relay (testing)
- Per-title peer traffic off
- Capability report
- Peer keys (server side) partial
- Public address for LAN players
- LAN discovery (server side)
- LAN discovery in the C client
- Host connect policy in the C client
- Server view in Status
- Experimental admin API, read-only part partial
- Desktop app archives
Next (15)
- Private rooms
- Friends, presence and invites
- SQLite storage (opt-in)
- Backup and restore
- Active/passive failover
- Connection checks
- Profile-specific transport and discovery
- Profile-specific sessions and lobby
- Profile-specific data path
- Desktop client, terminal UI
- Desktop client, graphical UI
- Colour themes
- Appearance mode
- Custom colours
- More CI checks
Later (9)
Not planned (1)
Changes
Unreleased
Changed
- **Docs and tooling:** the README and
docker/README.mdname v0.6.0 and its container image as the newest, and the features list sayssince0.6.0, notmain, for four rows that shipped in it (-no-peer-titles, the ...
0.6.0 (2026-11-12)
21 under Added, 18 under Fixed, 10 under Changed.
The full history is in CHANGELOG.md.
Settings
Each of the 35 flags can also be set as a RE_PSN_<FLAG> variable. A flag on the command line wins, then the variable; a value that does not parse stops the server at start. It runs with no settings at all. docs/running.md has the full text.
| Flag | Variable | Default | What it does |
|---|---|---|---|
-addr | RE_PSN_ADDR | :36100 | TCP and UDP address for game clients |
-tcp-addr | RE_PSN_TCP_ADDR | -addr | TCP address for game clients, if different |
-udp-addr | RE_PSN_UDP_ADDR | -addr | UDP address for game clients, if different |
-http | RE_PSN_HTTP | none (off) | Status page and /api/stats on this address, for example 127.0.0.1:36101 |
-http-token | RE_PSN_HTTP_TOKEN | none | Token the status page requires (Authorization: Bearer, or ?token=) |
-admin | RE_PSN_ADMIN | none (off) | **Experimental.** A read-only admin API on its own address; needs -admin-read-token-file, and an address that is not loopback also needs -admin-tls-cert and -admin-tls-key (admin-api.md); not in v0.5.0 |
-admin-read-token-file | RE_PSN_ADMIN_READ_TOKEN_FILE | none | File holding the admin read token (at least 32 characters, mode 0600), read again on SIGHUP; not in v0.5.0 |
-admin-tls-cert | RE_PSN_ADMIN_TLS_CERT | none | Certificate file for the admin listener (TLS 1.2 or newer); not in v0.5.0 |
-admin-tls-key | RE_PSN_ADMIN_TLS_KEY | none | Private key file for the admin listener; not in v0.5.0 |
-max-rooms | RE_PSN_MAX_ROOMS | 1000 | Rooms across all titles |
-max-player-rooms | RE_PSN_MAX_PLAYER_ROOMS | 8 | Rooms one player may be in at once (0: no limit) |
-max-conns | RE_PSN_MAX_CONNS | 4096 | Open TCP connections in total, players and relay legs (0: no limit); not in v0.5.0 |
-max-conns-per-addr | RE_PSN_MAX_CONNS_PER_ADDR | 32 | Open connections per remote address; an IPv6 address counts as its /64 (0: no limit) |
-max-req-rate | RE_PSN_MAX_REQ_RATE | 200 | Requests per second per connection before it is closed (0: no limit) |
-max-udp-rate | RE_PSN_MAX_UDP_RATE | 1000 | UDP packets per second per source (IPv4 address or IPv6 /64), shared by probes and relay traffic; same-size burst (0: no limit); since v0.4.0 |
-v | RE_PSN_V | off | Same as -log-level debug |
-log-level | RE_PSN_LOG_LEVEL | info | debug (room events), info, warn or error |
-log-format | RE_PSN_LOG_FORMAT | text | text or json |
-log-file | RE_PSN_LOG_FILE | none | Also append the log to this file (mode 0600) |
-trusted-proxies | RE_PSN_TRUSTED_PROXIES | none | Reverse proxies allowed to name the real client (CIDRs or addresses) |
-proxy-protocol | RE_PSN_PROXY_PROTOCOL | off | Require a PROXY v1/v2 header from a trusted proxy on the game TCP port |
-udp-any-source | RE_PSN_UDP_ANY_SOURCE | off | Accept UDP probes and relay packets from another address than the player's TCP one |
-relay | RE_PSN_RELAY | off | Carry traffic for players who cannot reach each other (uses the host's bandwidth) |
-relay-idle | RE_PSN_RELAY_IDLE | 600 | With -relay, close a relay stream after this many seconds with no bytes in either direction (0: never); not in v0.5.0 |
-relay-force | RE_PSN_RELAY_FORCE | off | With -relay, send every stream through the relay, even between players who could connect directly. For testing the relay from one network; uses the host's bandwidth |
-no-peer-titles | RE_PSN_NO_PEER_TITLES | none | Comma-separated communication IDs of titles that use no peer traffic. For their players the server answers no UDP probe, relays nothing and routes nothing, and HELLO_ACK says the relay is off; rooms and scores work as usual. Other titles are unaffected; not in v0.5.0 |
-discover | RE_PSN_DISCOVER | off | Answer LAN discovery broadcasts on the game UDP port, from private-network senders only (connecting.md) |
-name | RE_PSN_NAME | none | Server name shown in LAN discovery: at most 24 printable ASCII bytes; shown only with -discover |
-lan-public-addr | RE_PSN_LAN_PUBLIC_ADDR | none | IPv4 address given to players outside the LAN as the address of players who connected over a private, carrier-grade NAT or link-local address; the host must forward each such player's P2P port |
-medius | RE_PSN_MEDIUS | off | Medius compatibility switch. **No Medius protocol is implemented** (scope): it opens the -medius-port listener, which closes every connection; not in v0.5.0 |
-medius-port | RE_PSN_MEDIUS_PORT | 10071 | TCP port of the Medius listener; a value other than the default needs -medius; not in v0.5.0 |
-medius-addr | RE_PSN_MEDIUS_ADDR | 127.0.0.1 | Address of the Medius listener: a loopback, private or link-local IP address, or 0.0.0.0 / :: in a container; a value other than the default needs -medius; not in v0.5.0 |
-db | RE_PSN_DB | off | Score storage: off or memory |
-db-file | RE_PSN_DB_FILE | none | Read the -db setting from this file instead |
-replace | RE_PSN_REPLACE | off | If an earlier re-psn of this user holds the game port, stop it (SIGTERM) and start in its place (Linux only) |
Install
The latest release is v0.6.0, a pre-release.
Release archives
Archives for Linux (amd64, arm64) and Windows (amd64), and a Linux x86_64 AppImage, all unsigned, with a SHA256SUMS file. Check a download on Linux:
sha256sum -c SHA256SUMS --ignore-missing
On Windows, compare the output of Get-FileHash <archive> with the line in SHA256SUMS.
Container
docker pull ghcr.io/undecuple/re-psn:0.6.0
docker run --rm -p 36100:36100 -p 36100:36100/udp ghcr.io/undecuple/re-psn:0.6.0
Podman works the same way. See docker/README.md.
From source
You need Go 1.22 or newer; a C compiler only for the C client and its tests.
go build -o re-psn ./server
./re-psn
Connect
Players on one LAN or VPN need nothing forwarded. Otherwise the host forwards a port, runs a public server, or uses an overlay network such as Tailscale or ZeroTier; the relay is the fallback when two players cannot reach each other.
- Game connection and address probes:
:36100, TCP and UDP, open to every player. - Status page:
none (off), for the host only; it lists every online ID. - Each player's P2P port (UDP and TCP) must be reachable by the other players; the game opens it.
A game reaches the server through the C client in client/psnr.c, via ps3recomp's NP modules. That side is upstream's work; docs/connecting.md covers each way to connect.
Docs
- Website controls Numbered terminal output, keyboard controls and contributor checks
- Architecture The parts, the threadless client, why game traffic is peer to peer, the relay
- Protocol Framing, session, rooms, leaderboards, UDP, relay, HTTP
- Running a server Flags, variables, UDP source check, reverse proxy, logging, ports, firewall, storage
- Connecting players LAN, port forwarding, a public server, Tailscale or ZeroTier
- Features Every feature with its status and evidence
- Roadmap What is done, planned and out of scope
- Changelog What changed in each release
- Releasing The version, the bump and cutting a pre-release
- Deployment examples Settings file, systemd unit, Traefik, nginx and HAProxy
- Container Building and running with Docker or Podman
- NAT lab Players behind routers, in Docker
- Contributing Ground rules and the tests to run
- Security How to report a vulnerability privately
Source and issues: https://github.com/undecuple/re-psn.
Credits and licence
- sp00nz (sp00nznet) wrote psnr: the server, the C client, the protocol, NAT traversal, the relay and the NAT lab.
- undecuple (undecuple) maintains re-psn and wrote everything added since the fork: the release and container tooling, the AppImage, hardening and limits, rooms and invite codes, the desktop app, this website, the documentation and the tests.
- ps3recomp is the recompiler whose NP modules use the client.
- The site's colours follow the undecuple page, which credits them to guts.nvim (that repository states no licence). The light colours of the desktop app and the status page are from gruvbox-material (MIT, Copyright (c) 2020 sainnhe).
MIT, see LICENSE; upstream's copyright line is kept and a line for the fork's changes is added. PlayStation, PS3 and PSN are trademarks of Sony Interactive Entertainment, used only to say what the server stands in for.