Skip to main content

Demo & dev environments — runbook

Two isolated, non-production surfaces from the design:
  • demo.findmydata.io — a public, isolated demonstration of the real product running on synthetic data only, safe for anyone to poke at.
  • dev.findmydata.io — a private engineering environment behind identity-aware access; never public, never customer data.
Neither touches a customer tenant or the vendor commercial plane.

1. Demo mode (product) — built

The product has a fail-closed demo mode (FMD_DEMO_MODE=true, kernel/config.ts). When set, the deployment refuses to boot unless it is safe as a public demo:
  • FMD_CONNECTOR_MODE must be mock — a demo uses synthetic data via the mock M365 tenant, never a live Graph tenant. (Boot fails on graph.)
  • Live label writes (FMD_FEATURE_PURVIEW_LABEL_WRITE) and directory sync (FMD_FEATURE_ENTRA_DIRECTORY_SYNC) are forbidden (boot fails if enabled).
  • demoMode is surfaced at GET /api/ready; the web UI shows a persistent “Demo environment — synthetic data only” banner.
So even a misconfigured demo cannot reach a real tenant or write anything back. Run a demo locally:
Reset on a schedule (wipe + reseed) so the public demo stays clean and can’t accumulate abuse. The demo DB is disposable — reset = delete the DB file (or volume) and re-run migrate + seed:
Wire that into a scheduled job (cron / Container Apps job / GitHub Actions schedule) at whatever cadence you like (e.g. every 6 h).

Hosting the demo

The product is a long-running Bun + SQLite container, so it needs a container host (not Cloudflare Workers). Reuse the existing Azure Container Apps IaC (infra/main.bicep) with a demo profile — set FMD_DEMO_MODE=true, FMD_CONNECTOR_MODE=mock, FMD_AUTH_MODE=dev (or a demo IdP), an ephemeral volume (so a restart is a clean slate), and low CPU/memory + max-replicas=1 to bound cost. Fly.io / Render work equally well. Then:
  • Cloudflare DNS: demo → the container host (proxied is fine here — no GitHub Pages cert dependency).
  • Put Cloudflare Turnstile (or Bot Fight Mode) in front to bound abuse.
  • Point the scheduled reset job at the demo instance’s volume/DB.
Distinguish this safe public demo from disposable Microsoft CDX engineering resources (see ADR-0012) — CDX is for live-tenant engineering, not a public demo.

2. Protected dev — dev.findmydata.io via Cloudflare Access

Do not rely on an obscure hostname. Gate the dev instance with Cloudflare Access (Zero Trust) — an identity-aware proxy with SSO/MFA and narrow membership. Access is free up to 50 users. Setup (Cloudflare dashboard → Zero Trust):
  1. Cloudflare Tunnel (cloudflared) from the dev host so it needs no public inbound ports — the tunnel dials out to Cloudflare, which routes dev.findmydata.io to it. Create the tunnel, run cloudflared on the dev box, and add a public hostname dev.findmydata.iohttp://localhost:8710.
  2. Access application for dev.findmydata.io:
    • Policy: Allow only a named list of emails / an identity-provider group (e.g. your Google Workspace), require MFA.
    • Session duration short (e.g. 24 h); everything else blocked by default.
  3. Cloudflare DNS: the Tunnel creates the dev CNAME automatically (proxied).
  4. Validate: an unauthenticated request to https://dev.findmydata.io is challenged by Access; only allowlisted, MFA’d identities get through.
The dev instance runs like any deployment (dev auth or an IdP) but is reachable only through the Access-protected tunnel — no ports open to the internet, no customer data.

Change discipline

Record each Access application / policy / tunnel change in docs/cloudflare-runbook.md with purpose + rollback, per the domain guardrails (inspect first, reversible, preserve registrar/DNSSEC/email posture). Cloudflare Access + Tunnel changes are additive and reversible (delete the application/tunnel to remove them).

Cost summary