Files
Jesse c95620d566 Bundle Station Master v0.5.3: games in progress, and a way to end one
Pin moves to v0.5.3, package version to 0.5.3:0.

The health check no longer just probes a port. It fetches the server's own
/api/health and reports what it says — "Multiplayer server is ready — 3
games in progress", with ", 1 waiting to start" appended only when a lobby
exists and "no games in progress" on an idle server. A server that doesn't
answer is reported as STARTING, never failed: the server replays its saved
games before binding its port, so a boot legitimately looks like nothing is
listening, and calling that a failure makes an ordinary restart look like a
crash.

Two new actions, both only-running. Games in Progress is read-only and
lists every game and lobby with players, Day/Stage/phase, who it waits on,
when it started and when it last moved. Manage Game picks one from a
dropdown built live from the server and either exports it or ends it —
ending being the only way a game finishes other than being played out,
since an abandoned game otherwise stays active and is resumed on every
restart forever. Ending always returns the deleted game's save, so nothing
is destroyed without being handed back first.

They are only-running because none of it is readable from disk: a save is a
seed plus a list of moves, so whose-turn-it-is exists only after a replay
through the engine, which lives in the game repo rather than here. The
running server has already done that work and is asked for the answer.
startos/serverApi.ts is the single place that asks.

store.json gained adminSecret (32 chars) beside joinSecret, seeded on
install and backfilled on update for a volume written before the field
existed. It is deliberately NOT the join secret: every player holds that
one, so gating a delete with it would let anyone at the table destroy
anyone else's game. init/generateJoinSecret.ts is renamed generateSecrets.ts
now that it mints both.

Also brought getJoinSecret's result strings under i18n(). They shipped as
plain strings two commits ago, which actions.md is explicit about — every
user-facing string including result titles, messages and thrown errors.
The new actions follow it, so the old one shouldn't be the odd one out.

Verified on phoenix.local, installed as an UPDATE from 0.5.2:0 rather than
a fresh install, which exercised three things at once: the boot log line
("Resuming 1 saved game(s)…"), the engine-version refusal firing for real
on a game recorded under 0.5.2, and the adminSecret backfill (store.json
came out with both secrets, 24 and 32 chars). Then, from inside the
container: the package-generated admin secret authenticating against
/api/games (200) while a wrong one is refused (403), health counts tracking
0 -> lobby 1 -> active 1 through a create/bot/start, and every field the
actions render present and correct on the listing.

NOT verified: the actions' own forms and result rendering. `start-cli
package action run` fails with a client-side deserialization error on every
action on this box — including the already-shipped get-join-secret and
actual-budget's equivalent — so it is a start-cli problem, not this
package's. The data path underneath them is verified above; the SDK
form/result rendering needs the web UI.

A test game (TRESTLE-3221, Alice + a bot) is left running on the box so
there is something for Games in Progress to show.
2026-08-21 16:00:05 -04:00

56 lines
3.9 KiB
Markdown

# AGENTS.md
This is a StartOS service-package repository — it builds a `.s9pk` for StartOS.
Develop it inside a StartOS packaging workspace created by `start-cli s9pk init-workspace`,
which provides the packaging guide and agent context one level up. If you're reading this in a
bare clone with no workspace, the full guide is at <https://docs.start9.com/packaging>.
**Start every task at the recipe index** — `../start-technologies/projects/start-sdk/docs/src/recipes.md`
(or <https://docs.start9.com/packaging/recipes.html>). It maps an intent ("prompt the user to create
admin credentials", "expose a web UI") to the constructs, the reference pages, and a named production
package to copy. Find the recipe before you read this package's neighbours: a package you reach by
grepping may be non-conformant, and the recipe outranks it.
Freshly scaffolded? Work the
[New Package Checklist](../start-technologies/projects/start-sdk/docs/src/new-package-checklist.md)
(or <https://docs.start9.com/packaging/new-package-checklist.html>) from top to bottom. It is a
guide page, not a file in this repo — read it, don't copy it in.
Keep `README.md` (technical reference for an AI support or administering agent) and
`instructions.md` (end-user docs) in sync with your changes.
**Bugs and feature requests are issues on this repo** (self-hosted Gitea, not GitHub) — file
them as you find them. Don't record work in the repo instead: no `TODO.md`, no `NOTES.md`, no
`PLAN.md`. What you verified, tried, and decided belongs in the commit message and the PR body.
## This repo
- **Station Master is a git submodule (`station-master/`), not vendored source.** It tracks
[Jesse's own game repo](https://draco.local:53871/Jesse.Markowitz/station-master), self-hosted,
pinned to a tag. See `UPDATING.md` for how the pin is bumped — the `Dockerfile`, `main.ts`, and
the manifest should never need to change for an ordinary version bump.
- **Built from source, not a published image.** The `Dockerfile` is entirely this repo's own:
a `node:*-slim` builder stage runs the submodule's `npm run build:web` to produce the static
client, then a runtime stage copies only `package.json`, `src/`, and the built `dist/` — no
`node_modules`, since `src/server/index.ts` has zero runtime dependencies (see the submodule's
own `TODO.md` for why) and Node's native TypeScript type-stripping means it needs no compile
step either.
- **Two secrets in `store.json` are this package's only state beyond the game data itself.**
`startos/init/generateSecrets.ts` seeds both on install. `joinSecret` (D14 in the game's
`docs/architecture/multiplayer.md`) is what players need, and the server refuses to start without
one; it is rotated by the **Get Join Secret** action, which handles first retrieval and rotation
in one, per `recipe-admin-credentials.md`.
- **`startos/serverApi.ts` is the only place this package talks to the game server, and the game
actions cannot work without it running.** Nothing about a live game is readable from disk: a save
is a seed plus a list of moves, so day/stage/phase and whose-turn-it-is only exist after a replay
through the engine — which is in the game repo, not here. Hence `allowedStatuses: 'only-running'`
on both game actions, and hence the health check fetching `/api/health` rather than probing a
port. There are two secrets in `store.json` for the same reason there are two on the server:
`adminSecret` gates `/api/games` and must never be the `joinSecret` every player holds.
- **One volume, one interface, no dependencies.** Everything the server persists —
`store.json` and every game's own save data — lives on the `data` volume; the single `ui`
interface carries the browser client, the lobby/intent HTTP API, and the SSE game stream from
one port, which is what keeps players reachable from different addresses in the same game
(`docs/architecture/deployment.md` §3 in the game repo).