↓
— running of —
— players —
— agents —
— CPU —
🔍 `↑``↓` nav · `Enter` open · `Esc` close

### 📘 RGSP Documentation Refuge Game Server Panel

Get started
👋Welcome 🏛️Architecture 🚀Quick start
Dashboard
🖥️Servers grid 🎮Server tile 🔍Filter strip
Manage modal
⚙Overview Console Players Config Files Backups Schedules Updates Settings
Game-specific
🦖ARK Survival Ascended Mods (CurseForge) Clusters & transfers 🧟DayZ 📦Game modules
Operations
🌐Agents 🛜Networking 🎮Steam Accounts 🎚App settings ⌨️panelctl & admin CLI
Reference
⌨Keyboard shortcuts 🩺Troubleshooting 🛠For developers ⓘAbout RGSP

# Welcome to RGSP

Refuge Game Server Panel (RGSP) is an open-source, self-hosted control plane for game servers. One controller orchestrates many agents; each agent runs Docker on its host and manages game-server containers. Modules describe how each game runs — image, ports, env vars, RCON dialect, ready-line patterns. The panel handles install, start/stop, console, files, configs, backups, schedules, and (where supported) mods + clusters.

💡

Designed for the use case of "I run game servers for friends and want to stop SSH-ing into hosts to babysit them." RGSP doesn't sell you anything; everything ships in the binary. No telemetry, no phone-home, no license server.

### What makes RGSP different

- Multi-agent — one panel, many physical hosts. Add agents on different boxes; each runs its own subset of servers.
- Real INI/properties/JSON/YAML editors per game — not just a "raw text" textarea. Schemas describe each setting; unknown keys are preserved verbatim on save.
- Live console + RCON with autocomplete and a 📖 reference drawer per game.
- Container-friendly — every server is a Docker container; world data lives in named volumes that survive recreates.
- CEL-driven schedules — restart on cron, on player count, on log line, on idle timeout, etc.
- Self-contained — the controller is one Go binary; the agent is another. No JS build step, no node_modules, no DB except a single Postgres.

# Architecture

RGSP is a distributed Controller / Target design. Two binaries:

Component | What it does | Where it runs
--- | --- | ---
Controller | HTTP/SSE web UI, gRPC server for agents, persists state in Postgres, embeds module manifests, dispatches commands. | Anywhere reachable on the LAN. Ports `8080` (HTTP, dev) or `8180` (HTTP, prod), `8443` (gRPC).
Agent | Dials the controller via mTLS, executes Docker operations locally (create / start / stop / delete / exec), streams logs + stats back. | On every host where you want to run servers. Docker daemon must be installed.
Modules | YAML files declaring how each game runs: image, ports, env, RCON adapter, browseable roots, backup paths, ready-line regex. | Loaded by the agent at startup from `/modules/`.

### Data flow

    Browser ──HTTP/SSE──▶ Controller ──gRPC/mTLS──▶ Agent ──docker.sock──▶ Container
                              │                          │
                              ▼                          ▼
                           Postgres                Docker volumes

### State you can rely on

- Containers are ephemeral — recreated whenever launch args / env / ports change.
- Volumes are durable — labeled `panel.instance_id=<id>` and survive every recreate unless the operator explicitly purges them via the Danger Zone.
- Config values live in the DB row's `config_values` column. The recreate dance always re-injects them so per-instance settings persist across container lifetimes.

# Quick start

1. Pair an agent. Click `+ Pair agent` in the sidebar. Copy the install command onto the host where you want to run game servers. The agent registers and shows up in the Agents drawer at the bottom of the dashboard.
2. Create a server. Click `+ New server`. Pick an agent, pick a game, give it a friendly name. Submit. The server is created in installing state — the module's first `update_provider` runs to download the game's binaries.
3. Start it. Click ▶ on the tile (or `▶ Start` in the modal). The status pill goes starting → running when the game's joinable line shows up in the live log.
4. Manage it. Click anywhere on the tile to open the Manage modal. Eight tabs: Console, Players, Config, Files, Backups, Schedules, Updates, Settings.
5. Configure it. The Config tab has launch settings (env vars), network ports, and the per-game INI/properties editor. Stage changes; click `Apply` to commit them all in one recreate.

💡

First-time installs can take 5–30 minutes per game (SteamCMD pacing). Subsequent restarts are seconds.

# Servers dashboard

The main page is a flat dense grid of every server across every agent — no per-agent collapsible panes. Density is configurable via Settings → Card density.

### Hero stats (top of page)

