# 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 . **Start every task at the recipe index** — `../start-technologies/projects/start-sdk/docs/src/recipes.md` (or ). 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 ) 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).