feat(demo): DEMO_MODE lock, seed:demo and the demo banner (MODULE_API 1.18.0) #223
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/demo-mode-core"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Core half of the public demo (#222). This is step 1 of
docs/website/DEMO_MODE.md§10. Design PR: docs#377 (merged). Docs for this PR: RunicGateway/docs PR ondocs/demo-mode-core.Nothing here runs unless
DEMO_MODE=true.app.jsmounts none of it otherwise, and a test proves it (demoAppOff.test.js).Server
The lock (
server/src/demo/). It is mounted at/api/v1, ahead of every router. A match answers403 { message, code: "DEMO_LOCKED" }before the route's own auth check. Reads are never locked./admin/users/:id…are refused when the target is a demo account./auth/me/…account changes are refused when the caller is signed in as one. The account row decides, not the token's username claim.POST /auth/registeris locked.DELETE /auth/me/sessions/:idis locked on a demo account.PUT /admin/settingskeys. That route writes arbitrary keys, so{site_mode: "maintenance"}there got around the/admin/site-modelock. On the demo it now refuses a body namingsite_mode*,module_source_hosts,teams_voice_*ordemo_*, and names the keys. Theme, nav and the other settings stay open (DM2). This applies locks you already decided; it adds no new decision. ⚠️ Flagging it for review anyway.Uploads. Any
multipart/*request is refused app-wide. PlusX-Robots-Tag: noindexandrobots.txt: Disallow: /.Loader (MODULE_API 1.18.0, both halves).
demoLocksinmodule.json: writes only, under the module's own mounts, validated on every boot.ctx.demoMode.api.onDemoSeed(DM15).GET /public/settingsgainsdemo: { resetsAt, logins }on the demo only.npm run seed:demo -- <fixture>:onDemoSeed;demo_state.It resolves
"$env:NAME"strings, so the rig token never sits in the repo. It refuses to run off the demo.Client
settings.demo.DEMO_LOCKEDtoast, fed from the request primitive, so installed modules' screens get it too./account/loginand/admin/login, with a Use button.Verified
npm test(server): 2206 pass, 0 fail, withMODULES_DIRpointed away from a localmodules/checkout. That checkout makesrouteManifest.test.jsfail on cleanmaintoo.npm test(client): 435 pass.npm run buildis OK.demoLocks.test.js: every core row is held againstroutes.manifest.json; every upload route is locked by name.demoModule.test.js: loader,ctx.demoMode,onDemoSeed, the public block, and the seed helpers.demoApp.test.js/demoAppOff.test.js: the real app with the flag on and off.DEMO_MODE=true, a probe module declaring ademoLockand anonDemoSeed):seed:demoran twice. The re-run left content alone and reset the accounts.routes.manifest.jsonhas a zero-line diff.engagement-triggers.jsonwas regenerated (it embeds the API version).Not in this PR
Module-Rust (step 2), the kit re-pin (3), Android (4), and
deploy/demo/with the workflows (5).AI-assisted: written with Claude Code (Claude Opus 5.5).
🤖 Generated with Claude Code
https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Core half of the public demo (docs/website/DEMO_MODE.md §10 step 1, website#222). Nothing here runs unless DEMO_MODE=true. - server/src/demo/: the lock at /api/v1, ahead of every router. It covers every Tier A row of §3.3, the shared-login conditions, POST /auth/register (DM16), DELETE /auth/me/sessions/:id on a demo account (DM17), and the PUT /admin/settings keys that reach a Tier A lock. Any multipart request is refused app-wide. noindex and robots.txt. A condition that cannot decide refuses. - Loader: demoLocks in module.json (writes only, under the module's own mounts), ctx.demoMode, and api.onDemoSeed (DM15). MODULE_API 1.18.0 on both halves. - GET /public/settings carries demo: { resetsAt, logins } on the demo only. - npm run seed:demo -- <fixture>: upserts the four accounts, sets the site live with registration disabled, seeds content on the first run only, calls each module's onDemoSeed, and stamps demo_state. It resolves $env: strings and refuses to run off the demo. - Client: the banner, one DEMO_LOCKED toast fed from the request primitive (so module screens get it too), and the published logins on both sign-in pages. Dashboard and register say the demo message instead of blaming a role. - OpenAPI: DemoLocked schema, the demo block, and a note that any write may answer DEMO_LOCKED on the demo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY