ADR-0022: Demo & dev environments (fail-closed demo mode; Cloudflare Access for dev)
Status: Accepted · 2026-07-15Context
The design calls for two non-production surfaces: an isolated public demo (demo.findmydata.io) on synthetic data, and a protected engineering
dev environment (dev.findmydata.io). The risk is that either accidentally
touches a customer tenant or exposes an unauthenticated environment. This ADR
makes the demo safe by construction and specifies the dev protection.
Decisions
1. Demo mode is a fail-closed product flag, not a deployment convention
FMD_DEMO_MODE=true (kernel/config.ts) makes the deployment refuse to boot
unless it is safe as a public demo: the connector must be mock (synthetic
data — never a live Graph tenant), and live label writes + directory sync
are forbidden. So a misconfigured demo can’t reach a real tenant or write
anything back — the guarantee is enforced at startup, not left to the operator.
demoMode is surfaced at GET /api/ready, and the web UI shows a persistent
“Demo environment — synthetic data only” banner so it always identifies itself.
2. The demo is disposable and reset on a schedule
The demo runs on synthetic data seeded from the existing fixtures; its database is disposable. A scheduled job wipes + re-migrates + re-seeds it (e.g. every few hours) so it stays clean and can’t accumulate abuse. Hosting reuses the existing Azure Container Apps IaC with a demo profile (ephemeral volume, one small replica,FMD_DEMO_MODE=true), fronted by Cloudflare DNS + Turnstile/Bot Fight
for abuse control. This is distinct from disposable Microsoft CDX engineering
resources (ADR-0012) — CDX is for live-tenant engineering, not a public demo.
3. Dev is gated by Cloudflare Access + Tunnel, never by obscurity
dev.findmydata.io sits behind Cloudflare Access (Zero Trust) — an
identity-aware proxy with SSO/MFA and an explicit allowlist (free ≤ 50 users) — in
front of a Cloudflare Tunnel so the dev host exposes no public inbound
ports. Everything is blocked by default; only allowlisted, MFA’d identities get
through. This is a runbook (docs/demo-and-dev-environments.md),
not repo code — Access/Tunnel are Cloudflare configuration, applied reversibly and
recorded in the Cloudflare runbook per the domain guardrails.
4. Both stay off the customer + commercial planes
Neither environment holds customer data (demo = synthetic; dev = engineering only), and neither is part of the vendor commercial/control plane. The content and plane-separation invariants (ADR-0015) are preserved.Consequences
- The product can be shown publicly with zero risk of touching a real tenant — the demo is safe because the binary refuses an unsafe configuration.
- The dev environment is reachable only through an authenticated, MFA’d, no-open-ports path.
- Out of scope here (ops/config, documented in the runbook): the actual Cloudflare Access application + Tunnel provisioning, the demo container deployment, and the scheduled-reset job wiring.
Verification
kernel/config.test.ts — demo mode boots with the mock connector and flags
demoMode; refuses to boot with graph connector, with live label writes, or
with live directory sync; defaults false. All 311 server tests pass; server + web
typecheck + lint + build clean; the demo banner renders from /api/ready.