- Running — count of servers in running status, with the total in the hint.
- Players online — sum across all running servers (where RCON returns a roster).
- Agents connected — count of agents whose mTLS session is live.
- Total CPU — CPU cores in use, summed across running servers (a server using 6 full cores adds 6.0). Per-server cards show percent of that host’s whole CPU, with the core count in the tooltip.

The topbar's health pill shows the same running/total + online/total agents in compact form. The pill at the right-side `⚙` opens app settings.

# Server tile

Each server is a glass tile with the game's art as the background:

- Top-left — agent chip (which host this server lives on).
- Top-right — status badge with a glowing dot. running pulses green; starting/stopping/installing/restarting pulse amber; stopped is dim; crashed/deleting is red.
- Bottom panel — server name + uptime, then `module · :port · 12/30 players`, then thin CPU + RAM bars.
- Hover — the art lifts and brightens; three quick-action buttons fade in over the center: `▶`/`■` Start/Stop, `↻` Restart, `⚙` Manage. Two corner pills appear bottom-right: `💾` Backup and `🗑` Delete.
- Click anywhere on the tile to open the Manage modal.

Tile cards diff-render in place — the polling tick never causes flashing. CPU/RAM bars and the player count update live via SSE between polls.

# Filter strip

Above the grid:

- Status pills: All / Running / Stopped / Issues — each shows the live count.
- Agent pills (when more than one agent): filter to one agent's servers.
- Sort dropdown: Name (default) / Status / Agent / Last updated.
- Topbar search: matches against server id / module / agent. Combined with the pills.

# Manage modal

The per-instance modal has a head bar with the server name, status, primary actions, and a tab strip of up to ten tabs (game-specific tabs are hidden when not relevant).

Tab | What you can do
--- | ---
Console | Live log + RCON send. History from the controller's ring buffer; live via SSE.
Players | RCON-driven roster (where the game supports it).
Config | Launch settings, ports, INI/properties editor — staged + single Apply.
Files | Multi-root file browser, edit any text file with a real search bar.
Backups | tar.gz snapshots; back up now / restore / delete.
Schedules | cron + event-CEL triggers per instance.
Updates | Manually trigger the module's update_provider (SteamCMD, etc.).
Settings | Overview, Maintenance (Rebuild container), Danger Zone (Delete + purge).
Mods (ARK / DayZ) | CurseForge / Workshop mod manager — staged + Apply.
Cluster (ARK) | One-click cluster creation + transfer permissions.

## 🖥️ Console tab

Renders log/player/instance_state events from the controller's 500-event ring buffer, then opens an SSE stream for live tail. The line at the bottom is an RCON sender — protocol depends on the module (Source RCON, BattlEye, Telnet, Stdio).

### Autocomplete + reference drawer

Type the first token of an RCON command; a dropdown shows matching commands with short descriptions. ↑/↓ to navigate, Tab/Enter to insert, Esc to dismiss. Click the `📖` button next to `Send` for the full command reference, filterable by name/args/desc.

Supported registries: DayZ, ARK SA, 7DTD, Minecraft Java, Palworld, Rust, Conan Exiles, Valheim. Unregistered modules show a friendly "no reference yet" placeholder.

💡

Console is a pure server-side log view — no panel-status chatter. Connection retries happen silently. When the server is stopped you'll see a subtle one-liner; nothing more.

## 👥 Players tab

