Running a public demo
Stand up a throwaway instance that reseeds itself nightly.
A throwaway OpenCMS instance on Cloudflare Workers and D1, seeded with sample content and reset on a schedule. This is what a "Try it" link on the site should point at.
It is a second deployment, deliberately separate from anything real: it has published admin credentials, so treat it as hostile territory and never point it at a database you care about.
1. Create the demo database
cd apps/worker
bunx wrangler login
bunx wrangler d1 create opencms-demoCopy the returned database_id. Rather than editing wrangler.toml, which
belongs to your real deployment, keep the demo in its own environment file so
the two can never be confused:
# apps/worker/wrangler.demo.toml
name = "opencms-demo"
main = "src/index.ts"
compatibility_date = "2026-07-01"
[assets]
directory = "../admin/dist"
binding = "ASSETS"
not_found_handling = "single-page-application"
run_worker_first = ["/api/*", "/health"]
[[d1_databases]]
binding = "DB"
database_name = "opencms-demo"
database_id = "PASTE_THE_DEMO_DATABASE_ID"2. Set the auth secret
openssl rand -base64 32 | bunx wrangler secret put BETTER_AUTH_SECRET -c wrangler.demo.toml3. Build the admin and deploy
bun run --cwd ../.. build:admin
bunx wrangler deploy -c wrangler.demo.tomlThe schema needs no migration step. The Worker creates the content and auth tables on first request, and both inits are idempotent.
4. Seed it
The first signup on an empty database becomes the admin, which is exactly what the seed script does:
OPENCMS_URL=https://opencms-demo.<your-subdomain>.workers.dev \
OPENCMS_EMAIL=demo@opencms.dev \
OPENCMS_PASSWORD='demo-password-123456' \
bun run seed:demoIt creates an article and a changelog type, three published articles, one
draft (so the demo can show that drafts 404 rather than merely being filtered
out), and a changelog entry. Re-running it updates in place instead of
duplicating, so it is safe to point at a live demo.
Publish those credentials on the demo page. They are the point.
5. Check it
curl https://opencms-demo.<your-subdomain>.workers.dev/api/content/article
curl -o /dev/null -w '%{http_code}\n' \
https://opencms-demo.<your-subdomain>.workers.dev/api/content/article/slug/an-unpublished-draft
# 404, because anonymous callers never see drafts6. Reset it on a schedule
A public demo with published admin credentials will be vandalised, so wipe it
nightly. .github/workflows/demo-reset.yml does this: it drops every table,
pokes the Worker once so the schema is recreated, then re-seeds.
It needs three repository secrets: CLOUDFLARE_API_TOKEN (with D1 edit
permission), DEMO_URL, and DEMO_PASSWORD. Run it manually from the
Actions tab once before trusting the schedule.
What the demo does not cover
Media uploads (M5) and the MCP surface (M6) are not built yet, so the media field renders as a plain storage-key input and there is nothing agent-facing to show. Say so on the demo page rather than letting a visitor find out by clicking.