dune-admin is a local-first, provider-aware terminal agent designed to manage Dune Awakening private servers efficiently. It maintains Claude Code wire compatibility while introducing innovative behavior and architecture.
Key Features:
Local-First Functionality: Operates effectively in offline environments with synchronized data updates when online.
Provider Awareness: Supports various server topologies including AMP, Kubernetes, Docker, and bare-metal installations.
Claude Code Compatibility: Ensures seamless integration with existing Claude Code infrastructure.
Configuration Wizard: Simplifies setup with guided prompts for provider-specific settings.
Market Bot Integration: Facilitates automated market operations with adjustable parameters for optimal performance.
Audience & Benefit:
Ideal for server administrators managing Dune Awakening private servers, dune-admin streamlines server management. It offers a simplified setup process, real-time monitoring through the Market tab, and flexible deployment across different environments. Installation is straightforward via winget, ensuring ease of use and adaptability to various infrastructures.
This tool empowers administrators with efficient management capabilities, enhancing server operations and user experiences.
README
dune-admin
Web-based admin panel for a Dune Awakening private server. Works against any deployment topology: CubeCoders AMP (podman or docker), k3s/k8s over SSH, Docker containers, or a bare-metal install.
🙏 Thank You
@dakkinbyte deserves special recognition for substantial ongoing contributions: bug fixes, feature work across the dashboard, issue triage, and keeping the project moving. The breadth and consistency of that work has made a real difference.
The RabbitMQ server command integration in dune-admin (the envelope format, auth token, AMQP publish path, and the complete catalogue of working server commands) was made possible by the research and live-testing work done in that project. Without it, we would not have known which commands actually work over MQ, what the correct field names are, or that the outer envelope must be base64-encoded before publishing via rabbitmqctl eval.
If you run a Dune Awakening private server, check out their project, a full-featured dedicated server manager with a Tauri desktop frontend.
Quick install
On a fresh Ubuntu 22.04 / 24.04 host with passwordless sudo and your game-server stack already running:
The script detects your OS and architecture, downloads the matching binary from the latest GitHub release, and installs it into /opt/dune-admin/. No build toolchain required — the release binary already embeds the frontend SPA. It refuses to overwrite a running service and leaves a .prev backup for one-step rollback. See --help for flags (--version, --install-dir, --service-user).
When the script finishes it prints the next manual steps: run the setup wizard, apply the sudoers entry it generates, drop a systemd unit, and start the service.
For development or non-Ubuntu hosts, see Manual build.
Providers
Pick the provider that matches your game-server topology. Each guide covers prerequisites, wizard answers, and provider-specific config keys.
After install, configure dune-admin with the built-in wizard:
cd /opt/dune-admin
./dune-admin -setup
It asks which control plane to use, then prompts for the settings that provider needs (instance name, paths, DB credentials, etc.). When you select amp, the wizard auto-detects instances via ampinstmgr -l and pre-fills prompts with discovered values, so you can typically accept the defaults straight through. For container topology it also probes the container to discover the actual game install path, so the wizard isn't pinned to any one AMP module's directory layout.
When done it writes ~/.dune-admin/config.yaml (mode 600, never committed) and prints a sudoers entry to copy into /etc/sudoers.d/dune-admin.
Re-run the wizard any time your configuration changes.
Deploy modes
The same binary supports three deployment shapes:
Single-binary on a host (AMP, local Go, k3s port-forward)
The binary serves both the API and the SPA from ./dist next to itself. scripts/install.sh lays this out for you. The simplest model: one process, one port, no CDN.
k3s / k8s cluster
A unified manifest deploys dune-admin into a cluster with PostgreSQL and RabbitMQ alongside. Helper scripts handle kubeconfig pull, image build/import, manifest render/apply, and port-forward:
./deploy.sh # macOS / Linux
./deploy.ps1 # Windows
Run the binary as an API-only process and serve the SPA from Cloudflare Pages (or any static host). The SPA prompts for a backend URL on first load, stored in localStorage. The backend adds CORS headers automatically, no extra config needed.
make deploy-web # builds and pushes the SPA to Cloudflare Pages
Modern browsers allow HTTPS pages to reach HTTP localhost without mixed-content errors, so https://your-site.pages.dev → http://localhost:8080 works out of the box.
Configuration
Config is loaded in this order (first match wins per field):
~/.dune-admin/config.yaml, written by dune-admin -setup
.env in the working directory, legacy fallback for existing installs
Environment variables
Command-line flags
Common fields
Env var
Flag
Default
Description
CONTROL
-control
(auto)
Control plane: amp, kubectl, docker, or local
SSH_HOST
-host
-
VM host:port, tunnels all connections through SSH when set
SSH_USER
-user
dune
SSH user
SSH_KEY
-key
(auto-detected)
SSH private key path (library mode only)
SSH_MODE
-ssh-mode
library
SSH transport: library (golang.org/x/crypto/ssh) or command (shells out to the OS ssh client — honours ~/.ssh/config, ProxyJump and ssh-agent; never reads a private key)
SSH_EXTRA_OPTS
-ssh-extra-opts
-
Extra ssh -o options for command mode (space-separated)
DB_HOST
-dbhost
127.0.0.1
PostgreSQL host or Docker DNS name
DB_PORT
-dbport
15432
PostgreSQL port
DB_USER
-dbuser
dune
PostgreSQL user
DB_PASS
-dbpass
-
PostgreSQL password
DB_NAME
-dbname
dune
PostgreSQL database name
DB_SCHEMA
-schema
dune
PostgreSQL schema
CONTROL_NAMESPACE
-control-ns
(auto-discovered)
K8s namespace (kubectl only)
AUTO_DISCOVER
-auto-discover
false
Fill empty DB/RMQ/Director fields from the running game-server process args at connect time (command mode + kubectl). Explicit config always wins
BROKER_GAME_ADDR
-broker-game
-
mq-game broker host:port
BROKER_ADMIN_ADDR
-broker-admin
-
mq-admin broker host:port
BACKUP_DIR
-backup-dir
-
Backup directory path
LISTEN_ADDR
-addr
:8080
HTTP listen address
SCRIP_CURRENCY
-scripcurrency
1
Scrip currency ID
Provider-specific fields (docker_gameserver, amp_instance, cmd_start, etc.) have no env var equivalents and are set via the wizard or config.yaml directly. See the provider guides for the full list.
Dashboard authentication
Off by default — without it, anyone who can reach the dashboard port has full control of your
game server. If you expose dune-admin over a public domain or IP, enable it:
auth_enabled: true
# Method 1 — local username/password (set the hash with: dune-admin --set-password)
auth_local_username: admin
auth_local_password_hash: "$2a$10$..." # bcrypt; never store the plaintext
# Method 2 — Discord OAuth (bring your own Discord application)
auth_discord_enabled: true
auth_discord_client_id: "your-app-client-id"
auth_discord_client_secret: "your-app-client-secret"
# Reuses discord_bot_token + discord_guild_id for membership/role lookup.
# Add scheme://host/api/v1/auth/discord/callback as a redirect in the Discord app.
# Owners always get full access: the guild owner, the local account, plus:
auth_owner_discord_ids: "" # comma-separated Discord user IDs
auth_owner_role_ids: "" # comma-separated Discord role IDs
auth_session_ttl_hours: 24
All login methods (local, Discord, guest) issue a signed HttpOnly session cookie; no
credentials are stored in the browser. Local and Discord login can be enabled together.
Discord users must be members of discord_guild_id. Their roles map to ~28 fine-grained
capabilities (e.g. players:write, broadcast:send, battlepass:manage) via the
Permissions tab; the matrix persists to ~/.dune-admin/permissions.yaml. The special
Guests and Default member rows extend what anonymous guests / unmapped members get.
Additional local users (username/password, no Discord) live in dune-admin.db and are
managed from the Permissions tab with per-user capability assignments.
The Permissions tab is editable by owners and anyone granted auth:manage.
Every session starts from a minimal floor: player/world/market read access, server status,
and the battlepass track. Everything else — events, welcome kits, bot status, schedules,
exports, config, database, logs, backups — requires an explicit grant.
Role changes in Discord are picked up within 15 minutes; kicked members lose access on the
same schedule.
Every authenticated mutation is recorded in ~/.dune-admin/audit.log (JSON lines).
Locked out (Discord misconfigured, password lost)? Run dune-admin --set-password on the
host — it works offline and writes straight to config.yaml.
The login endpoint is rate-limited (5/min per IP) and security headers (CSP, X-Frame-Options,
HSTS behind TLS) are emitted while auth is enabled.
TLS is still your reverse proxy's job (Caddy, nginx, Cloudflare Tunnel) — dune-admin marks
cookies Secure automatically when it sees X-Forwarded-Proto: https.
Market bot
dune-admin runs the market bot embedded, an in-process goroutine that shares the main DB pool. Enable in config.yaml:
market_bot_enabled: true
market_bot_cache_db: ~/.dune-admin/market-bot-cache.db # auto-created (SQLite, pure Go)
market_bot_item_data: ./item-data.json # falls back to standard search paths
market_bot_buy_interval: 5m
market_bot_list_interval: 30m
market_bot_buy_threshold: 1.05
market_bot_max_buys: 50
The Market tab's lifecycle buttons map to in-process actions:
Start -> Resume(): flips the bot's enabled flag back on
Stop -> Pause(): flips the bot's enabled flag off (goroutine stays resident)
Restart -> Restart(): pauses, reinitializes the exchange, resumes
Config edits in the Market tab apply directly to the live runtime config. A full process restart only matters when changing the cache DB path or item-data path.
An opt-in feature that auto-grants a configured item package to every player once, on first login. Manage it in the Welcome Kits tab, or in config.yaml:
dune-admin keeps a library of named packages plus an active-version pointer. An in-process scanner grants the active package once per (player, version), tracked in a persistent SQLite ledger at ~/.dune-admin/welcome-package.db (so a restart never re-grants). Bumping the active version re-issues to everyone. It defaults off (it mutates every player's inventory) and delivers items through the same live-RMQ + DB-fallback path as manual give-items.
Message of the Day (MOTD). The same tab (Welcome Kits → Config) also has a separate Message of the Day card: an in-game message whispered to a player from the GM persona every time they join — every session, unlike the once-per-version welcome above. It's decoupled from the package system, so it works even with no active package. Use {player} in the message for the player's character name, and leave the sender blank to use the seeded GM persona. A presence tracker diffs the online set on each scan tick to detect joins, and seeds a silent baseline on startup so a restart never re-messages players already in-game. Defaults off; configure and toggle it live (no restart) in the UI.
Server settings
The Server Settings tab manages gameplay config (mining/vehicle output, PvP & security zones, sandstorm/sandworm toggles, building limits, item deterioration, server name/password, …). How it writes depends on the provider:
amp: settings are written through AMP's Web API, because AMP regenerates the game INIs from its own config on every start (a direct file edit would be clobbered). This requires AMP API credentials in config.yaml:
amp_api_user: admin # an AMP panel login for the instance
amp_api_pass: yourpassword
amp_api_port: 8081 # instance ADS API port (default 8081)
A game restart applies them; dune-admin's Restart recycles the AMP container ( restart, matching amp_container_runtime), which is what actually cycles the game processes. The container-runtime sudoers grant must allow this. See SETUP_AMP.md.
docker / kubectl / local: settings are written straight to UserGame.ini / UserEngine.ini; no API credentials needed.
Either way, a server restart is required for changes to take effect.
SSH key lookup order
When SSH_KEY / -key is not set, dune-admin checks these paths in order:
./sshKey
~/.dune-admin/sshKey
~/.ssh/dune
~/.ssh/id_ed25519
~/.ssh/id_rsa
For building your ~/.ssh/config itself — modular config.d layout, jumphost (ProxyJump) chains, per-platform ssh-agent setup, and connection multiplexing — see SETUP_SSH.md.
View live market listings; control the embedded market bot
Welcome Kits
Auto-grant a configured item package to every player once, on first login — plus an optional Message-of-the-Day whispered on every join
Manual build
For development or if you'd rather not use scripts/install.sh:
git clone https://github.com/Icehunter/dune-admin
cd dune-admin
# Frontend (Vite + Rolldown, needs node-linker=hoisted for the native binding)
echo 'node-linker=hoisted' > web/.npmrc
cd web && pnpm install --frozen-lockfile && pnpm build && cd ..
# Backend
make linux # cross-compile a Linux amd64 binary (dune-admin-linux)
# or
make build # host OS binary (bin/dune-admin), plus the frontend build
Then run the setup wizard:
./dune-admin -setup
Prerequisites: Go 1.26+, Node 20.19+ or 22.12+, pnpm 10.28+, make.
> Windows note:make build works from PowerShell or cmd.exe as long as
> GNU Make is installed (e.g.
> winget install GnuWin32.Make or choco install make). The binary is named
> dune-admin.exe. For make dev, make verify, and the make version-* targets,
> run from a Git Bash shell (right-click the folder -> "Git Bash Here"); those
> recipes use POSIX shell features that cmd.exe can't run.
Building without a HeroUI Pro license
The frontend uses @heroui-pro/react, which requires a HEROUI_AUTH_TOKEN to install. If
you only need to run the app (not modify the frontend), each release ships a prebuilt
dune-admin-web-dist.tar.gz asset. A single make target downloads it and produces a fully
embedded binary:
make build-prebuilt
This fetches the dist that matches the local VERSION file (falling back to the latest
release), extracts it, and compiles the Go binary with the frontend embedded. No Node, pnpm,
or Pro license required — only Go and make.
Backend-only development is also possible without a license: run make dev-backend for
hot-reload and point a browser at the prebuilt dist from make build-prebuilt (served by
the binary at http://localhost:8080). Frontend hot-reload (make dev) requires the Pro
license.
Development
dune-admin ships pre-commit and pre-push hooks that mirror the CI quality gate. Set them up once:
make hooks # wires git's core.hooksPath to .githooks/
make tools # caches golangci-lint, govulncheck, gocognit, gosec, air (via Go's go tool mechanism)
After that, every git commit runs gofmt -w + go vet + golangci-lint on staged Go files (and markdownlint-cli2 on .md files), and every git push adds gosec, govulncheck, and go test -race. Bypass with --no-verify if you really need to.
You can run the full suite by hand at any time:
make verify # fmt-check + vet + test-race + vulncheck + lint + gocognit
> Windows note: the race detector needs cgo. Either install MinGW (e.g. winget install BrechtSanders.WinLibs.POSIX.UCRT) or skip race testing locally and rely on the pre-push hook on a Linux dev box or CI.
Makefile targets
Target
Description
make setup
Run the interactive setup wizard
make build
Build frontend + Go binary for the host OS
make build-prebuilt
Download prebuilt dist from a release + embed-build (no Pro license needed)
make linux
Cross-compile a Linux amd64 binary (dune-admin-linux)
make dev
Run backend + frontend in live mode (Air + Vite HMR)
make dev-server
Run backend only (go run ./cmd/dune-admin)
make web
Build the frontend only
make dist-archive
Package cmd/dune-admin/dist as a release asset tarball (CI use)
make deploy-web
Build and deploy the SPA to Cloudflare Pages
make render-k8s
Render deploy/k8s/dune-admin.rendered.yaml from ~/.dune-admin/config.yaml
make k8s-dry-run
Render and run kubectl apply --dry-run=client
make test
Run tests
make verify
Run all quality checks (fmt-check, vet, test-race, vulncheck, lint, gocognit)
make hooks
Install the pre-commit + pre-push hooks
make tools
Cache the dev toolchain (golangci-lint, gosec, etc.)
Item data (optional)
item-data.json provides friendly item names, stack limits, volume, tier, and rarity. It ships with the repo.
Without it the panel still works; inventory items show raw template IDs.
Architecture
dune-admin is a single Go binary that exposes a REST API over the game's PostgreSQL + RabbitMQ stack. The React SPA can be served by the same binary (from ./dist) or hosted independently (Cloudflare Pages). A control-plane abstraction (amp / kubectl / docker / local) drives lifecycle operations, log streaming, INI editing, and broker access against whatever topology you're running.
For design rationale and trade-offs, see the architecture decision records in docs/adr/:
Discord topologies — how game servers, Discord guilds, channels, and per-server characters map together (single guild, shared guild, guild-per-server, and mixed).