From 540b66c3046bc26622786c7c55cfb6edf2af114e Mon Sep 17 00:00:00 2001 From: wtclaude Date: Mon, 26 Oct 2026 12:24:32 -0500 Subject: [PATCH] =?UTF-8?q?docs(website):=20demo=20step=205=20built=20?= =?UTF-8?q?=E2=80=94=20deploy=20files,=20DM22-DM24?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEMO_MODE.md follows website#227: - §7.2: the baseline is captured with a power stop (quit saves), DM22; a first wipe with no baseline only starts the rig; what the panel calls were checked to do (synchronous decompress, binary-safe write); the server identifier is now 6eb9d621. - §7.3: MODULES comes from state/modules.env; MODULE_SOURCE_HOSTS seeds a fresh database's allowlist; the exact seed:demo command. - §7.5: where the bundle is recorded (state/bundle.json and the rig's own rust-link/bundle.json) and which plugin files the wipe writes. - §8: the deploy directory and the full .env contents. - §9.1: DM22 (power stop), DM23 (/opt/demo.runicgateway.com, host mode), DM24 (Module-Rust cut over now, v0.6.0). - §10: step 5's files built and how each half was tested. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- website/DEMO_MODE.md | 53 ++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/website/DEMO_MODE.md b/website/DEMO_MODE.md index 85785a5..2646ab5 100644 --- a/website/DEMO_MODE.md +++ b/website/DEMO_MODE.md @@ -310,23 +310,36 @@ The workflow uses the Pterodactyl **client** API (files and power): **Capturing the baseline** is a second, hand-run workflow (`workflow_dispatch` only): someone sets up the default set through the demo site (the permission groups, a handful of NPCs, events), then runs -it. It saves the world (`server.save` over the console), stops the server, `POST …/files/compress`es -the three directories into `demo-baseline.tar.gz`, and starts the server again. Re-run it whenever -the default set should change. +it. It stops the server with `POST …/power {"signal":"stop"}`, which runs the egg's stop command, +`quit`, and that writes the save, so the subuser needs no console permission (DM22). It then +`POST …/files/compress`es the three directories, renames the archive to `demo-baseline.tar.gz`, and +starts the server again. Re-run it whenever the default set should change. + +The first wipe on a rig with no baseline yet leaves the rig as it is and only starts it, with a +warning in the run. Set the default set up after that first wipe, then capture. + +**Checked against the panel (2026-10-26)**, on the offline `egg-oxide` test server: `files/compress` +returns the archive's name and size; `files/decompress` is synchronous (every entry is back when it +answers); `files/write` takes a raw binary body byte-exact, so the 8 MB sidecar goes up the same way +as a plugin file; one `files/chmod` per file. **The key.** Pterodactyl client keys belong to a user and reach every server that user can see. The demo gets its own Pterodactyl **subuser**, with access to `rust-oxide` only and the file and power permissions only, and that subuser's client key is the `demo-site` workflow's secret -(`PTERODACTYL_CLIENT_KEY`). The panel URL and server identifier are plain variables. **Done:** the -org lead created the subuser and added the secret on 2026-10-23. +(`PTERODACTYL_CLIENT_KEY`). The panel URL and server identifier are plain variables +(`PTERODACTYL_URL`, `PTERODACTYL_SERVER`), defaulting to `http://192.168.0.12` and `6eb9d621`, the +current `rust-oxide`. **Done:** the org lead created the subuser and added the secret on 2026-10-23. ### 7.3 The site 1. `docker compose down -v` — drops the database **and** the uploads volume. 2. `docker compose pull && docker compose up -d` — the app boots on the latest `main` image, creates - the schema, and installs the Rust module from `MODULES=rust@=`, which the workflow writes (§7.5). -3. Wait for the app's healthcheck. -4. `docker compose exec app npm run seed:demo -- ` (or `DEMO_SEED_FILE`) — one core + the schema, and installs the Rust module from `MODULES=rust@=`, which the + workflow writes to `state/modules.env` (§7.5). It installs under the allowlist a fresh database + seeds from `MODULE_SOURCE_HOSTS`, so the demo's `.env` sets that to `gitea.whitlocktech.com`. +3. Wait for the app's healthcheck (the demo compose file's own: `GET /api/v1/public/status`). +4. `docker compose exec app npm run --prefix server seed:demo -- /app/deploy/demo/seed.json` (the + compose file mounts the fixture there; `DEMO_SEED_FILE` also works) — one core script (`server/db/seedDemo.js`) that, from a fixture file in the deploy directory: - creates the four accounts (§4), or resets them if they exist: password, role, active, 2FA off; - sets the site live with registration disabled, plus any `settings` the fixture adds; @@ -382,10 +395,14 @@ That is why the module is chosen by protocol, not by date. 1. **The bundle changed since the last wipe.** It downloads the bundle's plugin tarball and sidecar binary, checks each `sha256`, and writes them onto the rig through the same Pterodactyl file API while the server is stopped. That means the plugin files into `oxide/plugins/`, and the sidecar - and its `with-sidecar.sh` launcher where the egg expects them. It records the bundle tag on the - `demo-site` host. + and its `with-sidecar.sh` launcher where the egg expects them (`rust-link/`, each `chmod 755`). + It records the bundle in `state/bundle.json` on the `demo-site` host, which is what "changed" + compares against, and in the rig's own `rust-link/bundle.json`, so the rig says what it runs. + The plugin files are the bridge's tarball and RunicNPC's (`payload` and `npc`); the other plugins + in `oxide/plugins/` (Kits, ZoneManager and the optional ones) are left alone. 2. **Always.** It picks the module release as above, and writes `MODULES=rust@=` - into the deploy directory's `.env` before §7.3. + to `state/modules.env` in the deploy directory before §7.3. The compose file reads it as a second + `env_file`, so the workflow never edits the `.env` that holds the secrets. `Module-Rust`'s release CI adds `protocol` (read from `PROTOCOL_VERSION` in `server/sidecarClient.js`) to the install manifest it already publishes, so the choice needs no @@ -403,8 +420,9 @@ down. | `DEMO_MODE` lock, banner field, `seed:demo` script | `website` (core) | | The Rust module's `demoLocks`, the console-command deny list (§3.4), the config-editor rules (§3.5), and `protocol` in its install manifest (§7.5) | `Module-Rust` | | `DEMO_LOCKED` error kind and the banner in the app | `Android-app` | -| `deploy/demo/` (compose file, `.env.example`, seed fixture) and `.gitea/workflows/demo-wipe.yml` + `demo-baseline.yml` | `website` (DM8) | -| `.env` (JWT secret, encryption key, the rig's sidecar token) | On the `demo-site` host only, never in a repo | +| `deploy/demo/` (compose file, `.env.example`, seed fixture `seed.json`, `rig.mjs` for the rig and the versions, README) and `.gitea/workflows/demo-wipe.yml` + `demo-baseline.yml` | `website` (DM8) | +| The deploy directory, `/opt/demo.runicgateway.com` (DM23): the copied deploy files, `.env`, `state/` (`bundle.json`, `modules.env`, `plan.json`), `modules/` | The `demo-site` host | +| `.env` (JWT secret, encryption key, `BOT_INTERNAL_KEY`, database passwords, the four demo passwords, the stand-in Steam id, the rig's sidecar URL and token) | On the `demo-site` host only, never in a repo | | `PTERODACTYL_CLIENT_KEY` (the demo subuser's key) | A Gitea Actions secret on `website` | | `demo-baseline.tar.gz` | On the rig, at the Pterodactyl server root | @@ -435,6 +453,9 @@ down. | DM19 | (build, step 4) Each app screen writes its own failure line, and the admin screens turn every `403` into "You don't have permission for that action". How does the app show a refusal? | **One app-wide snackbar** with the server's sentence, like the web's toast, and the screens whose `403` line blames the role say the demo sentence instead. `ErrorKind.DEMO_LOCKED` for load screens (§3.1) | | DM20 | (build, step 4) Should the app's sign-in screen list the logins, as the web's does? | **No, the banner only.** The Home strip names the site's sign-in page for the passwords; Google's reviewer gets `play-review`'s from the Play Console (§6) | | DM21 | (amendment, 2026-10-26) Settings are open, so the hero editor's Publish changes the homepage every visitor sees. What may a visitor do in the hero editor? | **Edit and preview, never publish.** `hero_layout` joins the protected settings keys; the draft and Preview stay open; uploads were already locked. Server lock only: Publish stays clickable and answers the demo sentence (§3.3) | +| DM22 | (build, step 5) The subuser has file and power permissions only, so `server.save` over the console is out of reach. How does the baseline capture save the world? | **A power `stop`**, which runs the egg's `quit` and writes the save; no console permission (§7.2) | +| DM23 | (build, step 5) Where does the demo live on its host? | **`/opt/demo.runicgateway.com`**, the `demo-site` runner in host mode, like `rgcom`'s `/opt/runicgateway.com`. The org lead writes `.env` from `deploy/demo/.env.example` | +| DM24 | (build, step 5) The module's demo code was on `edge` only, so no release could be installed. Cut over? | **Yes, now:** `Module-Rust` `edge` → `main` releases v0.6.0, the first with `protocol` in its install manifest | DNS and HTTPS for `demo.runicgateway.com` come from an external reverse proxy, which the org lead is setting up. The app sits behind it on plain HTTP; core already trusts one proxy hop for @@ -491,6 +512,10 @@ through that proxy. event; a denied console command refused at save and at run; and a plugin config changed and reloaded through the editor, with the bridge's config refused for reading and another plugin's secret field redacted; and the hero editor's draft previewed while its Publish is refused - (DM21). One PR. + (DM21). One PR. **Files built 2026-10-26** (DM22-DM24): `deploy/demo/` and the two workflows. + The site half (compose, `down -v`/`up`, healthcheck, `seed:demo` with this fixture and the Rust + module at 0.6.0) ran locally against an image built from `main`; `rig.mjs plan` and `smoke` ran + against the real Gitea; the Pterodactyl file calls ran against `egg-oxide` (§7.2). The first + deploy, the baseline and the walk wait on the host's `.env` and `Module-Rust` v0.6.0. 6. **Play**: the sign-in details in the Play Console point at `play-review`; runicgateway.com `/beta` publishes the logins.