From 3751adcb649adcd68231c129c1c336ff6a5cab58 Mon Sep 17 00:00:00 2001 From: Void Date: Thu, 12 Nov 2026 16:50:16 +0000 Subject: [PATCH 1/3] docs: Ratatoskr client build, install, verify and rollback runbook Co-Authored-By: Claude Sonnet 5.5 --- docs/gamecult/client-build-install.md | 100 ++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 docs/gamecult/client-build-install.md diff --git a/docs/gamecult/client-build-install.md b/docs/gamecult/client-build-install.md new file mode 100644 index 00000000..88a7ee2c --- /dev/null +++ b/docs/gamecult/client-build-install.md @@ -0,0 +1,100 @@ +# 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) + +The repo's `config.json` is the served config. It must equal what `https://chat.gamecult.org/config.json` +serves (Ratatoskr #103); `vite build` copies it into `dist/` (`vite.config.js`, static copy of +`config.json`). 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 # .npmrc sets legacy-peer-deps and save-exact +node scripts/sync-poses.mjs # pose catalogue and art from /opt/commons/cast-art/poses +NODE_OPTIONS=--max_old_space_size=6144 npm run build # writes dist/ +``` + +`sync-poses` is needed because the pose art is not committed; skip it 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 for the host is scripted outside the repo: +`/opt/commons/handoff/ratatoskr-main-build/build-prod-dist.sh ` (see its `README.md`). +It writes `/opt/commons/handoff/ratatoskr-main-/{dist,SHA256SUMS,BUILD-INFO,build.log}`, +locks the files read-only, and refuses to overwrite. 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 +diff <(curl -s https://chat.gamecult.org/config.json) /dist/config.json # report any difference +``` + +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) + +Unpack beside the live release; do not touch the live directory. + +```sh +R=ratatoskr-<8hex> +H=/opt/commons/handoff/ratatoskr-main- +cp -r $H/dist /srv/cinny/releases/$R +cmp /srv/cinny/current/config.json /srv/cinny/releases/$R/config.json # expect no output +(cd /srv/cinny/releases/$R && sha256sum -c $H/SHA256SUMS) +``` + +If `cmp` reports a difference, stop and settle which config ships. If the decision is to keep +the host's config byte-for-byte, `cp /srv/cinny/current/config.json /srv/cinny/releases/$R/config.json` +and expect `sha256sum -c` to flag `config.json` only (use `--ignore-missing` and read the one line). + +Note the current target first, for the rollback: `readlink /srv/cinny/current`. + +Switch (atomic): + +```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 + +- `backgroundScene.style`: the repo and the served config both say `ridge` since #106 (Meta, + 2026-11-11). Confirm against the live file at install time with the `cmp` above; the earlier + `terrain` mismatch is closed only when live is switched. -- 2.47.3 From fef7d34fdb61498b698ef305d672a4a062527f48 Mon Sep 17 00:00:00 2001 From: Void Date: Thu, 12 Nov 2026 16:50:28 +0000 Subject: [PATCH 2/3] runbook: record the served-config drift as observed Co-Authored-By: Claude Sonnet 5.5 --- docs/gamecult/client-build-install.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/gamecult/client-build-install.md b/docs/gamecult/client-build-install.md index 88a7ee2c..9f133cb8 100644 --- a/docs/gamecult/client-build-install.md +++ b/docs/gamecult/client-build-install.md @@ -95,6 +95,8 @@ Hard-refresh to clear `sw.js`. Do not delete the failed release; leave it for di ## Open -- `backgroundScene.style`: the repo and the served config both say `ridge` since #106 (Meta, - 2026-11-11). Confirm against the live file at install time with the `cmp` above; the earlier - `terrain` mismatch is closed only when live is switched. +- 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), and lacks the repo's `dings`, + `fileZoneShare` and `registration` blocks (all off in the repo). The new keys default off or + inert when absent, so only the style is a decision. The `cmp` in step 2 shows the drift at + install time; Meta says which style ships. -- 2.47.3 From 45facff436894d3b1f02bed16fb3ffed9b7367ed Mon Sep 17 00:00:00 2001 From: Void Date: Thu, 12 Nov 2026 16:51:41 +0000 Subject: [PATCH 3/3] runbook: handoff build ships its own pinned config; drift check is not a pass check Co-Authored-By: Claude Sonnet 5.5 --- docs/gamecult/client-build-install.md | 69 ++++++++++++++++----------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/docs/gamecult/client-build-install.md b/docs/gamecult/client-build-install.md index 9f133cb8..cb060d2a 100644 --- a/docs/gamecult/client-build-install.md +++ b/docs/gamecult/client-build-install.md @@ -16,35 +16,46 @@ procedure. Nothing here is run by the build step against the live host. ## 1. Build (steward) -The repo's `config.json` is the served config. It must equal what `https://chat.gamecult.org/config.json` -serves (Ratatoskr #103); `vite build` copies it into `dist/` (`vite.config.js`, static copy of -`config.json`). Do not hand-edit `dist/config.json`. +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 # .npmrc sets legacy-peer-deps and save-exact -node scripts/sync-poses.mjs # pose catalogue and art from /opt/commons/cast-art/poses +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/ ``` -`sync-poses` is needed because the pose art is not committed; skip it 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). +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 for the host is scripted outside the repo: -`/opt/commons/handoff/ratatoskr-main-build/build-prod-dist.sh ` (see its `README.md`). -It writes `/opt/commons/handoff/ratatoskr-main-/{dist,SHA256SUMS,BUILD-INFO,build.log}`, -locks the files read-only, and refuses to overwrite. 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. +**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 -diff <(curl -s https://chat.gamecult.org/config.json) /dist/config.json # report any difference +# 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 @@ -52,23 +63,27 @@ the repo is wrong, and a ruling (Meta, for `backgroundScene.style`) says which s ## 2. Install (host owner, root, by hand) -Unpack beside the live release; do not touch the live directory. +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 # expect no output +cmp /srv/cinny/current/config.json /srv/cinny/releases/$R/config.json (cd /srv/cinny/releases/$R && sha256sum -c $H/SHA256SUMS) ``` -If `cmp` reports a difference, stop and settle which config ships. If the decision is to keep -the host's config byte-for-byte, `cp /srv/cinny/current/config.json /srv/cinny/releases/$R/config.json` -and expect `sha256sum -c` to flag `config.json` only (use `--ignore-missing` and read the one line). +`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`). -Note the current target first, for the rollback: `readlink /srv/cinny/current`. - -Switch (atomic): +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 @@ -96,7 +111,7 @@ Hard-refresh to clear `sw.js`. Do not delete the failed release; leave it for di ## 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), and lacks the repo's `dings`, - `fileZoneShare` and `registration` blocks (all off in the repo). The new keys default off or - inert when absent, so only the style is a decision. The `cmp` in step 2 shows the drift at - install time; Meta says which style ships. + `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). -- 2.47.3