feat: add selectable allocation-based privacy relay

This commit is contained in:
Alexander Sellite 2026-09-01 10:21:34 -04:00
parent e979da9e76
commit 46de16db00
14 changed files with 1067 additions and 43 deletions

View file

@ -1,12 +1,15 @@
# straywild Discovery Server
An ephemeral public-room directory for straywild. It complements the game's existing direct ENet
transport: hosts advertise a room through HTTPS, browsers retrieve compatible rooms, and joining
still uses the authoritative host's UDP endpoint.
An ephemeral public-room directory and allocation-based UDP privacy relay for straywild. Player
hosts choose direct or relay routing when they advertise through HTTPS. Browsers retrieve
compatible metadata without receiving an IP address, and relay-enabled joins give each player an
isolated relay endpoint instead of the other peer's network address. Dedicated servers are
direct-only and cannot consume shared relay capacity.
The service also runs a small UDP rendezvous. It observes packets sent from the exact ENet sockets
used by hosts and joiners, then lets an authenticated room host retrieve pending endpoints and send
hole-punch packets. Gameplay remains direct and never passes through this service.
The service also runs a small UDP rendezvous. It observes packets from the host's exact ENet socket
and lets that host punch the allocated relay socket. Relay gameplay is pinned to the authenticated
joining socket and verified host endpoint, bounded by packet/byte budgets, and removed after an
idle timeout. Addresses are held only in memory and are not written to the application log.
Friend presence and live room invitations use the same service as short-lived capability channels.
There are no discovery accounts, durable friend records, or offline messages; friendship state
@ -50,7 +53,9 @@ The intended public topology is:
```text
straywild client -> HTTPS reverse proxy -> 127.0.0.1:7770 discovery service
straywild client -----------------------> public UDP rendezvous:7771
straywild client -----------------------> advertised ENet/UDP host:port
direct room client ---------------------> authoritative ENet/UDP host
relay room client ----------------------> allocated UDP relay:20000-22047
allocated UDP relay --------------------> player-hosted ENet/UDP host
```
Configure the Godot client with the reverse proxy's HTTPS origin through
@ -63,6 +68,26 @@ its own address must be listed in `straywild_DISCOVERY_TRUSTED_PROXY_CIDRS`.
Apply ordinary request-rate limits to the write endpoints at the proxy. Do not
expose the Python listener directly to the public Internet.
### Privacy logging policy
The discovery and relay application must not log private player information. This includes raw IP
addresses and ports, address-derived pseudonyms, request paths tied to room IDs, room or player
names, request bodies, headers, join and lease tokens, authentication material, and friendship
capabilities. HTTP access logging is disabled completely. Unexpected request failures produce only
a generic operational error without a peer address, request contents, or request-specific
traceback.
The service necessarily handles network endpoints temporarily in memory to observe routes, apply
abuse limits, and forward active relay traffic. Those endpoints expire with their room, join, or
relay state and are never written to the application log or a database. Temporary processing is
not permission to retain or repurpose that information.
Production deployments must also disable reverse-proxy access logs for every discovery route.
Default proxy log formats normally contain a client address and are not acceptable here. Do not
log forwarded addresses, request bodies, authorization headers, or tokens at the proxy, firewall,
or service wrapper. Hosting providers and upstream networks may retain connection metadata under
their own policies; operators should choose providers accordingly and disclose that boundary.
Use the sample systemd unit in [`deploy/straywild-discovery.service`](deploy/straywild-discovery.service)
as a deployment starting point. The service keeps only active leases in memory, so it requires no
database, backups, or schema migrations.
@ -70,13 +95,23 @@ database, backups, or schema migrations.
## Connectivity boundary
The game first requests UPnP forwarding, then uses same-socket UDP rendezvous and hole punching.
This covers common home NAT configurations without changing ENet gameplay authority. Symmetric
NAT and some carrier-grade networks can still require a future ENet-compatible relay fallback.
Expose the configured traversal port over UDP in both the host firewall and provider firewall.
A player host chooses whether its public listing is direct or relay. A relay room punches a unique
allocation and both peers exchange ENet packets through it; a direct room connects through the
verified host route and consumes no relay allocation. ENet gameplay authority does not move to
discovery. Dedicated advertisements must be direct and a dedicated relay request is rejected.
Expose the configured traversal port and full relay allocation range over UDP in both the host
firewall and provider firewall.
The rendezvous listener intentionally processes its small, bounded packets serially so arbitrary
datagrams cannot create unbounded worker threads. Apply conservative edge rate limits as an
additional deployment control.
The relay prevents the player host and joiner from seeing one another's address during normal
network traffic. It is not anonymity from the relay operator, hosting provider, upstream network,
or a compromised client. The discovery process necessarily handles both endpoints in volatile
memory. Clients treat the host-selected connection mode as binding: failure to allocate or reach a
relay stops the join and never silently falls back to direct transport. Operators may explicitly
disable relay availability for private development; a player relay advertisement is then rejected.
## API
See [`docs/API.md`](docs/API.md) for the versioned HTTP contract.