trevato/mcpanel

Public: anyone can read this; the crew can change it.

A Minecraft server hosting panel: create servers (name, version, memory, port), start/stop/restart with a supervised child process, a live console with command input, scheduled backups with restore, a players list from the server log, and per-server settings. Where a real Java server cannot run in this environment, run a faithful simulator with the same interface and say so in the UI.

Running 75e13ee https://mcpanel--forge-trevato-apps.open-platform.sh in production 21d 16h

README.md

mcpanel

A hosting panel for Minecraft servers: create a server (name, version, memory, port), start, stop and restart it under a supervisor, watch its console live and type commands into it, see who is online, take and restore backups, and change its settings.

Real or simulated

A server here is a real, supervised child process. What that process is depends on the machine:

  • If java is on the PATH and a jar sits at

$FORGE_APP_DATA/jars/minecraft_server.<version>.jar, the panel runs java -Xmx<memory>M -jar <jar> nogui — the real thing.

  • Otherwise it runs src/mcsim.ts, a simulator with the same interface: it

binds the server's TCP port and answers a Minecraft client's server list ping with the MOTD, prints vanilla-shaped log lines to stdout, takes commands on stdin (list, say, save-all, op, kick, whitelist, time set, weather, difficulty, gamemode, seed, stop), keeps a world on disk, and shuts down on stop or SIGTERM.

There is no Java runtime on the forge, so every server on the deployed panel is simulated, and the page says so at the top of the server list and next to each server's status. Everything around the process — supervision, crash restart with backoff, the console, the players list read from the log, the backups, the port — behaves the same either way.

Using it

  • Servers (/) lists what exists, with status, players and Start/Stop.
  • A server (/servers/<id>) is the console (server-rendered history, then

live over server-sent events), a command box, the players list, backups and settings. Commands are written to the process's standard input one line at a time; nothing is ever handed to a shell.

  • Backups are single tar files under $FORGE_APP_DATA/backups/<id>/. You

can download one and open it with tar xf, and restore it onto a stopped server. Set "Backup every N minutes" in Settings and the panel takes them on a schedule, keeping the newest few. Every backup — by hand, on the schedule or from the terminal — prunes the server down to its "Backups kept", so what is on disk is what the settings say.

  • Crashes: if a server exits on its own and "Restart if it crashes" is on,

the supervisor brings it back with a growing delay, up to five times, and says so in the console. A server that was running is started again when the panel itself restarts.

Two buttons cost real work, so both are rate limited per server, whether they are pressed on the page or written by the terminal: a backup by hand at most once every 30 seconds (and never two at once for one server), and console commands at 20 in any 10 seconds. Over the limit you get a sentence saying how long to wait — 429 for the page's own JSON send — and nothing happens.

Set MCPANEL_PASSWORD in the environment to put the panel behind a password (session in an HttpOnly cookie). With it unset the panel is open and the header says "open panel". No secrets live in this repository.

PORT=8080 FORGE_APP_DATA=./data bun run src/server.ts   # run it here
bun test                                                # the check every pull runs
FORGE_APP_DATA=./data bun run src/tty.ts                # the terminal interface

The terminal is the same panel: j/k move, Enter opens a server's console, s/x/r start, stop and restart, c types a command, b backs up, R restores the newest backup, q quits. It does not own the child processes, so each key writes an intent that the panel process picks up within a second. The exact command that reaches it is in FORGE_APP_SSH when the app runs; the app is the login name and the forge is the host. When FORGE_APP_SSH is unset the terminal is off.

Shape

Zero dependencies — Bun, bun:sqlite and the standard library only.

fileone concept
src/server.tsthe floor: security headers, same-origin POSTs, static files
src/routes.tsthe table of paths, and the console's event stream
src/db.tsschema and queries
src/runtime.tsthe supervisor: spawn, read, write, stop, restart
src/engine.tsreal Java or the simulator, and the argv for each
src/mcsim.tsthe simulated server, run as its own process
src/backups.ts, src/tar.tsbackups, and the tar they are made of
src/schedule.tsthe heartbeat: intents, scheduled backups
src/players.ts, src/slp.ts, src/fields.ts, src/session.ts, src/limit.tslog parsing, server list ping, validation, sessions, rate limits
src/pages/*.tsrendering
static/panel.css, static/panel.jsone stylesheet, one small script

Everything the panel keeps — the database, each server's directory, the backups — lives under FORGE_APP_DATA. Production and each preview get their own, so a preview never touches production's worlds. forge.json says how the app runs.

↑↓ navigate ↵ open esc close

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs