docs(website): design demo mode for demo.runicgateway.com #377
@@ -1,6 +1,6 @@
|
||||
# Demo mode — `demo.runicgateway.com`
|
||||
|
||||
**Status:** design, **fully decided** 2026-10-23 (DM1-DM13, §9). Ready to build per §10. Nothing is
|
||||
**Status:** design, **fully decided** 2026-10-23 (DM1-DM14, §9). Ready to build per §10. Nothing is
|
||||
built yet.
|
||||
**Issue:** `RunicGateway/website#222`. **Why now:** Google Play (`../android/PLAN.md` item 9,
|
||||
M8) needs a public HTTPS site with working sign-ins for its reviewers and for the 12 testers who
|
||||
@@ -49,8 +49,8 @@ The web client shows the message as a toast wherever an action fails with that c
|
||||
app maps the code to a new error kind with the same wording; today it would show the generic
|
||||
"Something went wrong", which is misleading for a reviewer.
|
||||
|
||||
Reads are never locked. Every page can be opened and every form filled in; only the save is
|
||||
refused. That keeps the demo useful as a tour of the admin panel.
|
||||
Reads are never locked, with one exception: the bridge's config (§3.5). Every page can be opened
|
||||
and every form filled in; only the save is refused. That keeps the demo useful as a tour of the admin panel.
|
||||
|
||||
**The server is the lock, never the UI (DM10).** Every lock is enforced at the route, so a request
|
||||
sent with `curl`, an old app build, or a hand-edited page is refused exactly like a click. The UI may
|
||||
@@ -104,7 +104,7 @@ wipe puts them back (§7).
|
||||
| Area | Routes | Why |
|
||||
| --- | --- | --- |
|
||||
| The link to the rig | `PUT`/`DELETE /admin/rust/servers/:id` | Repoint or delete the sidecar link; the site goes dark |
|
||||
| Plugin config files | `POST /admin/rust/config/:serverId/file` | Writes a plugin's config file on the rig's disk |
|
||||
| The bridge's config, **read or written** | `GET`/`POST /admin/rust/config/:serverId/file`, `GET …/writes…` for the `RunicGateway` plugin's files, which are also left out of `GET …/files` | It holds the bridge's link to the sidecar and its credentials. Read, it would let someone talk to the sidecar directly over HTTP (DM14, §3.5) |
|
||||
|
||||
**Settings stay open (DM2).** Theme, navigation and the other `PUT /admin/settings` values can be
|
||||
changed and come back at the wipe. Brand images are uploads, so they fall under the upload lock.
|
||||
@@ -166,6 +166,41 @@ Commands that only change the world or the players (`server.writecfg`, `entity.*
|
||||
`weather.*`, `say`, `kick`, `ban`, `oxide.grant`) stay open. The baseline restore puts all of them
|
||||
back (§7.2).
|
||||
|
||||
### 3.5 The plugin config editor (DM14)
|
||||
|
||||
Every plugin's config **except the bridge's** can be read and changed on the demo. A visitor can tune
|
||||
RunicNPC, Kits, Economics and the rest and watch the change take effect. Three things make that safe:
|
||||
|
||||
- **The save reloads through the bridge, not the console.** The bridge backs up the file, writes
|
||||
it, reloads the plugin that owns it, and puts the old file back if that plugin fails to load (D178,
|
||||
D179). The §3.4 deny list never sees this reload, and a config that breaks its plugin undoes
|
||||
itself at once.
|
||||
- **The wipe restores `oxide/config/`** from the baseline (§7.2), so a change lasts two hours at
|
||||
most.
|
||||
- **The bridge's own config is out of reach.** The editor's routes refuse every file the
|
||||
`RunicGateway` plugin owns, for reading as well as writing, and the file list leaves those files
|
||||
out. This is the one place where §3.1's "reads are never locked" gives way. The file holds the
|
||||
bridge's link to the sidecar and its credentials, and anyone holding them could talk to the
|
||||
sidecar directly over HTTP, outside every lock in this document.
|
||||
|
||||
**Secrets in other plugins' configs.** A plugin config can hold a webhook URL or an API key. The
|
||||
editor already flags those fields by key name (`SECRET_WORDS` in `configEdit.js`), but today it
|
||||
returns the full text and masks them only **in the form**, behind a reveal button. Under DM10 that
|
||||
mask is not a lock. On the demo, the server therefore:
|
||||
|
||||
- **redacts** every flagged value in `GET …/file` and in the write history before it leaves the
|
||||
module, replacing it with a placeholder;
|
||||
- makes flagged fields **read-only**: a form save that edits one is refused;
|
||||
- refuses **raw-JSON saves** (`text` instead of `edits`), because the raw document a visitor sends
|
||||
back would hold the placeholders and overwrite the real values. Form saves splice only the fields
|
||||
that changed, so untouched secrets stay as they are on disk.
|
||||
|
||||
**The link on the Servers screen.** The module's admin view of a server never returns the sidecar
|
||||
token; it returns `hasToken`. On the demo it also leaves out the sidecar's **URL**, so the screen
|
||||
gives away nothing that helps someone reach the sidecar. Editing the link stays locked (§3.3).
|
||||
|
||||
All of this is enforced in the module's server code behind `ctx.demoMode` (DM10), with tests.
|
||||
|
||||
## 4. Static logins
|
||||
|
||||
| Account | Role | Notes |
|
||||
@@ -307,7 +342,7 @@ down.
|
||||
| Piece | Repo |
|
||||
| --- | --- |
|
||||
| `DEMO_MODE` lock, banner field, `seed:demo` script | `website` (core) |
|
||||
| The Rust module's `demoLocks`, the console-command deny list (§3.4), and `protocol` in its install manifest (§7.5) | `Module-Rust` |
|
||||
| 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 |
|
||||
@@ -333,6 +368,7 @@ down.
|
||||
| DM11 | (F1) Console-command steps on the demo | A **deny list** of the commands that would break the rig (`quit`, `oxide.unload RunicGateway` and the like), not an allowlist; everything else runs (§3.4) |
|
||||
| DM12 | (F2) What "latest" means across the Rust link | The latest **Rust bundle** for the rig's plugins and sidecar, the newest module release on the same protocol, and the website's `main` image; the wipe updates the rig when the bundle changes (§7.5) |
|
||||
| DM13 | (F3) DM2 read correctly? | Yes: only the settings stay open; every other Tier A row stays locked |
|
||||
| DM14 | (after DM13) May visitors change plugin configs? | Yes, every plugin except the bridge. The bridge's config can be **neither read nor written**, because it holds credentials that would let someone talk to the sidecar directly (§3.5) |
|
||||
|
||||
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
|
||||
@@ -354,9 +390,9 @@ through that proxy.
|
||||
`seed:demo`, `noindex`, the web banner and toast, the `MODULE_API` minor bump, and the OpenAPI
|
||||
note that a route may answer `DEMO_LOCKED`. Tests for every lock. One PR, plus `MODULE_API.md`
|
||||
and this doc.
|
||||
2. **Module** (`Module-Rust`): `demoLocks` in `module.json`, the console-command deny list behind
|
||||
`ctx.demoMode` (§3.4), and `protocol` in the release's install manifest (§7.5), each with
|
||||
tests. One PR.
|
||||
2. **Module** (`Module-Rust`): `demoLocks` in `module.json`; behind `ctx.demoMode`, the console-command
|
||||
deny list (§3.4) and the config-editor rules (§3.5); and `protocol` in the release's install
|
||||
manifest (§7.5). Each with tests. One PR.
|
||||
3. **Kit** (`Integration-kit`): a paragraph on `demoLocks` and `ctx.demoMode`, and the
|
||||
`core-ref.json` re-pin. One PR.
|
||||
4. **App** (`Android-app`): the `DEMO_LOCKED` error kind and the banner on Home. One PR.
|
||||
@@ -364,6 +400,8 @@ through that proxy.
|
||||
`demo-baseline.yml`; the first deploy on `demo-site`; setting up the default set and capturing
|
||||
the baseline. Then a walk of every static login, every Tier A lock and the upload lock, in the
|
||||
browser and the app, plus one full wipe cycle that restores a changed permission, NPC and
|
||||
event, and a denied console command refused at save and at run. One PR.
|
||||
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. One PR.
|
||||
6. **Play**: the sign-in details in the Play Console point at `play-review`;
|
||||
runicgateway.com `/beta` publishes the logins.
|
||||
|
||||
Reference in New Issue
Block a user