diff --git a/docs/gamecult/client-build-install.md b/docs/gamecult/client-build-install.md new file mode 100644 index 00000000..cb060d2a --- /dev/null +++ b/docs/gamecult/client-build-install.md @@ -0,0 +1,117 @@ +# Ratatoskr client: build, install, verify, roll back + +Who does what. The steward (Void) builds and hands off; Void holds no root and does not deploy. +Eureka installs on the chat.gamecult.org host, which is Eureka's. This page is the shared +procedure. Nothing here is run by the build step against the live host. + +## Facts about the host + +- Web root: `/srv/cinny/`. `current` is a symlink to one directory under `releases/`; the web + server serves `current`. Releases are `releases/ratatoskr-<8hex>` (older ones are + `ratatoskr-nemesis-<8hex>`). Previous releases stay on disk; that is the rollback. +- The client is static. `config.json` sits in the release root beside `index.html` and is read + at load. Cinny's `Dockerfile` and `docker-nginx.conf` are upstream's container path, not the + chat.gamecult.org path. +- Check the pointer before anything else: `readlink /srv/cinny/current`. + +## 1. Build (steward) + +Two builds exist, and they treat `config.json` differently. + +**By hand (the repo's config).** `vite build` copies the repo's `config.json` into `dist/` +(`vite.config.js`, static copy of `config.json`). Ratatoskr #103 made the repo file the intended +served config, and #106 set `backgroundScene.style` to `ridge`; the served file has not caught up +(see Open). Do not hand-edit `dist/config.json`. + +```sh +git clone https://eureka.gamecult.org/forge/GameCult/Ratatoskr.git && cd Ratatoskr +git checkout # build a named commit, not a branch tip +npm ci --ignore-scripts # .npmrc sets legacy-peer-deps; skips husky's prepare hook +node scripts/sync-poses.mjs # pose art is not committed; this also rewrites the tracked + # src/app/components/message/pose/catalogue.json +NODE_OPTIONS=--max_old_space_size=6144 npm run build # writes dist/ +``` + +Skip `sync-poses` and the build ships with no poses. Do not use `npm run build:preview` for +production: it rewrites `dist/config.json` for preview (hash router, chosen tier and style). + +**The handoff build (what the host installs).** +`/opt/commons/handoff/ratatoskr-main-build/build-prod-dist.sh ` (see its `README.md`) +writes `/opt/commons/handoff/ratatoskr-main-/{dist,SHA256SUMS,BUILD-INFO,build.log}`, +locks the files read-only and refuses to overwrite. It does **not** ship the repo's `config.json`: +it discards it and writes its own pinned `config.production.json` into `dist/config.json`, and +BUILD-INFO says so (`config_json_source`). That pin (sha256 `a26d846b…`, pinned 2026-11-09) has no +`backgroundScene` block, so it no longer equals the served file, and the script's live-config +check stops the build; earlier handoffs ran it with `SKIP_LIVE_CHECK=1` or a modified copy whose +pin was the repo's config (see `config_json_pinned_path` in +`ratatoskr-main-df245e0b76fd83d7ee14fe725b0df9994f36755f/BUILD-INFO`). Repin deliberately +(README, "The pinned production config") or say in the handoff which config the dist carries. +The folder, with the three pins (`SHA256SUMS` sha256, `BUILD-INFO` sha256, lockfile sha256) and +the node and npm versions, is what the install request names. + +Before handing off: + +```sh +cd /dist && sha256sum -c ../SHA256SUMS # 0 failures +grep -rl 'demo.invalid\|demo-preview-token' /dist # must print nothing +# from Yggdrasil the public name may not resolve; --resolve points it at the local server +diff <(curl -fsS --resolve chat.gamecult.org:443:127.0.0.1 https://chat.gamecult.org/config.json) /dist/config.json +``` + +A config difference is a finding to report, not something to paper over: either live drifted or +the repo is wrong, and a ruling (Meta, for `backgroundScene.style`) says which ships. + +## 2. Install (host owner, root, by hand) + +Note the rollback target first: `readlink /srv/cinny/current`. + +Unpack beside the live release; do not touch the live directory. The target must not exist, or +`cp -r` nests `dist` inside it. + +```sh +R=ratatoskr-<8hex> +H=/opt/commons/handoff/ratatoskr-main- +test ! -e /srv/cinny/releases/$R +cp -r $H/dist /srv/cinny/releases/$R +cmp /srv/cinny/current/config.json /srv/cinny/releases/$R/config.json +(cd /srv/cinny/releases/$R && sha256sum -c $H/SHA256SUMS) +``` + +`cmp` is a drift check, not a pass check. Live has no trailing newline; the repo's file has one, +so even equal content differs. Read the difference (`diff` the two) and settle which config +ships. To keep the host's config byte-for-byte, as past installs did, +`cp /srv/cinny/current/config.json /srv/cinny/releases/$R/config.json`; `sha256sum -c` then flags +`config.json` only (read that one line, or use `--ignore-missing`). + +Switch (a single symlink swap; `ln -sfn` unlinks then links, so there is a moment with no link): + +```sh +ln -sfn releases/$R /srv/cinny/current +``` + +## 3. Verify + +- `readlink /srv/cinny/current` names `releases/$R`. +- `curl -s https://chat.gamecult.org/config.json | sha256sum` equals the release's `config.json`. +- Load https://chat.gamecult.org/ and hard-refresh (the service worker, `sw.js`, can hold the + previous assets). Sign-in card shows, the background scene draws, the browser console has no + page errors, a poses/asset request returns 200 and not index.html. + +## 4. Roll back + +Point `current` at the release noted before the switch. Live at the time of writing is +`ratatoskr-nemesis-9b9b495d`: + +```sh +ln -sfn releases/ratatoskr-nemesis-9b9b495d /srv/cinny/current +``` + +Hard-refresh to clear `sw.js`. Do not delete the failed release; leave it for diagnosis. + +## Open + +- The served config (checked 2026-11-12) is not the repo's: it says `backgroundScene.style` + `terrain` where the repo says `ridge` (#106, Meta 2026-11-11), lacks the repo's `dings`, + `fileZoneShare` and `registration` blocks (all off in the repo), and has no trailing newline. + The new keys default off or inert when absent, so only the style is a decision. Meta says which + style ships. The handoff script's pin is stale against live (section 1).