Contributing
DiscoPanel lives at github.com/discohaus/discopanel. Issues and pull requests are welcome. For questions, the Discord is faster.
How the project is put together
Section titled “How the project is put together”The panel is one Go process: it embeds the SvelteKit web UI, keeps its state in SQLite, and talks straight to the Docker Engine API. Everything you interact with - the API, the provisioner, the proxy, the scheduler, the module orchestration - is in this repository.
Two pieces ship as container images built from private source:
- discoruntime (
ghcr.io/discohaus/discoruntime) - the minimal image every Minecraft server runs on. The panel installs server software into the data directory, and the container just supervises Java. - discomodule (
ghcr.io/discohaus/discomodule-*) - the built-in module images (Geyser, Steam Bridge, Playit.gg, Doctor, and friends).
The contracts between the panel and those images are public and live here, so panel changes never chase private code:
proto/discopanel/agent/v1- the telemetry stream between a running server container and the panel.proto/discopanel/v1/runtime.protoandpkg/runtimespec- the file contract (launch.json,agent.json, the manifest) the panel writes into each server’s.discopanel/folder.proto/discopanel/v1/doctor.proto- the shared journal contract for the Doctor module.pkg/mcprotoandpkg/moduleprompt- helper libraries for anyone building their own module.
Panel code never imports the private repos - make check fails the build if it does.
Toolchain
Section titled “Toolchain”- Go 1.25+
- Node.js 22+
- Docker - protobuf generation runs buf in a container, and the tests that touch containers need an engine.
- Make
A Nix dev shell with all of the above is available via nix develop.
First build
Section titled “First build”git clone --recurse-submodules https://github.com/discohaus/discopanel.gitcd discopanelmake gen # generate code from the protosmake deps # go mod download + npm installmake dev # backend (go run) + frontend (vite dev) togethermake dev also resets the local database from dev/discopanel.db (a seeded dev state). Use make run to keep your current data. The backend listens on 8080, the Vite dev server proxies to it with hot reload.
The protobuf workflow
Section titled “The protobuf workflow”The .proto files in proto/discopanel/ are the source of truth for the entire API and every persisted data model - storage.proto holds all of the latter. make gen regenerates everything: the Go server code (pkg/proto), the TypeScript clients (web/discopanel/src/lib/proto), and the GORM storage layer (protogorm generates internal/db/store.gen.go from the same protos). None of it is ever edited by hand.
- After changing a proto, run
make gen(cleans and regenerates all of it). make proto-lintandmake proto-formatkeep the definitions tidy.make proto-breakingchecks your branch againstmainfor breaking API changes.
Tests and checks
Section titled “Tests and checks”make test # go test ./...make lint # buf lint + frontend eslint/prettiermake check # svelte-check plus the private-import guardWhat pull requests should look like
Section titled “What pull requests should look like”- Complete features. No TODOs, no placeholders, no “wire this up later”. If a change spans backend and frontend, ship both halves.
- One implementation per concept. Before adding a structure, a helper or an event type, read how the existing code handles the same concern and extend that instead. Parallel implementations of the same idea get rolled back.
- Respect the ownership boundaries. Server state transitions go through
lifecycle.Manager, events through thepkg/eventsbus, container work throughinternal/docker. Don’t reach around them. - Regenerate, don’t hand-edit. If your diff touches generated code without a matching proto change, something went wrong.
- Target
main, keep the diff focused, and say in the description what you tested.
This documentation site lives in docs/discopanel/ (Astro + Starlight). make dev-docs serves it locally on http://localhost:4321. If something here is wrong or missing, open an issue or mention it in Discord.
The UI screenshots are captured by docs/discopanel/scripts/screenshots.mjs against a running panel. Point it at one with a server or two and it refreshes every image in src/assets/screenshots/:
cd docs/discopanelPANEL_URL=http://localhost:8080 PANEL_USER=admin PANEL_PASS=... node scripts/screenshots.mjs