Merge pull request 'docs(website): demo step 5 built — deploy files, DM22-DM24' (#385) from docs/demo-deploy into main

Reviewed-on: #385
This commit is contained in:
2026-10-27 17:31:19 +00:00

View File

@@ -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@<version>=<manifest>`, which the workflow writes (§7.5).
3. Wait for the app's healthcheck.
4. `docker compose exec app npm run seed:demo -- <fixture.json>` (or `DEMO_SEED_FILE`) — one core
the schema, and installs the Rust module from `MODULES=rust@<version>=<manifest>`, 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@<version>=<manifest>`
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.