Linting C4 architecture diagrams
Enforce the “declare your level” convention from Simon Brown’s C4 model across every diagram doc in a ctxgrd-enabled repository.
What ctxgrd checks – and what it does not
ctxgrd checks the markdown envelope: that a diagram doc has a non-empty
title and a c4.level drawn from the configured allowlist. It does not parse
the embedded diagram graph, validate Mermaid/Graphviz syntax, or evaluate
whether the diagram’s content matches its declared level. Diagram quality – the
right zoom, labelled relationships, a title that names the system – is the
author’s job, not a structural rule.
Prerequisites
ctxgrd0.48.0 or later.- A
ctxgrd.tomlin the repository root. If none exists, runctxgrd pack add project-docsto create a baseline.
1. Add the pack
ctxgrd pack add c4This writes a [C4] block into ctxgrd.toml, path-claiming docs/diagrams/**.
To preview what would be written without changing anything:
ctxgrd pack add c4 --dry-run2. Author a diagram doc
Create a markdown file under docs/diagrams/. The filename is the diagram’s
slug – there is no id: field. Every diagram doc requires two frontmatter
fields: a title and a c4 object containing a level.
Here is a minimal System Context diagram, the natural first view to draw in any C4 model:
---
title: System Context -- Payment Service
c4:
level: context
---
# System Context -- Payment Service
A customer submits a payment through the web app. The Payment Service processes
it via the external payment gateway and records the result.
` ``mermaid
C4Context
Person(customer, "Customer", "Places an order")
System(payment, "Payment Service", "Processes card payments")
System_Ext(gateway, "Payment Gateway", "Stripe")
Rel(customer, payment, "Submits payment", "HTTPS")
Rel(payment, gateway, "Charges card", "HTTPS / REST")
` ``Run the linter to confirm the doc is clean:
ctxgrdA missing title or absent c4.level produces a c4.frontmatter diagnostic
pointing to the offending file. A level outside the allowlist produces the same
code with a message naming the invalid value and the accepted set.
3. Understand the levels
The pack ships seven levels. The four core model levels map to zoom steps; the three supplementary views address specific scenarios.
| Level | C4 zoom | When to use |
|---|---|---|
context | The system as one box | Audience: non-technical stakeholders. Shows the system, its users, and direct upstreams / downstreams. |
container | Deployable / runnable units | Audience: developers and architects. Shows applications, services, databases, and their communication. |
component | One container’s internals | Audience: developers of that container. Shows components, their responsibilities, and interactions. |
code | Classes / functions | Audience: developers of that component. Usually auto-generated; most teams skip it. |
deployment | Infrastructure mapping | Shows how containers map to infrastructure nodes (cloud regions, VMs, containers). |
dynamic | Runtime sequence | Shows a specific scenario’s message flow; acts like a sequence diagram at any zoom level. |
landscape | Multiple systems | Shows a system landscape where more than one system is the focus – broader than a single Context diagram. |
4. Narrow the allowlist
Most teams skip the code level and may not need landscape. Edit
ctxgrd.toml to restrict the accepted values:
[C4."c4.frontmatter"]
levels = ["context", "container", "component", "deployment", "dynamic"]A c4.level value not in the list is caught by c4.frontmatter with a
diagnostic naming the invalid level and the accepted set. Remove the levels
key entirely to accept any non-empty string without vocabulary checking.
5. Lint and iterate
ctxgrd # human-readable output
ctxgrd --format json | jq '.[] | select(.code | startswith("c4"))'Exit codes follow the standard contract:
0– clean.1– diagnostics reported.2– kernel or config error.
Diagram source format
Diagram content lives as a fenced code block inside the same .md file –
Mermaid (```mermaid), Graphviz (```dot), or any renderer your toolchain
supports. ctxgrd does not restrict which fenced language you use; it reads only
the markdown envelope.
Raw .mmd files (standalone Mermaid source) are not supported by the core
walker – ctxgrd is strictly limited to markdown. If you need to lint standalone
.mmd files, use an external source script (see docs/sources.md) that emits
document envelopes.
Next steps
- Rule packs – the full built-in pack catalog.
- Configuring namespaces – path claims and allowed-values in depth.
- Writing external sources – extending ctxgrd to non-markdown file formats.