Live roster derived from periodic RCON polls (every 30s by default — the agent runs `ListPlayers` / `list` / `lp` per the module's `state_sources`). Auto-refreshes every 5 seconds while the tab is open. Headline shows `online / max`.

Games that don't expose a programmatic roster show a "no data yet — RCON may still be connecting" hint until the first successful poll lands.

## 🎛 Config tab

The Config tab is unified: a single navigation rail on the left, one focused pane on the right, and a sticky Apply footer that batches dirty changes across all three scopes:

- 🚀 Launch settings — container env vars (`SESSION_NAME`, `MAX_PLAYERS`, `BATTLEYE`, etc.). Saving recreates the container. Volumes (world data, saves, configs) survive.
- 🌐 Network ports — host:container port mapping. Edits are validated client-side for collisions before submit. Recreates the container.
- INI editor groups — one rail link per schema group (Identity, Slots & Network, Quality of Life, Cluster transfers, etc.). Writes to the live config file (e.g. `GameUserSettings.ini`). Effective on next start.

### Field cards

Each setting renders as a card with a friendly name, the underlying config key as a ghost caption, an inline hint, and the value control. Empty fields show schema defaults as placeholder text so you can see "what good values look like" without us pre-filling them. Dirty cards get a yellow accent stripe + dot.

### Filter input

Top-right of each pane: type to filter cards on the current pane by name / key / hint. Useful when the INI has 20+ settings in one group.

### Apply

One sticky footer button counts dirty fields across all panes, e.g. "3 changes pending (1 launch, 2 settings)". Click `Apply N changes`; the panel writes the INI first, then runs at most one recreate (for ports + env). If a launch setting declares a wipe path (e.g. changing `MAP_NAME` wipes `SavedArks`), you get a per-wipe-path checkbox in the confirm dialog before anything happens.

⚠

If you stage env and port changes together, Apply runs two recreates back-to-back (~2 min each on heavy games like ARK). Group port edits with each other when you can.

## 📁 Files tab

A multi-root file browser tailored to whatever the module declared in `browseable_roots`. For 7DTD: Saves & Configs + Game Files. For ARK: just ShooterGame/Saved. Single-root modules drop the picker.

- Breadcrumbs at the top show your current path; click any segment to jump.
- Toolbar: `⬆ Upload` (multi-file, 128 MiB cap each), `＋ New file`, `⟳ Refresh`.
- Click a file to open it in the editor (text files only — binary downloads instead).
- Works on stopped containers. The agent lazily spins up `panel-<id>-fs` (a tiny `debian:12-slim + sleep infinity` sidecar) mounting the same volumes, so you can edit configs without starting the game.

### File editor

Large modal (up to 1280×920) with a real search bar:

- `Ctrl`+`F` focuses the find input (suppresses the browser's native find).
- Type to search; the count pill shows `0/N`. `Enter` = next, `Shift`+`Enter` = previous, `Esc` = clear.
- Match navigation does a smooth scroll with an animated pulse highlight on the matched line — focus stays in the search box, so the textarea content is never accidentally edited.
- `Ctrl`+`S` saves the file.

## 💾 Backups tab

tar.gz snapshots of whatever the module's `backup.include` covers (saves, configs, logs — not game install or shared cluster mounts). Click `Back up now` to create one. `Restore` on a snapshot overwrites live data — the panel warns first.

Backups are timestamped and per-instance; nothing is uploaded off-host. Files live under the panel's data dir.

### 📅 Auto-backup: recurring full snapshots

The toolbar's `📅 Auto-backup` dropdown installs a cron schedule that fires the same Back-up-now action on a regular cadence (every 6h / 12h / 24h / weekly). Pick Off to remove it. Each auto-created snapshot is a complete tar.gz, tagged with an Auto chip in the list so you can tell it from a manual one.

### 🌍 SaveWorld RCON: recurring world flushes (ARK only)

For ARK Survival Ascended a separate dropdown `🌍 SaveWorld RCON` appears next to `📅 Auto-backup`. Pick a cadence (every 15 min / 30 min / hour) and the panel installs a cron that fires the `SaveWorld` RCON command. This is not a backup — it just tells ARK to flush its current world to the in-place save files, useful between full backups on busy servers so a crash loses less progress.

RCON only answers while the server is running, so a fire while stopped just no-ops with an error in the schedule log.

Both dropdowns persist as regular schedules — you'll see them on the Schedules tab labeled Auto-backup … and Auto SaveWorld …. Editing or deleting them there is fine; the dropdown re-detects state on next open.

## ⏰ Schedules tab

Per-instance schedules with two trigger kinds:

- cron — standard 5-field cron expression (e.g. `0 4 * * *` = daily 4am).
- event-CEL — fire on observed events: player count, log line, idle timeout, etc.

Each schedule has an action (Start / Stop / Restart / RCON command / Backup). Toggle enable/disable per row.

## ⬆ Updates tab

Lists every `update_provider` declared by the module (usually one — SteamCMD). Click `Update` to dispatch; the agent runs the provider in a sidecar container and streams progress to the Console.

Most modules also auto-update on Restart via the image's built-in updater (acekorneya's ARK image, etc.) — the Updates tab is for forcing a manual run.

## ⚙ Settings tab

Three sections inside the Manage modal's Settings tab (not to be confused with the app-wide Settings cog in the topbar):

- Overview — agent, module, image, run mode, last-updated timestamps. Read-only.
- Maintenance — `🔄 Rebuild container` recreates the container against the latest image without purging volumes. Use after a module's Dockerfile changes.
- Danger Zone — `Delete & purge` stops, removes the container, and wipes the volumes labeled with this instance's id. Irreversible. Confirm dialog spells it out.

⚠

Default delete preserves volumes. Only the Danger Zone purges. The "purge data" toggle was deliberately separated so a routine delete doesn't blow away worlds.

# 🦖 ARK Survival Ascended

RGSP runs ARK SA on the acekorneya/asa_server image (Wine + Proton + the dedicated server). The image has a built-in CurseForge mod downloader and SteamCMD updater.

### Per-instance ARK tabs

When viewing an ARK SA instance, two extra tabs appear in the Manage modal: 🧩 Mods and 🔗 Cluster.

## 🧩 ARK Mods tab

A staged mod manager backed by the CurseForge API (via the `api.curse.tools` proxy — no key needed). Each mod ID lives in either the active (`-mods=`) or passive (`-passivemods=`) launch arg. ASA downloads the binaries on next start.

### Staging flow

The big change from per-mod-recreate: nothing touches the container until you click Apply.

- Search for a mod by name, paste a URL, or paste a project ID. Click `+ Stage`.
- The staged mod appears in the active list with a yellow stripe + "+ to install" badge.
- Click `✕` on an installed mod to stage a removal — it gets a red strike + "– will remove" badge.
- Use `▲`/`▼` to reorder; the load order stages.
- The footer counts pending changes: "3 to install · 1 to remove · load order changed".
- Click `Apply` — one batched stop / delete / create / start, then ASA downloads any new mods on first boot.
- Click `Reset` to throw away all pending changes.
- Switching to a different server's modal drops the staged changes for the previous one.

### Active vs passive

- Active (default) — `-mods=` launch arg. Clients must subscribe; mod content is visible in-game.
- Passive — `-passivemods=` launch arg. Server-only balance / admin tools that don't force a client download.

Tick the "Install as server-side (passive)" checkbox before staging to drop a mod into the passive list.

## 🔗 ARK Clusters & transfers

Clustering lets multiple ARK servers share a transfer directory so survivors / dinos / items can move between maps via obelisks. RGSP makes this one-click:

1. Open any ARK server's Cluster tab.
2. Click `Create cluster` (gives the cluster a generated id + folder under `data/arkcluster/<cid>`) or `Join existing cluster`.
3. Add other ARK servers to the cluster from the same tab.
4. The panel sets `CLUSTER_ID` on each member, bind-mounts the shared folder into each container's default cluster path, and recreates them. Members appear in the registry under `data/ark-clusters.json`.

### Transfer permissions

Per-server controls live in the Config tab → Cluster transfers group. Six toggles, all written to `[ServerSettings]` of GameUserSettings.ini:

- Prevent X uploads — block sending survivors / dinos / items from this server into the cluster.
- Prevent X downloads — block survivors / dinos / items from arriving on this server.

Default is permissive (transfers in both directions). Restart the server after toggling for the new INI to take effect.

⚠

Smoke test for a working cluster: have a player upload a survivor at an obelisk. Within seconds a `<cluster_id>/<steam64>` file should appear under `data/arkcluster/<cid>` on the host. No file = transfers aren't actually writing to the bind mount.

# 🧟 DayZ

DayZ has its own mod manager (Steam Workshop, asynchronous via SteamCMD) plus an XML integration tab that helps merge mod-provided `types.xml` / `cfgspawnabletypes.xml` / etc. into your live mission folder.

### Mods

Search Workshop, click Install — a SteamCMD job queues; progress streams into a per-mod row. When the download finishes, the mod is added to `-mod=`. Existing mods can be uninstalled or reordered.

### XML integration

The XML tab fetches the mod's recommended XML files, shows you a 3-pane diff (current / mod's contribution / preview), and lets you commit the merge to the mission folder. The merger preserves comments and unknown elements so hand-edits aren't clobbered.

