# 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](README.md)** — public overview: quickstart, modules, panelctl commands, architecture diagram.
- **[memory/](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 via `panel_ui=classic` cookie, 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 …` plus `admin <subcommand>` (HTTP, see [memory/admin_cli.md](memory/admin_cli.md)).

## Build + run (dev)

```bash
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.yaml` to local plugins.** Smart App Control blocks locally-built `protoc-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 second `case` in `handleAgentMessage` that 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 default `purge=false` for 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](memory/ux_rules.md).
