Skip to main content

ADR-0022: Demo & dev environments (fail-closed demo mode; Cloudflare Access for dev)

Status: Accepted · 2026-07-15

Context

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.