C4

Who talks to what, and over which protocol

People, systems, containers, components, databases and queues, inside the boundaries that actually separate them. One diagram per C4 level, and a box can open the diagram one level down, so the whole system reads from the outside in.

> draw the containers and what they call
wrote system.c4.json · system.c4.html
 
# the spec is written by the skill, then rendered like any other
$ vibex validate docs/system.c4.json
$ vibex render docs/system.c4.json --open

Three levels of one system

Relay is drawn at three levels, and each links to the next. Open Relay on the context diagram and you land on its containers. Open Dispatch there and you land on its components.

relay-context.c4.html · who Relay serves and what it depends onFull screen ↗

On the container diagram, the merchant portal and the shipment API link sideways to their endpoint catalogues too. A link can point at any rendered diagram, not only the next C4 level.

What it draws

Every field in the spec, and where it shows up.

ELEMENTS

kind
Each kind has its own shape and fill: a figure for a person, a cylinder for a database.
personsystemcontainercomponentdatabasequeue
label · technology
The name, and a stereotype line under it, for example [Container: NestJS].
description
One sentence on what it does, wrapped inside the box. Boxes in a row grow to fit the longest description, up to five lines. If text still gets cut, the render says which element, and the full text stays in the tooltip and the details panel.
external
Drawn grey and marked external, for things you depend on but do not own: Stripe, an SMS gateway, a partner's API.
link
A relative path or http(s) URL to another diagram. The box gets a link to open it. Any other URL scheme is rejected.
sources[]
File and line. With meta.repository set, the element links to the code.

BOUNDARIES

kind
A tinted frame around what it contains.
enterprisesystemcontainerdeployment
contains[]
Element IDs, or other boundary IDs, which is how boundaries nest. Everything has at most one parent, and a loop in the nesting is an error.

RELATIONSHIPS

label
What flows, as a verb phrase: “Reads from”, “Sends emails via”. Long labels wrap to two lines instead of being cut off.
technology
The protocol, in brackets under the label: [HTTPS/JSON], [gRPC], [AMQP], [SQL].
style
solid for a synchronous call, dashed for anything asynchronous.
direction
forward, or both for an arrowhead at each end.

LEVEL AND LAYOUT

meta.level · meta.scope
Which C4 level this diagram is, and which system or container it zooms into.
landscapecontextcontainercomponent
layout.direction
Elements are ranked by the direction of their relationships, top to bottom or left to right. The default is left to right when there are more than four ranks. Lines that share a gap each get their own lane, so labels do not pile up in one column.
row · col
Pin an element to a cell. No two elements can share one.
cards[]
Up to four short notes under the diagram.

Where it reads from

There is no importer for C4, because no single file describes a deployment. The skill reads the places that do and writes the spec:

CONTAINERS AND DATASTORES

docker-compose.yml, k8s/, serverless.yml, infra/.

TECHNOLOGY

package.json and module files, such as *.module.ts.

EXTERNAL SYSTEMS AND CALLS

HTTP clients, queue clients and SDK imports: stripe, @aws-sdk/client-sqs, nodemailer.

PEOPLE

The roles in your auth code.

Check this one before you trust it. Every C4 spec is the agent's reading of your code. validate proves the spec holds together, not that the arrows are right. Read it once.

The spec you keep

{
  "schema_version": 1,
  "diagram_type": "c4",
  "meta": { "title": "Shop", "level": "container" },
  "elements": [
    { "id": "buyer", "kind": "person", "label": "Buyer" },
    { "id": "api", "kind": "container", "label": "API",
      "technology": "NestJS",
      "description": "Takes orders and payments",
      "link": "api.endpoints.html" },
    { "id": "db", "kind": "database", "label": "Orders DB",
      "technology": "PostgreSQL 15",
      "description": "Orders, lines and payments" },
    { "id": "stripe", "kind": "system", "label": "Stripe",
      "description": "Charges cards", "external": true }
  ],
  "boundaries": [
    { "id": "shop", "label": "Shop", "contains": ["api", "db"] }
  ],
  "relationships": [
    { "from": "buyer", "to": "api", "label": "Places orders",
      "technology": "HTTPS/JSON" },
    { "from": "api", "to": "db", "label": "Reads and writes",
      "technology": "SQL" },
    { "from": "api", "to": "stripe", "label": "Charges via",
      "technology": "HTTPS" }
  ]
}

The API box links to its endpoint catalogue. Clicking it opens api.endpoints.html, found relative to this diagram.

Aim for about fifteen elements in one diagram. When it gets bigger, draw the next level down and link to it.

The full contract is schemas/c4.schema.json.

What validate stops

$ vibex validate docs/shop.c4.json
error dangling-ref boundaries[0] contains unknown id "db" (known ids: web, api)
warning description elements[0] (Web app) has no description; C4 boxes should say what the thing does
warning label relationships[0] (web -> api) has no label; C4 arrows should say what flows
1 error(s), 2 warning(s)

Errors

Exit 1. Each one names the field.

  • dangling-refa relationship or boundary names an ID that does not exist
  • id-clasha boundary uses the same ID as an element
  • self-containa boundary lists itself
  • multi-parentone element inside two boundaries
  • cycleboundaries nested in a loop

Warnings

Printed, never blocking.

  • descriptiona box that does not say what it does. People are exempt
  • labelan arrow that does not say what flows
  • sizemore than 20 elements. Draw another level

The sentences it writes

A docs section can ask for c4.elements, c4.relationships and c4.boundaries. From the Relay build, word for word:

Merchant is a person. Books shipments and pays for them.c4.elements · relay-context.c4#merchant
The Core services deployment boundary contains Shipment API, Scan ingest, Dispatch, Routing engine, Notifier, Relay DB and Parcel events.c4.boundaries · relay.c4

Relationship sentences keep your label whole instead of rewording it into a clause it was not written for.

What it connects to

ENDPOINTS

From a container to its API

Point a container's link at its endpoint catalogue and the reader goes from the box to its routes in one click.

Endpoints →
DOCS

Claims about a box

A claim whose subject is relay.c4#dispatch carries a chip that opens this diagram with Dispatch selected.

Docs →
DASHBOARD

All three levels in one file

The dashboard overview lists every drill-down link that points at a diagram inside it, and each one opens that panel.

Dashboard →