import { utils } from '@start9labs/start-sdk' import { i18n } from '../i18n' import { sdk } from '../sdk' import { storeJson } from '../fileModels/store.json' /** * Mints a fresh join secret on every run and returns it — first-set and rotation are the same * action (recipe-admin-credentials.md). Writing a new value restarts the daemon with it: main.ts * reads `joinSecret` reactively via `.const(effects)`, so a rotation here takes effect * immediately rather than needing a manual restart. * * Rotating invalidates the old secret for anyone who hasn't joined yet, but never removes a * player already seated in a game — D14's secret gates the lobby door only (join-secret ≠ * session token, per `lobby-and-sessions.md` §1). */ export const getJoinSecret = sdk.Action.withoutInput( 'get-join-secret', async () => ({ name: i18n('Get Join Secret'), description: i18n( 'Retrieve or rotate the secret players need to create or join a game', ), warning: null, allowedStatuses: 'any', group: null, visibility: 'enabled', }), async ({ effects }) => { const joinSecret = utils.getDefaultString({ charset: 'a-z,A-Z,0-9', len: 24, }) await storeJson.merge(effects, { joinSecret }) return { version: '1', title: i18n('Join Secret'), message: i18n( "Share this with anyone you want to be able to create or join a game on this server. It doesn't identify a person or a seat, just who's allowed at the lobby door. Running this action again generates a new one and restarts the server with it; anyone already seated in a game keeps playing, but the old secret stops working for new games and joins.", ), result: { type: 'single', name: i18n('Join Secret'), description: null, value: joinSecret, masked: true, copyable: true, qr: false, }, } }, )