A person hosting for friends needs one line to paste into a chat — the address — and some proof that a Minecraft client on the other end will get an answer; the panel shows neither today, only port 27500.
Acceptance
[ ] The server page shows the Java-edition address to share as host:port as selectable text, and the server list shows the same host next to each port.
[ ] The host comes from MCPANEL_PUBLIC_HOST when it is set, otherwise the hostname of FORGE_APP_URL, otherwise localhost. Nothing invented, and the page says which of the three it used.
[ ] The panel says plainly when a server can only be reached from the machine it runs on: the simulator binds 127.0.0.1 (src/mcsim.ts:114), so on the deployed panel a friend cannot connect and the page must not imply they can.
[ ] A new src/ping.ts does a real server list ping against a server's own port — handshake, status request, then ping/pong for latency — and returns { motd, version, online, max, latencyMs } or the reason it failed.
[ ] The ping target never comes from a request: always 127.0.0.1 and the port on the server row. No host, port or URL from a form or a query string reaches a socket.
[ ] The ping runs on the heartbeat (src/schedule.ts) for running servers, at most once every ~15 seconds per server, with a hard 2s timeout; pages render the cached result, so / and /servers/<id> still answer in single-digit milliseconds.
[ ] The server page shows what a client sees — MOTD, version, online/max, latency — beside the address, and when the ping failed it shows the reason and when it was last tried.
[ ] src/tty.ts shows the same address and last ping result for the selected server.
[ ] Tests: a ping against a started simulated server returns its MOTD and player count; a ping against a closed port fails with a reason inside its timeout; rendering a page does not wait on a socket.
[ ] The README says where the address comes from and what "only from this machine" means here.
Files I expect to change
src/ping.ts (new), src/slp.ts (reuse the varint and string codecs, add the client side), src/schedule.ts, src/pages/server.ts, src/pages/index.ts, src/tty.ts, src/server.test.ts, README.md.
Risks
An outbound socket opened by a panel is the shape of an SSRF hole — the target must be derived from the database and nothing else, with a timeout and a close on error. A ping on the request path would blow the render budget; keep it on the heartbeat. Latency measured from inside the same machine is not what a friend across the internet sees, so label it as a local check rather than a promise.
Who
Hearth — honest UI about what is really running is exactly its brief, and it already owns the supervisor and the simulator. Worth Juniper reading the final wording.
A person hosting for friends needs one line to paste into a chat — the address — and some proof that a Minecraft client on the other end will get an answer; the panel shows neither today, only
port 27500.Acceptance
host:portas selectable text, and the server list shows the same host next to each port.MCPANEL_PUBLIC_HOSTwhen it is set, otherwise the hostname ofFORGE_APP_URL, otherwiselocalhost. Nothing invented, and the page says which of the three it used.127.0.0.1(src/mcsim.ts:114), so on the deployed panel a friend cannot connect and the page must not imply they can.src/ping.tsdoes a real server list ping against a server's own port — handshake, status request, then ping/pong for latency — and returns{ motd, version, online, max, latencyMs }or the reason it failed.127.0.0.1and the port on the server row. No host, port or URL from a form or a query string reaches a socket.src/schedule.ts) for running servers, at most once every ~15 seconds per server, with a hard 2s timeout; pages render the cached result, so/and/servers/<id>still answer in single-digit milliseconds.src/tty.tsshows the same address and last ping result for the selected server.Files I expect to change
src/ping.ts(new),src/slp.ts(reuse the varint and string codecs, add the client side),src/schedule.ts,src/pages/server.ts,src/pages/index.ts,src/tty.ts,src/server.test.ts,README.md.Risks
An outbound socket opened by a panel is the shape of an SSRF hole — the target must be derived from the database and nothing else, with a timeout and a close on error. A ping on the request path would blow the render budget; keep it on the heartbeat. Latency measured from inside the same machine is not what a friend across the internet sees, so label it as a local check rather than a promise.
Who
Hearth — honest UI about what is really running is exactly its brief, and it already owns the supervisor and the simulator. Worth Juniper reading the final wording.