Public Read-only Demo Mode
Applies to: ChengOS v0.2.0+ | Last verified: 2026-09-25 | Source:
crates/cheng-api/src/middleware/demo_mode.rs,crates/cheng-api/src/rest/handlers/auth/mod.rs,deploy/README.md,deploy/.env.example
Demo mode turns a ChengOS deployment into a public, read-only showcase. Visitors do not sign in: the UI enters the app as one fixed demo account, and they can browse workflows, canvases, conversations, and documents — but nothing they do can change anything, run anything, or read a secret.
Run a demo as its own deployment with its own database, seeded with the content you want to show. Never switch a production instance into demo mode.
How it works
Two settings in deploy/.env drive it:
| Variable | Meaning |
|---|---|
CHENG_DEMO_MODE | true enables the demo policy (also accepts 1, yes, on). Default false. |
CHENG_DEMO_USER_EMAIL | The ordinary account visitors are signed in as. |
With demo mode on, the UI calls POST /api/v1/auth/demo. That endpoint takes no credentials
and returns a normal session for the demo account. It refuses to run when:
| Response | Reason |
|---|---|
404 DEMO_MODE_DISABLED | Demo mode is off — the endpoint does not exist on a normal deployment |
503 DEMO_USER_UNAVAILABLE, reason: unset | CHENG_DEMO_USER_EMAIL is not set |
503 DEMO_USER_UNAVAILABLE, reason: not_found | No active account has that email |
503 DEMO_USER_UNAVAILABLE, reason: admin | The account is a platform administrator — never handed out |
Signing in writes nothing: no last-login stamp, no workspace repair. The demo database is prepared ahead of time.
What visitors can and cannot do
The policy is an allowlist applied to the whole API router, so a route added in a future release is denied until someone explicitly reviews it. It fails closed.
Allowed
GET/HEADreads under/api/v1/— the whole app renders.GET /health,GET /ready.POST /api/v1/auth/demo,/auth/refresh,/auth/logout, plusGET /auth/me,/auth/validate,/auth/bootstrap-status.POST /api/v1/nodes/schemas:resolve— a bounded read that happens to usePOST.GET /api/v1/llm/providersand/llm/models, which in demo mode return only the catalog fromproviders.tomland never touch stored credentials or probe remote APIs.- A workspace itself and its knowledge bases.
Refused with 403 DEMO_MODE_RESTRICTED
- Every write: any
POST,PUT,PATCH, orDELETEnot listed above — including login, registration, and password changes. - Every execution transport, and every WebSocket.
- Reads under these prefixes:
/admin,/account,/auth(except the session endpoints above),/channel,/credentials,/deployment-settings,/internal,/llm(except the catalog),/mcp,/settings,/setup,/skill-registry,/translate,/webhook,/ws. - A workspace's members, channels, channel keys, settings, and storage providers.
403 DEMO_MODE_RESTRICTED means "this deployment is a demo", not a permission problem with the
account.
Demo mode also stops the server from changing itself: RUN_MIGRATIONS is ignored, and the
scheduler, MCP recovery, channel polling and streams, OAuth token refresh, code indexing, file
watchers, and cleanup jobs do not start. The API logs a warning at startup saying so.
Setting one up
Prepare the content with demo mode off, on a trusted network, then switch:
# 1. Normal mode: set up the demo content.
# deploy/.env: CHENG_DEMO_MODE=false
./chengos.sh start
# In the UI, over a trusted network: create the demo account (an ordinary,
# non-admin user) and its default workspace, then build what you want to show.
./chengos.sh stop
# 2. Demo mode.
# deploy/.env: CHENG_DEMO_MODE=true
# [email protected]
./chengos.sh start
Then verify from outside:
curl -s -X POST https://demo.example.com/api/v1/auth/demo | head -c 200 # a token
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
https://demo.example.com/api/v1/auth/login # 403
Maintenance
Anything that changes the demo — a release upgrade that migrates the database, new content, a reset — happens with demo mode off:
- Take the instance off the public internet.
- Set
CHENG_DEMO_MODE=falseand restart. - Upgrade, migrate, or edit the content.
- Set
CHENG_DEMO_MODE=true, restart, and put it back online.
Network exposure
Demo mode protects the API's behaviour, not its ports. Expose only your reverse proxy or tunnel, exactly as for any public deployment — see Hardening a Public Deployment. The API stays on loopback, and the database, Valkey, and Qdrant ports are never public.
Next steps
- Hardening a Public Deployment — the network side of a public demo.
- Authentication — the session the demo login issues.
- Environment Variable Reference —
CHENG_DEMO_MODEand friends.