Self-hostable game-server control panel: controller + agent + 26 game modules. One-line install (prebuilt release, no Go required): curl -fsSL https://git.pdxtechs.com/dbledeez/panel/raw/branch/main/install.sh | sudo bash Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4.9 KiB
panel
Open-source Go-based game-server management panel (AMP-class). Distributed Controller/Target over gRPC, Docker runtime, CEL-driven automation, 26 game module directories, live dashboard + panelctl CLI. Single static binary per component.
First thing every session
Read the latest memory/changelog_*.md (the file with the most recent date in its name) before doing anything else. Each changelog covers what shipped in a previous autonomous session — bugs, root causes, files touched, and live one-off cleanups that didn't survive a recreate. Skipping this means re-discovering bugs you already fixed last time.
If the user opens with a generic ask ("what should we work on?", "any bugs left over?"), the changelog's "What still needs to happen" tail section is the answer.
Start here
- README.md — public overview: quickstart, modules, panelctl commands, architecture diagram.
- memory/ — project-specific deep refs migrated from the user's global auto-memory. Covers repo layout, build commands, module quirks, UX preferences, gotchas. Always start with
memory/README.md— it's the index pointing at every other deep ref + every changelog.
Component entrypoints
- Controller:
./controller/cmd/controller(port 8080 web / 8443 gRPC). Embeds DB migrations, admin bootstrap, static dashboard. Default UI is the Refuge Command Center split assets:static/new.html(shell, 1,723 lines) +static/rcc.js(18,928 lines) +static/rcc.css(5,727 lines), served hash-stamped/immutable.static/index.html(23,982 lines) is the frozen classic UI — opt-in viapanel_ui=classiccookie, no new features land there. - Agent:
./agent/cmd/agent(any host with Docker; dials controller). Loads modules from./modules/, talks to local Docker daemon. - CLI:
./controller/cmd/panelctl—agents | instances | create | start | stop | update | backup | rcon | watch …plusadmin <subcommand>(HTTP, see memory/admin_cli.md).
Build + run (dev)
export PATH="/c/Program Files/Go/bin:$PATH"
cd /c/Users/dbled/sources/panel && \
go build -o /c/Users/dbled/AppData/Local/Temp/controller.exe ./controller/cmd/controller && \
go build -o /c/Users/dbled/AppData/Local/Temp/agent.exe ./agent/cmd/agent
powershell -Command "Get-Process -Name controller,agent -ErrorAction SilentlyContinue | Stop-Process -Force"
# Then relaunch each (see memory/build.md for full cycle)
Static/HTML/CSS/JS changes for the default UI live in controller/cmd/controller/static/new.html + rcc.js + rcc.css — all embedded via //go:embed, so controller rebuild+restart is required on any change. Do NOT add features to static/index.html; the classic UI is frozen.
Gate before any controller deploy: node controller/.test/gate-parse.mjs — extracts and parses every inline <script> block in static/{new,index,login}.html (a syntax slip blanks the whole dashboard; go build can't catch it because the HTML embeds as opaque bytes). For rcc.js itself, node --check controller/cmd/controller/static/rcc.js. (controller/.test/ui.spec.mjs is the Playwright suite; it has not had a first real run yet.)
Running Go tests: the repo is a go.work multi-module workspace — go test ./... from the root does NOT cover everything. Invoke per module dir explicitly: go test ./controller/... ./agent/... ./pkg/... (or cd into each). Known baseline: 1 pre-existing failure TestDialerForUnknown + 1 go vet copylock nit in agent/internal/dispatch/dispatch.go (PortMap).
Golden rules
- Never switch
buf.gen.yamlto local plugins. Smart App Control blocks locally-builtprotoc-gen-*.exe. Stay on remote buf plugins. - When adding a new Agent→Controller reply type, wire it into BOTH the first
switch(type-specific) AND the secondcaseinhandleAgentMessagethat delivers by correlation ID. Missing the second = silent timeout. - Docker image rebuild ≠ container rebuild. Containers pin to image ID at create time. Use Settings → Maintenance → Rebuild container, or the API delete-preserve-volumes + create dance.
- The module manifest loader uses strict
KnownFields(true)(pkg/module/loader.go:22). An old agent binary that doesn't know a new module.yaml key (e.g.ready_pattern,appearance) will REJECT the whole manifest. Ship agent binaries BEFORE or WITH any module.yaml sync that adds new keys. - SteamCMD exit 2/5/8 is transient. Already auto-retried in
steamcmd.go. Don't surface as a user error. - Volumes survive container delete unless
purge=true. The Delete button's Danger Zone purges. Keep defaultpurge=falsefor world safety. - User prefers local art over CDN hotlinks, gradients + animations over flat UI, sub-tabs for any config >10 fields, no wrapper headings for single items, transparent retry on flaky externals. Full list in memory/ux_rules.md.