The production demo account
Demos and screen recordings used to run from the owner’s real account, which puts real clients,
suppliers and invoices on camera. demo@viite.ai exists so they don’t have to: a separate
organisation on the same production deployment, with a board that looks like a healthy studio, a
gmail-triage schedule, and a permanent entitlement so no free-tier ceiling appears mid-recording.
It is an ordinary account. It has no admin rights and no special server-side handling — the only thing that separates it from any other tenant is the data seeded into it.
What the seed builds
Section titled “What the seed builds”bun run demo:seed (scripts/seed-demo-account.ts) writes, in order:
| Step | What | Notes |
|---|---|---|
| Account | demo@viite.ai | Signup alone creates the personal organisation and the owner membership. |
| Entitlement | permanent (complimentary) | POST /api/entitlements/admin/orgs/:orgId/grant-permanent. currentPeriodEnd is null, so nothing can lapse it and no Stripe webhook can match and revoke it. |
| LLM channel | dashboard://viite.ai | Org-scoped, so the fresh organisation inherits nothing. Without it the triage step soft-fails on every fire. |
| Skills | 2 triage skills | Adapted from the owner’s own, with the real supplier list removed. Skills are per-user and private — there is no copy endpoint. |
| Schedule | gmail-triage, daily at 08:00 | Cloned from the gmail-triage workflow template, steered by the urls-to-links skill — the accounting skill dismisses nearly everything a demo mailbox holds, so it ran green and filed nothing. |
| Board | ~30 cards + 3 link captures | Board-native kinds only (task, user-story, custom, deal). No post and no email, so nothing the demo holds can publish. |
The content itself lives in scripts/lib/demoSeed.ts and is unit-tested (bun run test:unit) —
tags have to parse as a group:child taxonomy, titles have to be unique, and the schedule’s skill
reference has to name a skill the seed actually creates.
Running it
Section titled “Running it”Dry-run by default; --apply writes. It is idempotent, so a second apply is a no-op rather than a
duplicate board — re-run it whenever a demo leaves the account messy.
bun run demo:seedDEMO_PASSWORD='…' DEMO_LLM_API_KEY='…' bun run demo:seed --apply| Variable | Required | Default |
|---|---|---|
DEMO_PASSWORD | for --apply | — |
DEMO_LLM_API_KEY | unless a dashboard:// channel already exists | — |
DEMO_EMAIL | no | demo@viite.ai |
DEMO_LLM_PROVIDER | no | anthropic (cline also valid) |
DEMO_LLM_MODEL | no | claude-haiku-4-5 |
DEMO_LLM_DOMAIN | no | viite.ai |
Keep the password and the key in a gitignored file (apps/api/.env.local is the usual home) and
source it — never in the repo, never on the command line you paste into a recording.
The API base URL comes from deploy/coolify/coolify.env like the rest of the tooling.
PLATFORM_API_URL overrides it, which is how you point the seed at local dev. The admin session is
read from ~/.platform-cli/session.json and is used for exactly one call — the entitlement grant —
so it must belong to an account listed in ADMIN_EMAILS.
The two steps a script cannot do
Section titled “The two steps a script cannot do”Connect Gmail. A gmail:// channel is per-user by design and holds that user’s own OAuth
refresh token, so it cannot be copied from another account. Sign in as demo@viite.ai, open
/channels, and connect Gmail with the demo Google account. Until then the schedule exists but
every fire fails at its first step (runRead: source 'Gmail' requires a connected gmail:// channel)
— a failed job, not lost data. The seed prints this as a NEXT STEP whenever the channel is absent.
Nothing else. In particular, do not add demo@viite.ai to ADMIN_EMAILS: it would hand the
demo account the admin org list, the grant endpoint and the org-delete endpoint, and would make its
own organisation undeletable.
Related
Section titled “Related”scripts/purge-dummy-accounts.tskeepsdemo@viite.ai— twice over, since an entitled organisation is never proposed for deletion and the server refuses it anyway.- Jobs architecture — the two engines, scheduling and the overlap policy.
