C4 Model PoC — Cloudflare Coding Platform
Status: Proof of concept (AURA-807) · Updated: 2026-07-24
This page is the proof of concept for AURA-807: adopt a single C4 model authored in Structurizr DSL as the source of truth for architecture diagrams, and render it into the docs site through a PlantUML export. Rather than start from our own architecture, the PoC reproduces a known, non-trivial reference — Cloudflare's Enterprise AI coding-platform reference architecture — so that the workflow is exercised end to end before we migrate any real diagram.
The point being tested is one model, many views: a single workspace.dsl defines the model once,
and the four diagrams below are generated from it. Changing the model and re-running make diagrams
regenerates every embedded SVG, so the views cannot drift from each other the way hand-drawn
diagrams do.
The pipeline
workspace.dsl ──(Structurizr CLI)──> <view>.puml ──(PlantUML)──> <view>.svgThe Structurizr CLI does not rasterise images itself — it exports each view as a diagram
definition, which is then rendered to a committed SVG. We render with PlantUML rather than
Mermaid: Structurizr's Mermaid export encodes every node label as an SVG <foreignObject>,
which does not render when the SVG is referenced through <img> (the way the docs site embeds
diagrams), and it cannot carry our house C4 style. PlantUML emits native <text>, honours the
workspace's element styles, and — with a small house theme applied at render time — matches the
c4-diagram skill palette and IBM Plex Mono, so these diagrams look like
pi-runtime-c4.svg. Both the model source and every generated
.puml/.svg live under images/cloudflare-coding-platform-poc/,
co-located with this page. Regenerate with make diagrams from the repo root.
Editable model source: images/cloudflare-coding-platform-poc/workspace.dsl — one workspace, four views.
1. Core architecture (system context)
The platform in its enterprise environment: employees describe applications and an approver promotes them; the platform reaches an identity provider, external LLM providers (only through the AI Gateway), and third-party APIs (only through governed egress).
2. Development plane
Authoring and sandboxed execution. Cloudflare Access brokers SSO before any surface is reached; the web builder and local IDE run generated code in the sandbox runtime; the AI Gateway governs every model call; a dev egress worker injects credentials at the platform edge so generated code never sees secrets; the metadata store holds app definitions.
3. Deployment pipeline (dynamic view)
The promotion path between planes: an employee submits an app, the pipeline validates the definition and runs policy checks, the approver approves, and the pipeline promotes the approved app to production. This is a C4 dynamic view — the numbered steps are a second render target distinct from the boundary-heavy container views.
4. Production plane
Published apps run on Workers for Platforms with an outbound worker owning all egress, per-app isolated D1/KV/R2 storage, trace-event observability, and an application registry for discoverability.
Findings
The workflow holds end to end. One workspace.dsl produced all four views; the Structurizr →
PlantUML chain rendered each to an SVG that reads correctly on both the light and dark site themes —
the house C4 palette (dark boxes, light text) means contrast does not depend on the page background —
using native <text> that renders through <img>.
Two things settled the render-target choice:
- Mermaid was not viable. Structurizr's Mermaid export encodes every label as an SVG
<foreignObject>, which does not render when the SVG is embedded via<img>— the diagrams would show boxes with no text — and it could not carry the house style; its nested-boundary titles also collided with nodes. - PlantUML handles the hard cases. The nested-boundary container views and the dynamic view all render cleanly, with boundary labels placed correctly.
Cost of the choice: rendering needs Java + PlantUML (no mermaid-cli, no headless Chrome). The committed SVGs are what deploy, so the docs build itself never runs the renderer.