Apply configurable-demo-persona: per-instance demo identity + voice

Adds DEMO_BOT_PERSONA env var (inline JSON or file:<path>) so
self-hosted instances can replace Bruno-in-Berlin with their own
demo account identity and content pools. No-op for trails.cool — the
built-in Bruno persona remains the default.

- DemoPersona type + Zod schema validation
- loadPersona() cached at boot; falls back to default on any failure
- ensureDemoUser throws DemoPersonaUsernameClashError when the
  persona username is already a real user; server declines to
  schedule demo jobs for that process
- isDemoUser flag moved from hardcoded "bruno" check to loader-supplied
  boolean computed from the running persona
- docs/demo-persona.md explains the schema + operator flow

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-04-19 10:30:32 +02:00
parent f447852d70
commit fc4485f6ef
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
15 changed files with 877 additions and 113 deletions

View file

@ -34,13 +34,13 @@ services:
WAHOO_CLIENT_ID: ${WAHOO_CLIENT_ID:-}
WAHOO_CLIENT_SECRET: ${WAHOO_CLIENT_SECRET:-}
WAHOO_WEBHOOK_TOKEN: ${WAHOO_WEBHOOK_TOKEN:-}
# Demo-activity-bot (Bruno). Disabled by default everywhere; flip to
# "true" only in prod to let the background worker generate public
# demo routes + activities and prune old ones. Region is a JSON blob
# `{"bbox":[w,s,e,n]}`; retention is in days (default 14).
# Demo-activity-bot. Disabled by default everywhere; flip to "true"
# only in prod. When unset, DEMO_BOT_PERSONA uses the built-in
# Bruno/Berlin persona. See docs/demo-persona.md for the schema.
DEMO_BOT_ENABLED: ${DEMO_BOT_ENABLED:-}
DEMO_BOT_RETENTION_DAYS: ${DEMO_BOT_RETENTION_DAYS:-}
DEMO_BOT_REGION: ${DEMO_BOT_REGION:-}
DEMO_BOT_PERSONA: ${DEMO_BOT_PERSONA:-}
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:3000/api/health || exit 1"]
interval: 15s