Instant lobby-based multiplayer connectivity via WebRTC DataChannels with coturn fallback: a Go signaling/lobby server plus TypeScript (browser) and Rust (native) client libraries. Full design spec: lobbylink_implementation_guide.md.
- Go server (
cmd/p2p-lobby-server,internal/): implemented, with unit + integration tests. - TypeScript client (
clients/ts): implemented — zero runtime dependencies, built by plaintsc(makeinclients/ts), browser test suite passes against production including forced-TURN relay. See clients/ts/README.md for the API and the wire/framing contract. - Rust client (
clients/rust): next.
CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=$(git rev-parse --short HEAD)" \
-o dist/p2p-lobby-server ./cmd/p2p-lobby-server
go test -race ./...The binary embeds web/ (demo page + browser client bundle). Build the
TypeScript client first when you want the real web/p2p-client.js
instead of the placeholder.
./dist/p2p-lobby-server \
--listen-http 127.0.0.1:8787 \
--allowed-origin http://localhost:8787,http://127.0.0.1:8787 \
--public-url http://127.0.0.1:8787Then open http://127.0.0.1:8787/ for the demo page, or check
/healthz and /config.json.
Defaults < --config /etc/p2p-lobby/config.toml < CLI flags. See the
config example in the implementation guide §2.3; every key there is
supported, and unknown keys are rejected so typos fail fast.
Key server behaviors:
- WebSocket
Originmust be on the global allowlist (or an app allowlist forappIdjoins); native clients without an Origin header are only accepted with--allow-no-origin/allow_no_origin = true. - Stable player IDs
0..maxPlayers-1; disconnects preserve slots. - Hidden resume tokens (rotated on every resume/claim; only hashes are
stored server-side) plus timeout-based slot claiming (
claim_after, default 40s of silence; any WS message counts as liveness). - TURN credentials are minted per player with coturn's REST-secret
scheme (
use-auth-secret).
Other binaries can link the lobby in and mount their own HTTP routes next to
it ("plugins") through the public lobbyserver package — the stock
cmd/p2p-lobby-server runs through the same package, so embedders get the
exact production serving behavior. The lobby stays game-agnostic; anything
game-specific lives in the embedding module.
cfg, _ := lobbyserver.LoadConfig("server.toml") // or DefaultConfig()
cfg.SetListenHTTP("127.0.0.1:8787")
srv, _ := lobbyserver.New(cfg, version)
mux := http.NewServeMux()
mux.Handle("/mygame/", myGameRoutes(cfg.AllowedOrigins()))
mux.Handle("/", srv.Handler())
err := srv.Run(ctx, mux) // listeners + room GC + graceful shutdownRecommended: the self-contained kit — everything in ~/lobbylink
on the server, run as a systemd user service, with dual entry points
(Apache subpath proxy + standalone :4443). Step-by-step guide and
all scripts: deploy/selfcontained/README.md.
Alternative system-wide shapes from the guide (§10–11):
- Apache reverse proxy — Apache owns
:443, proxies/and/wsto Go on127.0.0.1:8787:scripts/setup-apache-proxy.sh <domain>. - Direct HTTPS on :4443 — Go serves TLS itself with copied
Let's Encrypt certs:
scripts/setup-direct-4443.sh <domain>.
Common steps first:
sudo scripts/install-common.sh <domain> # user, dirs, secret, config
sudo scripts/copy-certs.sh <domain> # also a certbot deploy hook
sudo scripts/setup-coturn.sh <domain> <ip> # coturn with shared secretTemplates for systemd/Apache/coturn live in deploy/.