Architecture Decision Records
One file per decision, numbered, immutable once accepted (write a new ADR to supersede). Template:
# ADR-NNNN: Title- Status: Proposed | Accepted | Superseded by ADR-NNNN- Date: YYYY-MM-DD- Related: RFC-…, ADR-…- Sources: primary links the decision was verified against## Context## Decision## Alternatives considered| Alternative | Pros | Cons | Why not |## Consequences (positive / negative / risks)| # | Title |
|---|---|
| 0001 | JSON Schema is the contract source; TS and Rust are generated |
| 0002 | Thin SQL/blob driver interface, batch-only, two backends |
| 0003 | Studio is a Vite React SPA on Kumo; Astro only renders sites |
| 0004 | Stateless MCP server (spec 2026-07-28), layered approval |
| 0005 | Skills follow the Agent Skills spec; Cloudhal data in metadata |
| 0006 | Node-first toolchain and runtime; Bun supported, not primary |
| 0007 | Apache-2.0 open core, DCO, Contributor Covenant 3.0 |
| 0008 | Precompiled schema validators; D1 tested through Miniflare |
| 0009 | Renderer: live in dev, snapshot in builds; starters own routes |
| 0010 | Better Auth for identity, tenants and MCP authorization |
| 0011 | Agent layer: SDK-free policy/audit/skills, MCP wrapper, MRTR approval + token fallback, API keys |
| 0012 | Cloudflare: site + admin Workers, pointer-flip publishing, Workers Cache, signed previews |
| 0013 | npm packaging: sources in the repo, dist in tarballs; CLI ships Studio/starters/skills; create-cloudhal |
| 0014 | Approvals in Studio: single use, bound to the call and the content state; no agent self-approval |
| 0015 | Agent modes: plan, manual, edits, auto (low-risk publishes), bypass (local only) |
| 0016 | Time-boxed elevation: a more autonomous mode until a set time, computed per call, audited |
| 0017 | Taxonomy and redirects are graph nodes; terms referenced by id; governed rename/merge with automatic 301s (RFC-0002) |
| 0018 | Staff roles are scope sets; agent keys never exceed their creator; approvals follow roles; invites are one-time links (RFC-0005) |
| 0019 | Menus are Menu nodes with a tree of id-referenced items; agent edits always approved (RFC-0003) |
| 0020 | Assets are content-addressed nodes with SHA-256; SSRF-safe agent fetch; asset:upload / asset:manage (RFC-0004) |
| 0021 | Site analytics: first-party cookie-less script, Workers Analytics Engine via bindings (no token), D1 rollups, analytics:read (RFC-0006) |
| 0022 | Reader comments: guest comments held for moderation, D1 outside the graph, first-party proof of work, agents label and hide while people approve (RFC-0007) |
| 0023 | Schedules: content releases pinned to exact versions, render-time time gates, one every-minute tick (cron or DO alarm), deterministic jobs plus MCP agent tasks (RFC-0008) |
| 0024 | Snapshot bundles: the site Worker loads a published snapshot from 256-node shard objects, written at publish and read-through; per-node blobs stay the source of truth (RFC-0010) |
| 0025 | Email delivery: one MailSender on the Cloudflare Email Service binding, locked to one sender; emailed invites and self-service password reset (RFC-0011) |
| 0026 | HTML bodies are sanitized at render time: parse5 (spec-compliant, Worker-safe), a fixed allowlist, our own serializer; stored content unchanged (RFC-0012) |
| 0027 | cloudhal.json observability (log sampling, opt-in traces) and placement: "smart" for the admin Worker; query strings always redacted from logs and traces (RFC-0013) |
| 0028 | Bodies stay markdown under profile v1 with a store-maintained block index (profile, blocks); agents edit by block with get_body/edit_body (apply, rebase, propose); diffs and proposals by block (RFC-0014) |
| 0029 | In-editor AI on Cloudflare Workers AI (binding on Workers, REST from Node, optional AI Gateway); the model is chosen per site; ai:invoke person-only, daily cap, audited without text (RFC-0016) |
| 0030 | One block registry for page sections (component kind) and bodies (markdown directives); props in a JSON Schema subset checked by an eval-free interpreter that also drives Studio forms; global English type names (RFC-0015) |
| 0031 | A generator that writes data only: theme settings in Site.theme.config (declared by the theme, props subset, contrast rules), plans applied as drafts or proposed, Studio panels as validated Kumo catalog trees with allowlisted components, scoped read-only data and actions through the policy engine (RFC-0017) |
| 0032 | Studio messages are an ICU MessageFormat 1 subset (plural, select, selectordinal, number), compiled to data at build time by @cloudhal/messages (no parser shipped, no eval); parameter types generated from English (RFC-0032) |
| 0033 | Right-to-left Studio — logical properties (codemod + design-lint rule), DirectionProvider and <html dir> from the locale registry, a DirectionalIcon block, a generated Kumo patch until upstream ships logical properties |
| 0034 | Translation workflow — the repo stays the source of truth, nested ICU JSON export/import with the checks on every import, Weblate self-hosted (Tolgee as the alternative), AI drafts by an agent skill uploaded as needing review |
| 0035 | Published content in several languages: one node per locale in translation groups (source rev/hash, method, provenance, review), structure keeps per-locale maps, locale in the URL with generated hreflang, linked landing-page layouts, agents draft behind glossary/checks/score while people review and unreviewed machine text is labelled, Site = publication (RFC-0033) |
| 0036 | Multi-site installations: one Studio, D1 and R2 manage many isolated sites (per-site roles, export and delete), created empty or as editions, served by hostname through site_hosts with per-Worker allow-lists, one theme per site Worker and several site Workers per installation (RFC-0034) |
| 0037 | Domain strategy: cloudhal.com is marketing, a future hosted control plane and the playground; cloudhal.dev is docs, schemas, registry and the open-source home; customer sites never run on either (they deploy into the customer’s own Cloudflare account), so no third domain or PSL entry |
| 0038 | Public playground: one SQLite Durable Object per visitor holding store, API and renderer; @cloudhal/storage/do-sqlite and SQL/layered/fetcher blob stores; a deny-list guard in the Worker and the API with a route-walk test and a fixed Editor principal; 60-minute TTL with a 2-hour cap; private, uncached session pages (RFC-0036) |
| 0039 | cloudhal.dev: Starlight in apps/docs over the repo’s docs/, generated reference, a link contract checked in CI (301s, permanent /schemas/v1, /e/<code> pages from one error-code registry), docs for agents (.md per page, Accept: text/markdown, llms.txt, a standalone read-only docs MCP at cloudhal.dev/mcp, none in cloudhal mcp (amended)); all ADRs and RFCs published (RFC-0037) |