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:

VariableMeaning
CHENG_DEMO_MODEtrue enables the demo policy (also accepts 1, yes, on). Default false.
CHENG_DEMO_USER_EMAILThe 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:

ResponseReason
404 DEMO_MODE_DISABLEDDemo mode is off — the endpoint does not exist on a normal deployment
503 DEMO_USER_UNAVAILABLE, reason: unsetCHENG_DEMO_USER_EMAIL is not set
503 DEMO_USER_UNAVAILABLE, reason: not_foundNo active account has that email
503 DEMO_USER_UNAVAILABLE, reason: adminThe 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

Refused with 403 DEMO_MODE_RESTRICTED

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:

  1. Take the instance off the public internet.
  2. Set CHENG_DEMO_MODE=false and restart.
  3. Upgrade, migrate, or edit the content.
  4. 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