# 📦 Game modules

Each game RGSP supports is a module — a YAML manifest declaring how it runs. Currently shipped:

Module | Image | RCON | Notes
--- | --- | --- | ---
ARK Survival Ascended | acekorneya/asa_server | Source RCON | CurseForge mods, clusters, BattlEye
7 Days to Die | panel-7dtd | Telnet | Multi-root files (Saves + Game)
Minecraft Java | itzg/minecraft-server | Stdio | Vanilla / Forge / Fabric / Paper
Valheim | panel-valheim | Source RCON | —
V Rising | panel-v-rising | RCON optional | Full env-config; CLI-flag entrypoint
Empyrion | panel-empyrion | — | Wine + Unity; log-only ready signal
Palworld | panel-palworld | Source RCON | —
Rust | panel-rust | WebRCON | EAC handshake delays "ready"
DayZ | panel-dayz | BattlEye RCON | Workshop mods, XML merger
Conan Exiles | panel-conan-exiles | Source RCON | Match-state ready signal
Project Zomboid, Factorio, Satisfactory, Barotrauma, Enshrouded, Terraria, Core Keeper, Soulmask, Sons of the Forest, Windrose, Dragonwilds | Various — see each module's `module.yaml` for specifics.

23 modules registered as of this writing. Adding a new game is a couple of YAML files + a Dockerfile.

