docs(website): DM25 data: URI lock on the demo + first-wipe notes
DEMO_MODE.md §3.3 gets the data: URI rule (a pasted image is an upload), §9.1 records DM25, and §10 step 5 records the first wipes: the rig half worked, and the seed loaded no module because the runner's chown of modules/ and logs/ failed silently. BACKEND_DESIGN.md §5.1 mirrors the rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -1834,7 +1834,10 @@ it.
|
|||||||
A sentence back refuses the write `403 { code: "DEMO_LOCKED", message, errors }` with that sentence; a
|
A sentence back refuses the write `403 { code: "DEMO_LOCKED", message, errors }` with that sentence; a
|
||||||
hook that throws refuses too.
|
hook that throws refuses too.
|
||||||
- **Uploads.** Any request with a `multipart/*` content type is refused app-wide, before a router or
|
- **Uploads.** Any request with a `multipart/*` content type is refused app-wide, before a router or
|
||||||
multer sees it.
|
multer sees it. So is any write whose JSON body has a `data:` URI in any string (DM25): a pasted
|
||||||
|
image is an upload that did not use multipart. The string is read as a browser reads an attribute
|
||||||
|
(character references decoded, tabs and newlines removed); "metadata:" and prose such as
|
||||||
|
"Data: none, yet" pass. Matching rules are in DEMO_MODE.md §3.3.
|
||||||
- **Search engines.** Every response carries `X-Robots-Tag: noindex, nofollow`. `/robots.txt` is
|
- **Search engines.** Every response carries `X-Robots-Tag: noindex, nofollow`. `/robots.txt` is
|
||||||
`Disallow: /`, served ahead of the SPA's static file.
|
`Disallow: /`, served ahead of the SPA's static file.
|
||||||
- **`GET /public/settings`** gains `demo: { resetsAt, logins }`, from the `demo_state` settings row.
|
- **`GET /public/settings`** gains `demo: { resetsAt, logins }`, from the `demo_state` settings row.
|
||||||
|
|||||||
@@ -151,6 +151,20 @@ next wipe.
|
|||||||
body is `multipart/form-data`, before a router sees it. An upload route added later is locked
|
body is `multipart/form-data`, before a router sees it. An upload route added later is locked
|
||||||
without anyone remembering to list it, and a module cannot open one.
|
without anyone remembering to list it, and a module cannot open one.
|
||||||
|
|
||||||
|
**A pasted image is an upload too (DM25).** An image pasted into rich text (a post, a wiki page, an
|
||||||
|
event description, a forum post, the hero draft) does not use multipart. It travels inside the
|
||||||
|
JSON body as a `data:` URI. So with `DEMO_MODE` on, core also refuses **any write**
|
||||||
|
(`POST`/`PUT`/`PATCH`/`DELETE`) whose JSON body carries a `data:` URI in **any string**, on any
|
||||||
|
route, core's or a module's, before a router sees it. As with multipart, nothing is listed, so a
|
||||||
|
rich-text field added later is covered without anyone remembering it. Every `data:` URI counts,
|
||||||
|
not only `data:image/*`. A match is `data:` that is not the end of a longer word (so "metadata:"
|
||||||
|
does not match), followed by a comma, a `;`, or a `type/subtype` and then a comma. So prose such
|
||||||
|
as "Data: none, yet" saves. The string is first read as a browser reads an attribute: HTML
|
||||||
|
character references are decoded and tabs and newlines removed, so `data:` and a `data:`
|
||||||
|
split by a newline are still refused. Reads are never checked. (Off the demo, the server's HTML
|
||||||
|
sanitizer already drops `data:` from `<img>` on news, wiki and forum bodies, but not on every
|
||||||
|
rich-text field. The demo rule is the one rule that covers them all.)
|
||||||
|
|
||||||
**Tier B — the rig: open, reset by the wipe (DM3).** Permission writes (policy, grant, revoke,
|
**Tier B — the rig: open, reset by the wipe (DM3).** Permission writes (policy, grant, revoke,
|
||||||
groups, members, drift actions, sync), RunicNPC writes (factions, profiles, placements, push,
|
groups, members, drift actions, sync), RunicNPC writes (factions, profiles, placements, push,
|
||||||
respawn), economics and permission grants to a linked player, the world actions events run, and map
|
respawn), economics and permission grants to a linked player, the world actions events run, and map
|
||||||
@@ -456,6 +470,7 @@ down.
|
|||||||
| 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) |
|
| 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` |
|
| 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 |
|
| 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 |
|
||||||
|
| DM25 | (amendment, 2026-10-27) Uploads are locked, but an image pasted into rich text is saved as a `data:` URI inside the JSON body. Block it? | **Yes, on every write and for every `data:` URI.** Core refuses any write whose JSON body has a `data:` URI in any string, on any route, the same way the multipart rule needs no list (§3.3) |
|
||||||
|
|
||||||
DNS and HTTPS for `demo.runicgateway.com` come from an external reverse proxy, which the org lead is
|
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
|
setting up. The app sits behind it on plain HTTP; core already trusts one proxy hop for
|
||||||
@@ -517,5 +532,10 @@ through that proxy.
|
|||||||
module at 0.6.0) ran locally against an image built from `main`; `rig.mjs plan` and `smoke` ran
|
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
|
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.
|
deploy, the baseline and the walk wait on the host's `.env` and `Module-Rust` v0.6.0.
|
||||||
|
**First wipes 2026-10-27:** the host's `.env` was written and v0.6.0 released. Wipe 358's rig
|
||||||
|
half worked (bundle 2026.10.22 written, rig restarted), but the seed loaded no module: the
|
||||||
|
runner is not root, so its `chown` of `modules/` and `logs/` failed silently. The wipe now does
|
||||||
|
the chown in a root container. The same day, DM25 added the `data:` URI rule (§3.3) to the walk's
|
||||||
|
upload checks.
|
||||||
6. **Play**: the sign-in details in the Play Console point at `play-review`;
|
6. **Play**: the sign-in details in the Play Console point at `play-review`;
|
||||||
runicgateway.com `/beta` publishes the logins.
|
runicgateway.com `/beta` publishes the logins.
|
||||||
|
|||||||
Reference in New Issue
Block a user