# Rust — panel-native module

Panel-native Rust dedicated server, native-Linux (no Wine), debian:12-slim base.

- **Image:** `panel-rust:latest` — built from `./Dockerfile`.
- **Game files:** downloaded by the panel's SteamCMD updater sidecar (app 258550) into the `panel-<id>-game` named volume. Fresh install is ~7 GB; first-ever boot additionally generates a ~500 MB map on the save volume depending on `SERVER_WORLD_SIZE`.
- **Saves:** `panel-<id>-saves` at `/game-saves`. Entrypoint symlinks `<game>/server` into `/game-saves/server/` so SteamCMD validate can't wipe worlds.
- **RCON:** **WebSocket RCON** (Facepunch dropped Source RCON years ago). Panel speaks this via the `websocket_rcon` adapter — `ws://<ip>:28016/<password>` with JSON-framed `{Identifier, Message, Name}` requests. Authenticated via the password in the URL path, not headers.

## Ports

| Port | Proto | Purpose |
|------|-------|---------|
| 28015 | UDP | Main game traffic |
| 28016 | TCP | WebSocket RCON (internal) |
| 28082 | TCP | Rust+ push notifications (optional — only needed for the companion app) |

## RCON commands wired

- `list` → `playerlist`
- `broadcast {msg}` → `say {msg}`
- `kick {player}` → `kick "{player}"`
- `ban {player}` → `banid {player}`
- `save` → `server.save`
- `shutdown` → `quit`

## Log events captured

- **Join:** `<name>[<pid>/<steamid>] has entered the game`
- **Leave:** `<steamid>/<name> disconnecting: <reason>`

## Environment vars

| Var | Default | Notes |
|-----|---------|-------|
| `SERVER_NAME` | `panel Rust` | Shown in the Rust server browser |
| `SERVER_DESCRIPTION` | `Powered by panel` | Browser description / tooltip |
| `SERVER_IDENTITY` | `panel-rust` | Save-tree subdirectory name |
| `SERVER_SEED` | `12345` | Procedural-gen seed |
| `SERVER_WORLD_SIZE` | `3000` | 1000–6000; smaller = faster |
| `MAX_PLAYERS` | `50` |  |
| `RCON_PASSWORD` | `panel_rcon` | RCON auth (URL path) |
| `SERVER_LEVEL` | `Procedural Map` | Map preset |
| `SERVER_URL` | `` | Website URL shown in the browser |
| `SERVER_HEADER_IMAGE` | `` | 512×256 banner image URL |

## Known gotchas

- First-ever boot takes 2–5 min to generate the world. No players can connect until generation finishes (log: `Server startup complete` is the signal).
- RCON port is published on the container only (panel talks to it over the Docker bridge). Don't expose 28016 externally — password in URL means anyone who sees the URL has full server control.
- If you change `SERVER_WORLD_SIZE` or `SERVER_SEED` on a running server, the next boot wipes the existing world. Rust ties the world to a seed+size pair per identity.
- `libgl1` + a handful of X libs are installed in the image even though the server runs headless; Rust's Unity runtime `dlopen`s them at startup.