# 🌐 Agents

An agent is a lightweight Go binary that connects to the controller over mTLS gRPC and executes Docker operations on its host. One controller can manage many agents — useful for distributing servers across multiple physical hosts.

### Pairing

1. Click `+ Pair agent` in the sidebar.
2. Copy the install command shown — it embeds a one-time pairing token + the controller's address.
3. Run the command on the host you want to add. The agent installs as a systemd service, registers, and goes online.
4. The new agent appears in the Agents drawer at the bottom of the dashboard.

### Multi-agent setups

The dashboard's filter strip shows a chip per agent — click to filter to that agent's servers. Each tile carries an agent chip in its top-left corner. + New server asks which agent to create on.

### Orphans

If you remove an agent that still has servers, those servers become orphan instances — their `agent_id` doesn't resolve anymore. They appear in the Manage agents drawer marked "orphaned" with a warning style. You can still delete them, but Start/Stop won't work until you re-pair the agent or migrate them.

# 🛜 Networking

### Port allocation

Each module declares a list of ports in its YAML (game / RCON / query / etc.). When you create a server, the agent picks free host ports from the agent's configured range (default 10000–30000) and stores the assignments per-instance. The Config tab → Network ports lets you override any port; the panel detects collisions client-side as you type.

### Internal vs external ports

Ports marked internal in the manifest bind to `127.0.0.1` only and don't show up as configurable knobs on the tile or in the Network ports group. RCON usually rides an internal port — the agent dials it locally, no external exposure needed.

### Behind NAT

Forward each game's external port to the agent host. For multi-NAT setups (e.g. WireGuard over a VPS), make sure the destination-NAT rule's interface matches the path traffic actually arrives on, not just the WAN. The kernel UDP counter trick (`/proc/net/snmp Udp:NoPorts`) helps verify whether forwarded UDP is making it to the agent at all.

# 🎮 Steam Accounts

Click Steam Accounts in the sidebar to manage Steam linkage:

- Paste your SteamID64 (the 17-digit number from your profile URL) to link your panel user to your Steam identity. The panel pulls your avatar via the controller's Steam proxy.
- Admins see a list of all panel users + their Steam linkage status.
- Used by ARK admin OPs, Workshop authentication for DayZ mods, etc.

# 🎚 App settings

The cog button in the topbar (or the Settings link in the sidebar) opens the app-wide settings modal. All values persist to `localStorage` under `rgsp.settings.v1`.

### Display

- Card density — Cozy (340px tiles, fewer per row), Comfortable (300px, default), Compact (240px, max density).
- Show CPU + RAM bars on tiles — toggle off for an art-focused view; stats stay available in the Manage modal.

### Behavior

- Auto-refresh interval — 1s / 2s (default) / 5s / 10s. Slower intervals reduce controller load on large fleets.
- Browser notifications on crash — pops a system notification when a server flips into crashed/error/install_failed. Requests browser permission when enabled.

# ⌨️ panelctl & admin CLI

RGSP ships a single CLI binary, `panelctl`, with two surfaces. The day-to-day surface talks to the controller's gRPC and covers normal lifecycle (start, stop, RCON, watch). The admin surface is HTTP and is meant for out-of-band repair — anything that requires a recreate of the underlying container, like changing an env-config knob or re-attaching an ARK cluster's bind mount, without needing to be logged into the dashboard.

💡

You don't need this for normal use — the dashboard already does everything `panelctl admin` does. Reach for the CLI when the dashboard is down, when you're SSH'd into the controller host doing a recovery, or when you want to script a repair across several servers.

### Operator commands (gRPC)

    panelctl agents
    panelctl instances [agent-id]
    panelctl create   <agent-id> <instance-id> <module-id>
    panelctl start    <agent-id> <instance-id>
    panelctl stop     <agent-id> <instance-id> [grace-seconds]
    panelctl delete   <agent-id> <instance-id> [--purge]
    panelctl update   <agent-id> <instance-id> [provider-id]
    panelctl rcon     <agent-id> <instance-id> <command...>
    panelctl watch    [instance-id]
    panelctl backup   create  <agent-id> <instance-id> [description]
    panelctl backup   restore <instance-id> <backup-id>

Defaults to `--controller localhost:8443`. From a different host you'd point that at the controller's gRPC address.

### Admin commands (HTTP)

    panelctl admin token-show
    panelctl admin status
    panelctl admin instances
    panelctl admin ark-rebind <instance-id>
    panelctl admin set-config <instance-id> KEY=VALUE [KEY=VALUE...]
    panelctl admin del-config <instance-id> KEY [KEY...]
    panelctl admin recreate   <instance-id> [--mount CONTAINER_PATH=HOST_PATH ...]

Three flags govern where the CLI looks:

- `--http URL` — controller HTTP base. Default `http://localhost:8080` (dev). The prod controller listens on `:8180`; pass `--http http://localhost:8180` or set `PANEL_HTTP`.
- `--admin-token TOK` — explicit token. Defaults to `$PANEL_ADMIN_TOKEN`, then to reading `<data-dir>/admin-token`.
- `--data-dir DIR` — where the `admin-token` file lives. Default `./data`; on prod, `/home/refuge/panel/data`.

### How auth works

An admin request is admitted if either:

1. The request comes from loopback (`127.0.0.1` or `::1`) — running the CLI on the controller host needs no token, OR
2. The `X-Panel-Admin-Token` header (constant-time compared) matches the on-disk token.

The token is auto-generated on first controller start and written to `<data-dir>/admin-token` at mode `0600` — only the panel user can read it. There is no rotation API; to rotate, delete the file and restart the controller (a fresh token gets written on the next boot).

⚠️

The admin token is an ambient bearer credential. Anyone who can read the file can drive the admin surface from anywhere. Treat it like an SSH private key — don't paste it into screenshots, chat, or commit it.

### Common recipes

SSH into the controller host first; from there, loopback bypass means no token is needed.

    # See everything the panel knows about, with config-key counts and cluster ids
    panelctl admin --http http://localhost:8180 --data-dir /home/refuge/panel/data instances

    # Change an env-driven config value (triggers a stop → recreate → start dance)
    panelctl admin --http http://localhost:8180 --data-dir /home/refuge/panel/data \
      set-config ragnarok MAP_NAME=Ragnarok_WP

    # Remove an env override entirely (the manifest default re-applies)
    panelctl admin --http http://localhost:8180 --data-dir /home/refuge/panel/data \
      del-config ragnarok CUSTOM_SERVER_ARGS

    # Re-run an ARK cluster's mount recreate (uses the cluster registry to find the right cluster id)
    panelctl admin --http http://localhost:8180 --data-dir /home/refuge/panel/data \
      ark-rebind ragnarok

    # Generic recreate with no config change — cluster mount overrides preserved automatically
    panelctl admin --http http://localhost:8180 --data-dir /home/refuge/panel/data \
      recreate ragnarok

### What it does not do

- No instance create / delete from the admin CLI — use the dashboard or `panelctl create` / `panelctl delete`.
- No agent management — agent restarts disrupt running game streams and need explicit operator action via `systemctl restart panel-agent`.
- No streaming output — handlers block until the recreate finishes (~1–3 minutes typical). For live progress, use `panelctl watch <id>` against the gRPC stream.

# ⌨ Keyboard shortcuts

Where | Keys | Action
--- | --- | ---
Anywhere | `Esc` | Close the topmost modal
File editor | `Ctrl`+`S` / `Cmd`+`S` | Save current file
File editor | `Ctrl`+`F` / `Cmd`+`F` | Focus the in-file search
File editor search | `Enter` | Next match
File editor search | `Shift`+`Enter` | Previous match
File editor search | `Esc` | Clear search + hide highlight
Console | `↑` / `↓` | Navigate autocomplete suggestions
Console | `Tab` / `Enter` | Insert selected suggestion

# 🩺 Troubleshooting

### Server stuck on "starting" forever

Most games take a while to actually become joinable. The pill clears when either the game's RCON answers (most modules) or a known "ready" log line shows up (ARK, Empyrion, Windrose, Conan, Rust). If the server is running cleanly but the pill never clears, the module's `READY_PATTERNS` regex may be too strict — check the Console for the actual ready line; a 10-minute uptime fallback also clears it eventually.

### "No game pictures" on tiles

Local art lives at `/game-art/<module-id>.{jpg,png}` embedded in the controller binary. If a module shows just an emoji + gradient, no art is bundled for that game yet. Fallback to Steam CDN works for most Steam games.

### SteamCMD downloads are slow

