Hosting for friends is mostly deciding who may join, and today the panel can only kick: everything else is a console command typed by hand and forgotten at the next restart.
Acceptance
[ ] A "Who may join" section on the server page: whitelist on/off, add and remove a name, op and deop, ban and unban with an optional reason — each a same-origin POST that redirects with a sentence.
[ ] Names are validated at the boundary against ^[A-Za-z0-9_]{3,16}$, alongside command in src/fields.ts:69; anything else is a sentence a person reads, and every name is escaped on output.
[ ] The lists are rows in SQLite (one access table keyed by server, name and kind, with the reason and when) — the panel's truth, readable whether or not the server is running.
[ ] Writing a list also writes the server's own files — whitelist.json, ops.json, banned-players.json under serverDir(id), in vanilla's shape — so a real jar honours them at the next start.
[ ] While the server is running the change is additionally sent as a console command (whitelist add|remove|on|off, op, deop, ban, pardon) so it takes effect now, and the console shows it happening.
[ ] The players table marks who is op, whitelisted or banned and offers those actions inline beside kick.
[ ] src/mcsim.ts learns ban, pardon and whitelist add|remove|list, reads the three files at boot, and keeps a banned player — or a stranger while the whitelist is on — from joining, so the simulated path stays a faithful stand-in.
[ ] src/tty.ts shows who is op, whitelisted and banned, and can add and remove them, as intents like every other terminal action.
[ ] After a restore, the rows and the files agree: the restore path reconciles them and says which won.
[ ] Tests: adding a whitelist entry writes the row, writes the file and sends the command; an invalid name is refused with a sentence and leaves no row; a banned player does not join the simulator; rows and files agree after a restore.
[ ] The README documents the section and names the files the panel writes.
Files I expect to change
src/access.ts (new: the lists and the files they become), src/db.ts, src/fields.ts, src/routes.ts, src/pages/server.ts, src/mcsim.ts, src/tty.ts, src/server.test.ts, README.md.
Risks
Two writers can disagree — the panel writing files and the running server writing its own. Decide it once and write it in the module header: the panel owns the files while the process is stopped, and commands while it runs. A name must never reach a path or a command without passing the boundary check first. The schema change has to be an additive CREATE TABLE IF NOT EXISTS against a database that already has servers in it. Bans and ops are precisely the state a person cannot afford to lose in a restore.
Who
Hearth to build it, Sentinel to review the pull — user input becomes both file contents and console commands here.
Hosting for friends is mostly deciding who may join, and today the panel can only kick: everything else is a console command typed by hand and forgotten at the next restart.
Acceptance
^[A-Za-z0-9_]{3,16}$, alongsidecommandinsrc/fields.ts:69; anything else is a sentence a person reads, and every name is escaped on output.accesstable keyed by server, name and kind, with the reason and when) — the panel's truth, readable whether or not the server is running.whitelist.json,ops.json,banned-players.jsonunderserverDir(id), in vanilla's shape — so a real jar honours them at the next start.whitelist add|remove|on|off,op,deop,ban,pardon) so it takes effect now, and the console shows it happening.src/mcsim.tslearnsban,pardonandwhitelist add|remove|list, reads the three files at boot, and keeps a banned player — or a stranger while the whitelist is on — from joining, so the simulated path stays a faithful stand-in.src/tty.tsshows who is op, whitelisted and banned, and can add and remove them, as intents like every other terminal action.Files I expect to change
src/access.ts(new: the lists and the files they become),src/db.ts,src/fields.ts,src/routes.ts,src/pages/server.ts,src/mcsim.ts,src/tty.ts,src/server.test.ts,README.md.Risks
Two writers can disagree — the panel writing files and the running server writing its own. Decide it once and write it in the module header: the panel owns the files while the process is stopped, and commands while it runs. A name must never reach a path or a command without passing the boundary check first. The schema change has to be an additive
CREATE TABLE IF NOT EXISTSagainst a database that already has servers in it. Bans and ops are precisely the state a person cannot afford to lose in a restore.Who
Hearth to build it, Sentinel to review the pull — user input becomes both file contents and console commands here.