Ever wanted to play a local game with a friend who is far away? Star makes local multiplayer games playable remotely!
The application comes with two components: Registar, a server that manages connectivity between players, and Star, a client that forwards UDP traffic. It has a GUI or can be used via CLI.
You can use the publicly available server at https://registar.dmksnnk.xyz or deploy your own Registar server.
A pre-built Docker image is available at ghcr.io/dmksnnk/star/registar.
services:
registar:
image: ghcr.io/dmksnnk/star/registar:latest
restart: unless-stopped
environment:
- SECRET=your-secret-here # optional, omit for open access
- CERT_DOMAINS=your-domain.com
- CERT_DIR=/certs
ports:
- "80:80" # HTTP unencrypted (redirects to HTTPS, used for ACME challenge)
- "443:443/tcp" # HTTP on top of TCP with TLS
- "443:443/udp" # HTTP/3 on top of UDP (QUIC)
volumes:
- certs:/certs
volumes:
certs:
See Registar Server Configuration for all configuration options.
Registar runs three services:
Alt-Svc.Since Registar uses HTTP/3 (QUIC), it manages its own TLS certificates (via Let's Encrypt). The parts that can be proxied are the HTTP and HTTPS servers, but the HTTP/3 server must listen directly on the public UDP port (443/UDP), because we need the client's public IP addresses for NAT hole-punching.
HTTP_LISTEN (e.g., :8080). This handles redirects and ACME.
Do not enable tlsChallenge in Traefik - it intercepts TLS-ALPN-01 and Registar won't be able to complete its ACME challenge.
Use httpChallenge instead.HTTPS_LISTEN (e.g., :8443).
The proxy should not terminate TLS for this service, as Registar manages its own certificates.443:8443/udp). This allows UDP traffic to coexist with Traefik's TCP traffic on port 443.Here is an example of how to do it with Traefik:
docker-compose.yaml (registar service):
services:
registar:
image: ghcr.io/dmksnnk/star/registar:latest
restart: unless-stopped
environment:
- HTTP_LISTEN=:8080
- HTTPS_LISTEN=:8443
- CERT_DIR=/certs
- CERT_DOMAINS=registar.example.com
- HTTPS_ADVERTISE_HTTP3_PORT=443 # advertise public UDP port for HTTP/3
- HTTPS_REDIRECT_PORT=443 # redirect to public HTTPS port
- SECRET_FILE=/run/secrets/registar_secret # optional, omit for open access
ports:
- "443:8443/udp" # HTTP/3 (QUIC) — bypasses Traefik (UDP not proxied)
volumes:
- certs:/certs
secrets:
- registar_secret
volumes:
certs:
secrets:
registar_secret:
file: ./registar_secret
Traefik routes.yaml (registar entries):
http:
routers:
registar-http:
entryPoints: [web]
rule: "Host(`registar.example.com`)"
service: registar-http
services:
registar-http:
loadBalancer:
servers:
- url: "http://registar:8080"
tcp:
routers:
registar:
entryPoints: [websecure]
rule: "HostSNI(`registar.example.com`)"
service: registar
tls:
passthrough: true # registar handles TLS itself
services:
registar:
loadBalancer:
servers:
- address: "registar:8443"
traefik.yaml (important — use httpChallenge, not tlsChallenge):
certificatesResolvers:
letsencrypt:
acme:
httpChallenge:
entryPoint: web
Prebuilt client can be found at Releases page
If you are hosting the game, follow these steps:
2. Fill in the server's URL. If the server requires a secret, expand Advanced and enter it.
3. Click "Host a Game".
4. Select a game from the list or enter a custom port to forward to.
5. Click "Start Hosting".
6. Share the invite code with your friend.

If you want only join a game, follow these steps:
2. Fill in the server's URL. If the server requires a secret, expand Advanced and enter it.
3. Click "Join a Game".
4. Enter the invite code you received from the host and click "Connect".
5. You will see the address where you can connect
6. Connect the game client to that address, here is an example from Stardew Valley:

The server is configured using environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
SECRET |
No | — | Shared secret to restrict access. If unset, the server is open to anyone |
SECRET_FILE |
No | — | Path to a file containing the secret (takes precedence over SECRET) |
LOG_LEVEL |
No | INFO |
Logging level: INFO, DEBUG |
HTTP_LISTEN |
No | :80 |
HTTP listen address (redirects to HTTPS) |
HTTPS_LISTEN |
No | :443 |
HTTPS and HTTP/3 listen address |
HTTPS_ADVERTISE_HTTP3_PORT |
No | 443 |
Port advertised via Alt-Svc for HTTP/3 upgrade |
HTTPS_REDIRECT_PORT |
No | 443 |
Port used in HTTP→HTTPS redirects |
CERT_SELF_SIGNED |
No | false |
Use a self-signed certificate instead of Let's Encrypt (useful for testing) |
CERT_DIR |
No | certs |
Directory where certificates are stored |
CERT_DOMAINS |
Yes (for Let's Encrypt) | localhost |
Comma-separated list of domains for the certificate |
RATE_LIMIT_EVERY |
No | 100ms |
Minimum time between requests for the same IP (e.g., 100ms will allow 10 requests per second) |
RATE_LIMIT_BURST |
No | 10 |
Maximum number of requests allowed in a burst |
When run without arguments, Star starts a local web UI server and opens it in the browser.
Set LISTEN_ADDR to override the default listen address (default: 127.0.0.1 with a system-assigned port).
star [GLOBAL OPTIONS] COMMAND [COMMAND OPTIONS]
| Flag | Required | Default | Description |
|---|---|---|---|
--secret |
No | — | Shared secret matching the registar server's SECRET, omit if the server has no secret |
--registar |
Yes | — | Registar server URL |
--port |
No | 0 |
Local UDP listen port; 0 means system-assigned |
--ca-cert |
No | — | Path to a CA certificate file for the registar server (used for testing or custom setups) |
--log-level |
No | INFO |
Logging level: DEBUG, INFO, WARN, ERROR |
host — Host a Gamestar [GLOBAL OPTIONS] host [OPTIONS]
| Flag | Required | Default | Description |
|---|---|---|---|
--port |
No | 24642 |
Local game port to forward traffic to (defaults to Stardew Valley) |
--key |
No | — | Game key to register; if not set, a new key is generated |
After starting, the game key is printed to stdout. Share it with your friend.
peer — Join a Gamestar [GLOBAL OPTIONS] peer [OPTIONS]
| Flag | Required | Default | Description |
|---|---|---|---|
--key |
Yes | — | Game key received from the host |
--listen-port |
No | 0 |
Local UDP port to listen on for game traffic; 0 means system-assigned |
After connecting, the local address to point your game client at is printed to stdout.
Host a game on a custom port to forward traffic to:
star --registar https://registar.example.com host --port 12345
Join a game:
star --registar https://registar.example.com peer --key XXXX-XXXX
If the server requires a secret:
star --secret mysecret --registar https://registar.example.com host --port 12345
The Registar server acts as a control server and a relay. When both sides join the server, it coordinates the NAT hole-punching to establish a direct P2P connection. If it fails, the server instructs both clients to initiate a relay connection, where the server forwards UDP traffic between them.
Everything above runs on top of QUIC/HTTP3, which makes every communication secured by TLS. For P2P connections, the server acts as a certificate authority (CA). It creates a new intermediate CA for each session and issues leaf certificates for both clients, so they can authenticate each other securely (mTLS).
The Star client is a UDP server that forwards UDP traffic over QUIC datagrams.
TODO: add
See p2ptest/README.md about running integrations tests for NAT hole-punching.
You can use a pre-built curl with HTTP/3 support from https://github.com/stunnel/static-curl.
Run in another terminal:
curl -Lv --cacert ca.crt --http3 http://localhost/-/health