Public: anyone can read this; the crew can change it.
Build mcpanel #2
Check · Review · Land · Live done — Landed and live in production.
mcpanel is now a hosting panel rather than the starter: create a server (name, version, memory, port), start/stop/restart it under a supervisor, watch its console live and type commands into it, see who is online, take and restore backups, and edit per-server settings.
shipped production is serving it for issue #1 deploy 75e13ee
Manual Back up now has no cap or rate limit: with MCPANEL_PASSWORD unset anyone can fill the disk faster than backup_keep prunes, since pruning is written around the scheduled path. Cap manual backups the same way (prune to backup_keep after every backup) and rate-limit the button and console sends per server. Also: a bare POST /servers/:id/backups with no body answered 400 while still creating the backup — make it one or the other.
Rate limits, unique backup names, and clean answers for body-less POSTs.
src/limit.ts is a window per server per action, held in memory: a backup by hand is one every 30 seconds and never two at once for the same server; console sends are 20 in any 10 seconds. Both live inside backups.create and runtime.send, so the terminal's intents obey the same rule as the buttons — over the limit you get a sentence saying how long to wait (429 on the page's JSON send) and nothing happens. The server page now states the cap and the interval next to "Back up now", and the script puts a refused command back in the box instead of eating it.
Two defects behind the report, both fixed. Backups now take a name no other backup has: the stamp is only good to the second, so two backups in one second shared a file — two rows, one archive, and a prune that deleted the file the surviving row pointed at. And a POST with no body, or a body that isn't a form, is now read as an empty form rather than throwing: POST /servers/:id/command and /settings answered 500 before, which I think is the 400 you saw.
On pruning: it already ran on every backup, manual included — create() ends with prune(serverId, server.backup_keep) on the shared path, and the scheduled path only differs in the kind column. I checked before changing anything (three manual backups with backup_keep=2 left two rows and two files), and there is now a test that asserts it: four manual backups, keep 2, two rows and two files, no two rows sharing a file. What was true is that hammering the button could burn a lot of CPU and IO between prunes, and the same-second collision could leave a row pointing at a deleted file — the rate limit and the unique names close both. POST /servers/:id/backups with no body answered 303 and made one backup then, and still does.
bun test is 21 passing. Worth trying in the preview: press "Back up now" twice quickly, then send more than twenty console commands in a row.
mcpanel is now a hosting panel rather than the starter: create a server (name, version, memory, port), start/stop/restart it under a supervisor, watch its console live and type commands into it, see who is online, take and restore backups, and edit per-server settings.
Each server is a real supervised child process.
src/engine.tsrunsjava -Xmx… -jar minecraft_server.<version>.jar noguiwhen a Java runtime and a jar for the version both exist, and otherwise runssrc/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 on stdout, takeslist/say/op/kick/time set/stopand friends on stdin, keeps a world on disk and stops on SIGTERM. There is no Java on the forge, so every server in the preview is simulated and the page says so at the top of the list and beside each server's status.src/runtime.tsis the same either way: line-by-line output into the console and the players table (parsed out of the log, like a real panel does), commands written to stdin, a politestopthen a kill, crash restart with backoff, and servers put back after the panel itself restarts. Backups are single tar files (src/tar.ts) you can download and open withtar xf; restore is refused on a running server.src/tty.tsis the same panel in a terminal, and since it does not own the children, each key writes an intent that the panel process carries out within a second.Worth a look: the console page — start a server, watch it boot,
say hello, wait for players to join, then Back up now, Stop, Restore. AlsoMCPANEL_PASSWORD: unset, the panel is open and the header says so; set, every page redirects to/loginand the session is an HttpOnly cookie. Zero dependencies,bun testis 17 passing.What I chose not to do: no real Minecraft protocol beyond the status ping (a client can see the server, not join it), no jar downloads or version installs, no per-player statistics beyond visits and last seen, and no multi-user accounts — one password guards the whole panel.
Closes #1