Skip to content
Keel
All docs

Public demo mode

Demo mode adds a "Try the demo" path: every "Live demo" link goes to /demo, and one button there signs the visitor into a single shared account that's already full of sample data. It's off by default, so a fresh clone or a normal production deploy never exposes a public login.

Turning it on

Set these on the deployment that should host the demo:

  • NEXT_PUBLIC_DEMO_MODE: true turns demo mode on. Inlined at build time, so redeploy after changing it.
  • DEMO_USER_PASSWORD: password for the demo account, at least 16 random characters. Only the server uses it, nobody types it.
  • DEMO_USER_EMAIL: optional, the demo account's email. Defaults to demo@example.com (the dev seed's login).
  • DEMO_TEAMMATE_EMAIL: optional, email for the seeded second team member.

Then create the account against that deployment's database:

bash
bun run db:seed-demo --yes

The script prints the database host and refuses to write without --yes, since Bun loads .env.local on its own. Re-running it resets the account: password, profile, 2FA, sessions, team name, members, credits, usage history and notifications all go back to the seeded state. Running it nightly (a cron job or a scheduled CI workflow) keeps the demo clean.

Locally, bun run db:seed plus NEXT_PUBLIC_DEMO_MODE=true and DEMO_USER_PASSWORD=password1234 is enough to try it.

What the demo account can and can't do

The demo account can read everything in its team and use the AI chat (which spends the team's seeded credits). Everything else is refused with a 403:

  • Team changes: rename, logo, delete, invites, role changes, removing members or leaving the team, creating new teams.
  • Billing changes: checkout, portal, cancel, resume.
  • Creating or revoking API keys, and avatar uploads.
  • Account security: profile, email, password, 2FA, sessions, linked logins, deleting the account.

The team checks live in authorizeTeamAccess (lib/dal/teams.ts), which only lets the demo through for the permissions listed in lib/demo.ts. A new permission you add is blocked for the demo until you add it there. The account checks live in the Better Auth hook in lib/auth.ts.

Limits

POST /api/demo/session allows 5 sign-ins per IP every 10 minutes (Upstash when configured, an in-memory fallback otherwise). The per-email sign-in limit in lib/auth.ts skips the demo email, since every visitor shares it. The AI chat keeps its own per-team rate limit, and the demo team's credits only go back up when the seed script runs again.

Last updated Oct 21, 2026