Steam's CDN serves at whatever rate it picks; the panel doesn't shape SteamCMD traffic. Slow first installs are normal — subsequent restarts pull only changed files (usually seconds).

### Port collision on Apply

The Config tab's Network ports pane catches duplicates client-side as you type and disables Apply until you fix them. If a collision sneaks through (e.g. another container outside the panel grabbed the port), the agent returns an error toast — pick a different host port.

### Mod added but server didn't load it

ARK and DayZ both download mod binaries on next start after the env changes. Apply queues the change; the actual download happens during the boot sequence. Watch the Console — first-boot mod downloads can take several minutes.

### Agent went offline

The agent reconnects automatically with backoff. If it stays offline, check that the agent process is running (`systemctl status panel-agent` on the agent host) and that the controller's gRPC port is reachable from there. The agent dials the controller — controller never connects to the agent — so firewalls only need to permit outbound from the agent.

# 🛠 For developers

### Repo layout

    controller/cmd/controller/   # the Go binary that serves the UI + gRPC
    controller/internal/...      # DB, events, log buffer, etc.
    agent/cmd/agent/             # the Go binary that runs on each host
    agent/internal/...           # dispatcher, docker driver, RCON adapters
    proto/panel/v1/              # gRPC schema (buf-driven, remote plugins)
    modules/<game>/              # YAML manifest + Dockerfile + entrypoint
    pkg/                         # shared utilities (dayzxml merger, etc.)
    controller/cmd/controller/static/    # the UI: one big index.html
    controller/cmd/controller/static/game-art/   # bundled hero art

### Building

    # cross-compile for Linux from any host
    GOOS=linux GOARCH=amd64 go build -o bin/controller-linux ./controller/cmd/controller
    GOOS=linux GOARCH=amd64 go build -o bin/agent-linux       ./agent/cmd/agent

Static assets (HTML/CSS/JS, art, module YAMLs) are embedded into the binary via `//go:embed` — there is no separate JS/CSS build step.

### Adding a module

1. Make `modules/<game>/module.yaml` declaring image, ports, env, RCON, ready-line, browseable roots.
2. If you need a custom image, add a `Dockerfile` + `entrypoint.sh` in the same dir.
3. Add an entry to `moduleAppearance` in `static/index.html` (emoji + gradient + optional art).
4. Add a `READY_PATTERNS` regex if the module is log-only-ready (no usable RCON readiness signal).
5. Optional: add a `configSchemas` entry for in-panel INI editing, and an `ENV_CONFIG_SCHEMA` entry for env-driven config.

### Source

github.com/dbledeez/panel · Issues / bug reports

# ⓘ About RGSP

Refuge Game Server Panel — open-source, single-binary, multi-agent control plane for self-hosted game servers. No telemetry. No license server. No paid tier. Built because every existing alternative either (a) charges per server, (b) is closed-source, or (c) was clearly designed for a hosting company instead of a person who runs servers for friends.

RGSP is licensed MIT. PRs welcome.

Co-authored with Claude Opus.

—
`Ctrl`+`F` focus · `Enter` next · `Esc` clear

### Confirm

### 📦 Backup contents

📂
Click a file in the tree to view its contents.

### ⚙️ Settings

#### Panel UI

Interface
You're on the New panel (command-center). Prefer the original layout? Switch back to Classic any time — your choice is remembered on this device.

#### Display

Card density
Smaller tiles fit more servers per row.
Show CPU + RAM bars on tiles
Hide them for a cleaner art-focused view. Stats stay on the modal.
Enabled

#### Behavior

Auto-refresh interval
How often the dashboard polls the controller for updates.
Browser notifications on crash
Pop a system notification when any server crashes.
Disabled

#### External port forwarding (opnfwd)

Auto-forward server ports to opnfwd
When enabled, every new server's public ports are auto-added as port forwards on opnfwd; deletes auto-clean. Internal-only ports (RCON, telnet) are skipped. Off = no panel-side mutation.
Disabled
opnfwd base URL
e.g. http://<opnfwd-host>:14995 on your LAN, or an HTTPS URL via reverse proxy.
API token
Bearer token from opnfwd's secrets/config.toml [api] token. Leave blank to clear.
Skip TLS verify
Only check this if your reverse proxy uses a self-signed cert. Leave off for LAN HTTP and trusted Let's Encrypt.
Off
Status
Click Test to round-trip the URL + token.
—
Tracked forwards
How many opnfwd rules the panel currently believes it owns. Reconcile diffs panel + opnfwd to surface drift (rules added/deleted out of band).
—

#### About

