Files
station-master-startos/startos/init/generateJoinSecret.ts
T
Jesse de28b30eea Initial StartOS package for Station Master (v0.5.1)
Built from source via a git submodule pinned to a tag, not a published image
— the Dockerfile, main.ts, and manifest should never need to change for an
ordinary version bump, only the submodule pin (see UPDATING.md). One volume,
one interface serving the browser client + lobby/intent API + SSE stream
from a single origin, no dependencies. The server-wide join secret (D14) is
seeded on install, exposed via the Get Join Secret action, and blocks start
behind a critical task until retrieved — the same first-set/rotation pattern
as actual-budget-startos's admin password.

Verified on phoenix.local: installs, the critical task correctly blocks an
ordinary start, force-starting confirms the daemon binds its port and serves
the client, and store.json correctly holds the install-seeded join secret.
Not verified: the Get Join Secret action's execution end-to-end — start-cli's
`package action run` fails with a client-side deserialization error that
reproduces identically against actual-budget's already-shipped equivalent
action, so this looks like a start-cli issue rather than a defect here.

icon.svg is still the scaffold's hello-world placeholder — no real Station
Master icon exists yet to ship in its place.
2026-08-21 08:44:07 -04:00

27 lines
1.1 KiB
TypeScript

import { utils } from '@start9labs/start-sdk'
import { getJoinSecret } from '../actions/getJoinSecret'
import { i18n } from '../i18n'
import { sdk } from '../sdk'
import { storeJson } from '../fileModels/store.json'
/**
* The server exits immediately if `JOIN_SECRET` is unset (`src/server/index.ts` in the game
* repo), so a value has to exist before main.ts's daemon ever starts — seeded here rather than
* left for the user to generate via the action first.
*/
export const seedJoinSecret = sdk.setupOnInit(async (effects, kind) => {
if (kind === 'install') {
await storeJson.merge(effects, {
joinSecret: utils.getDefaultString({ charset: 'a-z,A-Z,0-9', len: 24 }),
})
await sdk.action.createOwnTask(effects, getJoinSecret, 'critical', {
reason: i18n('Get the join secret to share with players'),
})
} else {
// 'update' and 'restore' — repairs a corrupted store.json without touching an existing
// joinSecret. A restored volume already carries one (it lives inside the backed-up volume,
// see backups.ts), so there is nothing to seed.
await storeJson.merge(effects, {})
}
})