> ## Documentation Index
> Fetch the complete documentation index at: https://docs.findmydata.io/llms.txt
> Use this file to discover all available pages before exploring further.

# ADR 0022 demo and dev environments

# 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](../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`.