R
Refuge Command Center v0.x · build —
Refuge Game Server Panel — open-source Go panel for self-hosted game servers across multiple agents. Distributed Controller/Target architecture, Docker runtime, CEL automation, — game modules registered.
📘 Docs 🐛 Report a bug

### ＋ Create a new server

Name
Server ID: —
Spaces OK — auto-converted to a Docker-safe id below.
Agent
Options
Pick a game every module registered on this controller

### New schedule

Server
When
Action
Grace
seconds (for stop/restart)
Clean backup
Take a clean backup while the server is stopped Server stays offline until the tarball finishes, then starts again.
Warn players
Send RCON warnings before restart
Start warnings minutes before restart, repeating every minute. Final emphasis at 2 min and 1 min.
Message template (`{min}` is replaced with remaining minutes):
Final-warning template (at 2 min & 1 min):
The schedule itself fires {{lead}} minutes earlier than the time shown above so the actual restart still lands on the chosen time.
Blood moon
Defer restart if blood moon is active (7DTD) When the scheduled time hits, the panel reads the in-game blood-moon state from the PanelBloodMoonAgent mod. If a horde is happening (or starts within the warning window), the restart is postponed until 15 real minutes after the horde ends, with a single in-game broadcast telling players what happened.
Note

### Pair a new agent

Generate a one-time token, then run this on the machine you want to add:

Run on the new host:

Then start the agent normally — it'll auto-detect the cert files and connect with mTLS.

Description
Expires

### 🎮 Steam login required

This module uses a SteamCMD app that won't download anonymously. Provide a Steam account that owns the game in its Steam library — the panel stores your password encrypted at rest and only uses it for SteamCMD.

📱 Watch your Steam Mobile app: When you click "Sign in to Steam", a push notification will appear in your Steam Mobile app — tap Approve there. The button below will sit on "Waiting for Steam Guard approval…" for up to 90 seconds. If you don't see the push within ~10 seconds, double-check your phone has the Steam Mobile app open with the Authenticator section visible.
Multi-agent note: if the install runs on a different host than the controller, you'll get a second push when the agent actually starts the download — both need to be approved.
Leave the code field below blank unless Steam Guard is via email (5-character code).
Steam username
Password
Steam Guard code (optional)
Leave blank — modern Steam Guard mobile sends a push notification you approve in the app. Only fill this in if your account uses email 2FA (5-char code) instead of the mobile app.

The password is encrypted with AES-GCM keyed on the panel's internal CA key before it hits disk. It's never logged, never echoed in the Console tab, and never returned to the browser once stored. SteamCMD caches an SSFN device-auth blob after a successful 2FA challenge so subsequent updates skip the code prompt.

### Mod info

loading…

### 🎮 Steam Accounts

Your Steam Account
Other Users

### 📚 Template Library

loading…

### 💾 Backups

loading…

### Delete server

🎮
⚠ This is destructive
The container is removed and (if purge is on) every Docker volume tagged for this server is wiped — world saves, configs, logs, the lot. There is no undo.
Also wipe Docker volumes (world data, saves, configs)
Type the server id to confirm:

### Edit agent

Agent ID
Label
Notes
Port range
–
Host-port window the panel allocates from when creating new servers on this agent. Stopped instances reserve their assigned ports too — operator intent isn't lost when a server is offline. Empty = global default 7000-9999.
LAN IP
RFC1918 IP this agent's host listens on. Used as the target for opnfwd auto-forwards (Settings → External port forwarding). Leave blank to disable opnfwd integration for this agent.
🎮
—
— 🌑 Blood Moon
—
👥—players ⚡—CPU ▤—RAM ↑—uptime
Live server output auto-scroll
Installing — first-run setup
Preparing…
Image build runs once per agent (30s–3min). SteamCMD download follows (size depends on the game — Empyrion ≈ 19 GB). Live progress lands in this console as it happens.

Live roster. Refreshes every 5s while this tab is open.
Game settings + ports. Changes take effect on next restart.
⬇️ Drop files here to upload to this folder
Uploading…
Save snapshots of worlds, configs & saves — game install rebuilds itself on next start. 📅 Auto-backup:
🌍 SaveWorld RCON:
Backing up… 0s
starting sidecar…
Automate tasks: auto-backup, idle shutdown, crash alerts, scheduled restarts.
⬆
loading…
loading…
loading…
loading…
loading…
loading…
Corrupt regions in the active world. Heal restores a region from the newest clean backup (stops, swaps, restarts). 🩺 Mode:
loading…
loading…
loading…
loading…
⌨ 1–9 · 0 · [ ]
loading…
loading…