c4drill

module
v1.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2026 License: MIT

README

= C4Drill image:https://pkg.go.dev/badge/github.com/Djarvur/c4drill.svg["GoDoc",link="https://pkg.go.dev/github.com/Djarvur/c4drill"] image:https://github.com/Djarvur/c4drill/actions/workflows/pr-sanity.yml/badge.svg["Build Status",link="https://github.com/Djarvur/c4drill/actions/workflows/pr-sanity.yml"] image:https://coveralls.io/repos/github/Djarvur/c4drill/badge.svg?branch=master&service=github["Coverage Status",link="https://coveralls.io/github/Djarvur/c4drill?branch=master"]
:toc:
:toc-title: Contents
:icons: font

Transform simple TOML architecture descriptions into professional,
interactive C4 diagrams without manual drawing.

*One TOML file → Clickable C1, C2, and C3 diagrams with automatic
drill-down navigation.*

== What It Is and What Problem It Solves

C4Drill is *diagram-as-code for C4*: describe your architecture once in
a TOML file, and get a hierarchy of nested, cross-linked SVG diagrams
that let you zoom from context down to components — no runtime, no GUI,
no manual drawing.

image::examples/cloud-system/cloud-system.svg[Cloud System — C1 diagram,width=400]

One model produces the C1 context diagram above, the C2 container
diagrams, the C3 component diagrams, and every drill-down link between
them (see the Examples section). Open any generated SVG to click
through.

=== The Problem

Real systems do not fit one sheet. A large architecture is dozens to
hundreds of elements; drawing it on a single diagram produces an
unreadable wall of boxes, and hand-maintaining a hierarchy of linked
diagrams (C1 → C2 → C3) is a lot of manual work that drifts out of sync
with the code.

C4Drill makes the hierarchy automatic: *the model is the diagram*. Edit
the TOML, re-run the tool, and every level plus all navigation links is
regenerated consistently.

=== Why Not an Existing Tool?

[cols="1,2,2",options="header",]
|===
|Tool |Good at |But
|PlantUML (+ C4 module) |Rich syntax, Markdown integration |No enforced C4 levels; every level is a separate hand-written diagram — the drill-down hierarchy is not generated
|Mermaid |Diagrams straight from Markdown (GitHub, Obsidian) |Flat pictures: no C4 model, no levels, no zoom-in navigation
|Structurizr DSL |Purpose-built C4 modeling language |More general than the task — a modeling platform with workspaces; C4Drill is deliberately minimal and strict
|LikeC4 |The closest: model + nested views with drill-down |Requires a runtime/workspace; C4Drill emits static files you can commit and open anywhere
|===

C4Drill is for the *static-artifact* case: diagrams that live in the
repository, are reviewed in pull requests, and render everywhere.

=== Why TOML?

There is deliberately no new DSL to learn. TOML is a configuration
format most engineers already read, and the schema *is* the C4 model:
units, types, levels, links. `type = "container"` and
`name = "API Service"` read like prose. A bespoke DSL (or HCL) would add
syntax to learn without adding modeling power.

=== Is This for You?

* *Yes*, if you have a system large enough that one diagram is
  unreadable, and you want the level hierarchy generated and kept in
  sync as versionable static files.
* *Probably not*, if you only need quick throwaway diagrams inside
  Markdown — Mermaid or PlantUML will serve you better.

== Install

[source,bash]
----
go install github.com/Djarvur/c4drill/cmd/c4drill@latest
----

Or build from source:

[source,bash]
----
git clone https://github.com/Djarvur/c4drill
cd c4drill
go build -o c4drill ./cmd/c4drill
----

== Quick Start

[source,bash]
----
# Generate SVG diagrams (default)
c4drill architecture.toml

# Generate to specific directory
c4drill architecture.toml -o ./docs/diagrams

# Generate DOT format for customization
c4drill architecture.toml -f dot -o ./output
----

== Examples

The https://github.com/likec4/likec4/tree/main/examples[likec4 examples]
are popular real-world C4 models. The directories below are ports of a
curated subset into C4Drill TOML — the generated diagrams are committed
next to each source file, so what you see in this README is exactly what
`c4drill` produces. Every port documents its adaptations in comments:
features likec4 has and C4Drill does not (dynamic and deployment views,
tags, metadata, view-local styles) are either mapped to the closest
C4Drill equivalent or dropped with a note.

=== Cloud System (likec4 `cloud-system`)

The flagship example: a SaaS platform with two external parties and
multi-level drill-down. Ported from
https://github.com/likec4/likec4/tree/main/examples/cloud-system[likec4
`cloud-system`] (a copy of
https://github.com/likec4/example-cloud-system[`likec4/example-cloud-system`]).

image::examples/cloud-system/cloud-system.svg[Cloud System — C1 diagram,width=720]

The C1 diagram already shows one nesting level; clicking the nodes
drills down further:

* link:examples/cloud-system/cloud-system/cloud.svg[Cloud System — C2 containers]
* link:examples/cloud-system/cloud-system/amazon.svg[Amazon — C2 containers]
* link:examples/cloud-system/cloud-system/amazon/rds.svg[RDS — C3 (PostgreSQL)]
* link:examples/cloud-system/cloud-system/amazon/sqs.svg[SQS — C3 (queues)]
* link:examples/cloud-system/cloud-system/amazon/lambdas.svg[Lambdas — C3 (enrichment)]

==== What was adapted

* `supportUser` is nested inside `cloud` in likec4; C4Drill C1 types are
  top-level only, so the actor is hoisted to the root level.
* Amazon is an `externalSystem` in likec4; C4Drill's `systemExternal`
  cannot contain subunits, so it is modeled as a regular `system`.
* `customer -> cloud "uses and pays"` targets the system itself; C4Drill
  cannot link to a unit that has subunits (validation rule 3 below), so
  the edge targets `cloud.ui` — the same endpoint likec4's own customer
  view uses.
* likec4 view-local edges (`cloud.* -> amazon.*`, `cloud.next.* ->
  sqs.*`, ...) are modeled as regular links; each carries a comment
  citing the source view.
* The `tblUsers` table nested under PostgreSQL is dropped — C4Drill
  components cannot have subunits.
* Dynamic views, deployment views, groups, tags and view styles have no
  C4Drill equivalent and are dropped.
* `legacy`'s link to the update-diagrams workflow file maps to the
  `reference` field (the 📖 marker).

Regenerate:

[source,bash]
----
c4drill examples/cloud-system/cloud-system.toml
c4drill examples/cloud-system/cloud-system.toml --expanded
----

==== All-Expanded Diagram

`--expanded` renders every unit on a single diagram — no drill-down
navigation, useful for a one-shot review of the whole architecture:

image::examples/cloud-system/cloud-system.expanded.svg[Cloud System — all-expanded diagram,width=720]

=== Vehicle Platform (likec4 `overflow-test`)

A stress test: very long display names and channel-technology labels
ported verbatim from
https://github.com/likec4/likec4/tree/main/examples/overflow-test[likec4
`overflow-test`] to demonstrate label wrapping. C4Drill sizes labels
from their content — tune the shape with `--label-ratio` (default 1.6,
credit-card proportions) or the `C4DRILL_LABEL_RATIO` environment
variable.

image::examples/overflow-test/overflow-test.svg[Vehicle Platform — C1 diagram,width=720]

The longest labels live in the sensor processing pipeline (C3):

image::examples/overflow-test/overflow-test/vehiclePlatform/sensorProcessing.svg[Sensor processing pipeline — C3 diagram,width=720]

Parallel edges between the same pair of units collapse to a single
thicker edge in resolved views (first-wins dedup, D-01); the
`--expanded` diagram shows every parallel link with its own label.

* likec4 custom kinds (`service`, `component`) map onto C4Drill
  `container` / `component`.
* likec4 tags (`#sensor`, `#perception`, ...) have no C4Drill equivalent
  and are dropped.

Regenerate:

[source,bash]
----
c4drill examples/overflow-test/overflow-test.toml
----

=== Effect of Rank (likec4 `rank-for-better-layout`)

A minimal graph from
https://github.com/likec4/likec4/tree/main/examples/rank-for-better-layout[likec4
`rank-for-better-layout`] showing rank hints. likec4's view-level
`rank same/source/max` directives have no direct C4Drill equivalent; the
closest tool is the per-link `rank` attribute — `rank = "equal"` puts
the link's endpoints on the same rank, `rank = "forward"` /
`"reverse"` order them along the layout direction.

image::examples/rank-for-better-layout/rank-for-better-layout.svg[Effect of Rank — C1 diagram,width=520]

* likec4's custom kind `block` maps onto C4Drill `system`.
* likec4's `D` has no relations; C4Drill requires every leaf unit to
  have at least one link (orphan rule), so `D` gains a link to `C` —
  the other member of its `rank source` group.

Regenerate:

[source,bash]
----
c4drill examples/rank-for-better-layout/rank-for-better-layout.toml
----

=== Not ported

The remaining likec4 examples build on features C4Drill does not have,
so ports would be more confusing than instructive:

[cols="1,2a",options="header"]
|===
|likec4 example |Why it was not ported
|`metadata-views` |View filtering on metadata (`where metadata.environment is "production"`) has no equivalent.
|`issue-1624` |A bug reproduction (custom kinds, rich descriptions) rather than a showcase.
|`multi-relation-extend` |Built on likec4's `extend` semantics (merging metadata/tags/links onto existing relations) — C4Drill's `[[include]]` merges units, not relation metadata.
|`multi-metadata-extend` |Same `extend` semantics as above.
|`multi-project` |Multiple independent projects in one workspace; C4Drill models one architecture per file.
|===

== TOML Format

=== Minimal Example

[source,toml]
----
[properties]
name = "My System"

[user]
type = "person"
name = "User"

[webapp]
type = "system"
name = "Web Application"

# Every unit needs at least one link (orphan rule)
[[webapp.link]]
peer = "user"
description = "Uses"
----

=== Properties Section

The `++[++properties++]++` section defines global settings for your
architecture:

[source,toml]
----
[properties]
name = "E-Commerce Platform"        # Required: Architecture name
description = "Online store system" # Optional: Description
color = "#E3F2FD"                   # Optional: Default background color
style = "filled"                    # Optional: Default visual style
border = "#1565C0"                  # Optional: Default border color
edges = "spline"                    # Optional: Edge routing (straight|spline|square)
lineLength = 40                     # Optional: Max line length before wrap (0=auto)
expanded = ["payments"]             # Optional: Units to expand by default
----

=== Unit Types

Each unit is defined as a TOML section. The section name becomes the
unit's identifier.

*Type is optional* — defaults based on nesting level:

* Root-level units → `system`
* Units inside system/box → `container`
* Units inside container → `component`

==== Person (Actor)

[source,toml]
----
[user]
type = "person"                     # Required
name = "Customer"                   # Optional: defaults to humanized identifier
description = "Online shopper"      # Optional
----

==== External Person (External Actor)

An actor outside the organization (partner, regulator, third-party
user). Same fields as `person`:

[source,toml]
----
[auditor]
type = "personExternal"
name = "Compliance Auditor"
description = "External regulatory auditor"
----

==== System

[source,toml]
----
[webapp]
type = "system"
name = "Web Application"
description = "Frontend web app"
technology = "React, TypeScript"    # Optional: Shown in diagram
----

==== External System

[source,toml]
----
[stripe]
type = "systemExternal"
name = "Stripe"
description = "Payment processor"
----

==== Database

[source,toml]
----
[postgres]
type = "db"
name = "PostgreSQL"
description = "Primary database"
technology = "PostgreSQL 15"
----

==== External Database

[source,toml]
----
[analytics]
type = "dbExternal"
name = "Analytics DB"
description = "Third-party analytics"
----

==== Queue

[source,toml]
----
[rabbitmq]
type = "queue"
name = "Message Queue"
description = "Async job processing"
technology = "RabbitMQ"
----

==== External Queue

[source,toml]
----
[sqs]
type = "queueExternal"
name = "AWS SQS"
description = "External message queue"
----

==== Box (Grouping Container)

[source,toml]
----
[cloud]
type = "box"
name = "AWS Cloud"
description = "Cloud infrastructure"
----

==== Reference (External Documentation URL)

Any unit accepts an optional `reference` field — an external
documentation URL. When set, a 📖 marker appears next to the unit name
and the node becomes clickable: clicking it opens the URL (via
GraphViz's native `URL` attribute in SVG). In `-f html` output, external
`http(s)` references open in a new tab, distinct from internal
drill-down navigation.

[source,toml]
----
[api]
type = "system"
name = "API Service"
reference = "https://wiki.example.com/api-runbook"   # Optional: 📖 marker, clickable
----

An empty string and an omitted field are equivalent (no 📖, not
clickable).

=== Optional Name (Humanization)

The `name` field is *optional*. When omitted, the display name is
derived from the *last segment* of the unit's identifier via a dumb
camelCase split:

[source,toml]
----
# Explicit name (always wins — use this for acronyms or custom labels)
[linuxSystem.localIDP]
name = "My Custom Name"

# Name omitted — humanized from the last path segment "sessionManager"
[linuxSystem.sessionManager]
# displays as "Session Manager"
----

*Humanization rules:*

* Splits camelCase boundaries and Title-cases each word.
* Operates on the *last path segment only* —
`++[++linuxSystem.localIDP++]++` becomes "Local IDP", not "Linux System
Local IDP".
* Examples: `sessionManager` → "Session Manager", `localIDP` → "Local
IDP", `linuxSystem` → "Linux System".

*Acronyms:* acronym preservation is intentionally *not* supported (the
split is deliberately dumb). `gRPC` humanizes to "Grpc". To preserve an
acronym or set any custom label, set `name =` explicitly — an explicit
`name =` always wins.

*Backward compatibility:* existing models that already set `name =` on
every unit are completely unaffected — humanization only fires when
`name` is omitted.

=== Optional Type (Inference)

The `type` field is *optional*. When omitted, the type is inferred from
the parent unit's type (and, for the generic `db`/`queue` types,
promoted to the level-specific variant based on nesting). Two rules
apply:

*1. Default type by parent* — the type assigned when `type` is omitted
entirely:

[cols=",,",options="header",]
|===
|Parent type |Inferred child type |Level
|(none — root) |`system` |C1
|`system` |`container` |C2
|`box` |`system` |C1 (same-level grouping)
|`container` |`component` |C3
|`containerBox` |`container` |C2 (same-level grouping)
|`componentBox` |`component` |C3 (same-level grouping)
|(other: db, queue, etc.) |`system` |C1 fallback
|===

*2. Generic `db`/`queue` promotion* — when `type = "db"` or
`type = "queue"` is set explicitly, the type is promoted to the
level-specific variant based on the parent:

[cols=",,,",options="header",]
|===
|Parent type |`db` becomes |`queue` becomes |Level
|(none) or `box` |`db` |`queue` |C1 (unchanged)
|`system` or `containerBox` |`containerDb` |`containerQueue` |C2
|`container` or `componentBox` |`componentDb` |`componentQueue` |C3
|===

*Before/after example:*

[source,toml]
----
# BEFORE — explicit types (verbose)
[platform]
type = "system"
[platform.webapp]
type = "container"
[platform.webapp.cache]
type = "componentDb"     # generic db promoted because parent is container

# AFTER — type omitted, inferred (identical result)
[platform]
# type omitted → inferred "system" (no parent)
[platform.webapp]
# type omitted → inferred "container" (parent is system)
[platform.webapp.cache]
type = "db"
# explicit generic db → promoted to "componentDb" (parent is container)
----

An explicit non-generic `type =` always wins (no inference runs).
Source: `defaultTypeForParent` and `inferGenericType` in
`internal/parser/parser.go`.

=== Nesting (C2/C3 Diagrams)

Systems and boxes can contain subunits using dotted notation:

[source,toml]
----
[mainapp]                    # C1 level
type = "system"
name = "Main Application"

[mainapp.api]                # C2 level (container)
type = "container"
name = "API Service"

[mainapp.webapp]             # C2 level (container)
type = "container"
name = "Web App"

[mainapp.api.handlers]       # C3 level (component)
type = "component"
name = "HTTP Handlers"

[mainapp.api.services]       # C3 level (component)
type = "component"
name = "Business Services"
----

=== Links (Relationships)

Define relationships between units using `[[link]]` (outgoing) or
`[[linkFrom]]` (incoming). Define each relationship exactly once — the
two forms are just different places to write the same edge:

[source,toml]
----
[user]
type = "person"
name = "User"

[webapp]
type = "system"
name = "Web Application"

# Outgoing link: User → Webapp (defined on the source)
[[webapp.link]]
peer = "user"
technology = "HTTPS"
description = "Browses"

[api]
type = "system"
name = "API Service"

# Incoming link: Webapp → API (defined on the target)
[[api.linkFrom]]
peer = "webapp"
technology = "REST/JSON"
description = "Calls"
----

==== Link Attributes

[cols=",,",options="header",]
|===
|Attribute |Description |Example
|`peer` |Target unit identifier (required) |`"user"`, `"platform.api"`
|`technology` |Protocol/technology label |`"HTTPS"`, `"gRPC"`, `"TCP"`
|`description` |Relationship description |`"Sends events to"`
|`arrow` |Arrow direction |`"forward"`, `"reverse"`, `"bidirectional"`, `"none"`
|`rank` |Layout ranking hint |`"forward"`, `"reverse"`, `"equal"`
|`color` |Edge color |`"blue"`, `"++#++FF5733"`
|`style` |Line style |`"solid"`, `"dashed"`, `"dotted"`
|`labelPosition` |Where the label appears |`"middle"`, `"tail"`, `"head"`
|===

==== Multiple Links

[source,toml]
----
[api]
type = "system"
name = "API"

[[api.link]]
peer = "user"
technology = "HTTPS"
description = "Authenticates"

[[api.link]]
peer = "webapp"
technology = "REST"
description = "Serves data"
----

*Parallel edges:* when several links connect the same pair of units,
resolved views collapse them into a single thicker edge (the first one
wins); the `--expanded` diagram shows every parallel link separately.

=== Expanded Units (Drill-Down)

Mark units as "expanded" to generate C2/C3 diagrams for them:

[source,toml]
----
[properties]
name = "My System"
expanded = ["mainapp"]        # Generate C2 for mainapp

[mainapp]
type = "system"
name = "Main Application"
expanded = ["mainapp.api"]    # Generate C3 for mainapp.api

[mainapp.api]
type = "container"
name = "API Service"

[mainapp.api.handlers]
type = "component"
name = "Handlers"

[mainapp.api.services]
type = "component"
name = "Services"

[[mainapp.api.handlers.link]]
peer = "mainapp.api.services"
description = "Calls"
----

*Output structure:*

[source,text]
----
output/
├── architecture.svg           # C1 diagram
├── architecture/              # C2 diagrams directory
│   └── mainapp.svg            # C2 for mainapp
│   └── mainapp/               # C3 diagrams directory
│       └── api.svg            # C3 for mainapp.api
----

=== Styling

Override default colors and styles per unit:

[source,toml]
----
[webapp]
type = "system"
name = "Web Application"
color = "#4A90D9"              # Background color
border = "#2E5A8B"             # Border color
style = "solid"                # Border style (solid|dashed|dotted)
edges = "spline"               # Edge routing for this unit's links
width = 300                    # Explicit label width (0=auto)
height = 200                   # Explicit label height (0=auto)
----

`width` and `height` are rarely needed — labels are normally sized
from their content (see `--label-ratio` in the CLI Reference).

=== Templates

Define a parametrized unit template once and instantiate it N times with
distinct parameter values. A template is a
`++[++template.++<++name++>]++` table declaring its parameters and the
unit shape (including subunits and links); each `++[[++use++]]++`
directive instantiates it with concrete values.

[source,toml]
----
[template.microservice]
params = ["name", "tech", "upstreamBus"]
name = "${name} Service"
type = "container"
technology = "${tech}"
description = "${name} handles its domain"
reference = "https://wiki.example.com/${name}"

[[template.microservice.link]]
peer = "${upstreamBus}"
description = "Publishes ${name} domain events"

[[use]]
template = "microservice"
parent = "platform"
name = "auth"
tech = "Go, gRPC"
upstreamBus = "messageBus"
----

*Rules:*

* All declared params are *required* on every `++[[++use++]]++` (no
defaults); a missing param is a hard error.
* `$++{++param}` substitutes into every string field — name,
description, technology, reference, color, and link fields (peer,
description, technology).
* The link set is *fixed*: a template with one
`++[[++template.X.link++]]++` produces exactly one link per
instantiation (no fan-out / `for++_++each`).
* Subunit subtrees are supported (declare `++[++template.X.child++]++`);
the subunit key is verbatim, only field values are substituted.
* Duplicate unit paths across instantiations are a hard error.

See `skill/examples/06-templates.toml` for a runnable example.

=== Multi-File Composition (Include)

Assemble a diagram from multiple TOML files. Each `++[[++include++]]++`
directive pulls in another file relative to the including file's
directory and merges its units into the model.

[source,toml]
----
# entry.toml
[platform]
type = "system"
name = "Platform"

[[include]]
path = "auth.toml"

[[include]]
path = "templates.toml"
once = true
----

*Rules:*

* Paths are *relative to the including file's directory* (not the CLI
cwd).
* Includes are *transitive* (an included file may itself include
others).
* `once = true` deduplicates by canonical path — a file included again
(even via a different path) is skipped.
* The merge is *flat* (no namespacing): included units append in include
order. Cross-file subunits are supported — an included file may
re-declare a parent declared in the entry and contribute subunits under
it.
* Include cycles are a fatal error; missing files are a hard error.
* Properties follow root-file-wins (the entry's `++[++properties++]++`
takes precedence).

See `skill/examples/08-include/` for a runnable multi-file example.

=== Relative Peer Resolution

A bare `peer` value (no dot) resolves against the enclosing parent's
ancestor scopes — walking up nearest-first until a sibling match is
found. A peer *with a dot* is absolute and used as-is.

[source,toml]
----
[platform.api]
# Bare peer "cache" resolves via walk-up:
[[platform.api.link]]
peer = "cache"                          # sibling: platform.cache (nearest ancestor scope match)

[[platform.api.link]]
peer = "platform.cache"                 # absolute: has a dot, used as-is
----

*The four resolution cases:*

* *Sibling match* — the nearest ancestor scope (the immediate parent)
has a child with that name.
* *Aunt/grandparent match* — walk up past the parent; a grandparent's
child matches.
* *Root match* — walk all the way to the top-level scope.
* *Absolute fallback* — a peer containing a dot is never walked-up; it
is used verbatim.

Multiple matches at the same depth are impossible (sibling keys are
unique per parent). A miss at root is a hard error naming the peer and
the host unit.

See `skill/examples/07-relative-peer.toml` for a runnable example
demonstrating all four cases.

== Full Example

[source,toml]
----
[properties]
name = "E-Commerce Platform"
description = "Online shopping system"
edges = "spline"
expanded = ["webapp", "webapp.api"]

# External actors
[customer]
type = "person"
name = "Customer"
description = "Online shopper"

[admin]
type = "person"
name = "Admin"
description = "System administrator"

# Main system with containers
[webapp]
type = "system"
name = "Web Application"
description = "Main e-commerce platform"
technology = "Go, React"
expanded = ["webapp.api"]

[webapp.frontend]
type = "container"
name = "Frontend"
description = "React web application"
technology = "React, TypeScript"

[webapp.api]
type = "container"
name = "API Service"
description = "Backend REST API"
technology = "Go"
expanded = ["webapp.api"]

[webapp.api.handlers]
type = "component"
name = "HTTP Handlers"
description = "Request routing"

[webapp.api.services]
type = "component"
name = "Business Logic"
description = "Core services"

[webapp.db]
type = "db"
name = "PostgreSQL"
description = "Primary database"
technology = "PostgreSQL 15"
# generic `db` is promoted to `containerDb` by the nesting level

# External dependencies
[stripe]
type = "systemExternal"
name = "Stripe"
description = "Payment processing"

[redis]
type = "queue"
name = "Redis"
description = "Session cache"
technology = "Redis"

# Relationships (every unit needs at least one — orphan rule)
[[customer.link]]
peer = "webapp.frontend"
technology = "HTTPS"
description = "Shops"

[[admin.link]]
peer = "webapp.frontend"
technology = "HTTPS"
description = "Administers"

[[webapp.frontend.link]]
peer = "webapp.api.handlers"
technology = "REST/JSON"
description = "Calls"

[[webapp.api.handlers.link]]
peer = "webapp.api.services"
description = "Dispatches"

[[webapp.api.services.link]]
peer = "webapp.db"
technology = "SQL"
description = "Persists"

[[stripe.linkFrom]]
peer = "webapp.api.services"
technology = "Stripe API"
description = "Processes payments"

[[redis.linkFrom]]
peer = "webapp.api.services"
technology = "TCP"
description = "Caches sessions"
----

== CLI Reference

[source,text]
----
c4drill <input.toml> [flags]

Flags:
      --expanded            Generate all-expanded diagram showing all units
  -f, --format string       Output format (dot|svg|html) (default "svg")
  -h, --help                help for c4drill
      --label-ratio float   Width:height ratio for unit labels (default: 1.6, credit card proportions)
  -o, --output string       Output directory (default: same as input file)
  -v, --version             version for c4drill
----

* `--expanded` renders a single diagram containing *all* units (every
  level), instead of the per-level drill-down set. The file is named
  `++{basename}++.expanded.++{format}++` and contains no drill-down
  links.
* `--label-ratio` overrides the width:height ratio used to size unit
  labels (default 1.6 — credit-card proportions). Higher values produce
  wider, shorter labels; lower values produce narrower, taller ones.
  The same value can be set via the `C4DRILL_LABEL_RATIO` environment
  variable; the flag takes precedence.

=== Output Format

* *svg* (default): Rendered SVG diagrams with clickable navigation links
* *html*: Self-contained HTML files (SVG inlined) with working
navigation in Safari/WebKit, which silently ignores SVG `++<++a++>++`
hyperlinks. Use `-f html` when diagrams will be opened in Safari or
viewed via `file://`.
* *dot*: Raw GraphViz DOT format for customization

=== Exit Codes

* *0*: Success (silent output)
* *1*: Error (parse failure, validation error, I/O error)

Errors are written to stderr, making the tool suitable for scripting.

== Validation Rules

[arabic]
. *Referenced units must exist* - Links can only reference defined units
. *No links on containers* - Units with subunits cannot have their own
links (`link` or `linkFrom`)
. *No linking to containers* - Cannot link to units that have subunits
. *Subunits only for grouping types* - Only `system`, `box`,
`container`, `containerBox`, and `componentBox` can contain subunits
(leaf types such as `db`, `queue`, and the level-specific variants
cannot)
. *Nesting hierarchy* - Top-level units must be C1 types; inside a
`system` — C2 types (containers); inside a `container` — C3 types
(components); boxes group same-level units only
. *No orphans* - Every unit must have at least one incoming or outgoing
link, or contain subunits

== Architecture

C4Drill implements a compiler-style pipeline:

[source,text]
----
TOML → Parse → Validate → Generate Views → Build Graphs → Render → Write Files
----

* *Parser*: Reads TOML into structured model
* *Validator*: Enforces C4 rules and reference integrity
* *View Generator*: Creates C1/C2/C3 views from model
* *Graph Builder*: Constructs graphviz-compatible structures
* *Renderer*: Outputs DOT, SVG, or HTML via go-graphviz
* *Writer*: Creates output directory hierarchy

== License

MIT

Directories

Path Synopsis
cmd
c4drill command
Package main provides the CLI entry point for c4drill.
Package main provides the CLI entry point for c4drill.
internal
graph
Package graph provides types and functions for constructing graph structures from C4 architecture views.
Package graph provides types and functions for constructing graph structures from C4 architecture views.
include
Package include resolves [include] directives into a single merged *parser.Model.
Package include resolves [include] directives into a single merged *parser.Model.
model
Package model defines the domain types for the C4 architecture model.
Package model defines the domain types for the C4 architecture model.
output
Package output provides file output functionality for rendered diagrams.
Package output provides file output functionality for rendered diagrams.
parser
Package parser provides TOML parsing for C4 architecture definitions.
Package parser provides TOML parsing for C4 architecture definitions.
peer
Package peer rewrites relative (bare) Link.Peer values into absolute dotted paths on an already-parsed model, before validation.
Package peer rewrites relative (bare) Link.Peer values into absolute dotted paths on an already-parsed model, before validation.
render
Package render provides functions to render graph structures to DOT, SVG, and HTML formats.
Package render provides functions to render graph structures to DOT, SVG, and HTML formats.
template
Package template expands [template.<name>] + [[use]] instantiations into concrete, parametrized unit subtrees drained into parser.Model.Units / UnitOrder, producing a model structurally indistinguishable from a hand-authored one.
Package template expands [template.<name>] + [[use]] instantiations into concrete, parametrized unit subtrees drained into parser.Model.Units / UnitOrder, producing a model structurally indistinguishable from a hand-authored one.
testutil/canonical
Package canonical provides an order-insensitive semantic comparator for DOT (Graphviz) output, realizing STATE.md decision DI-1 (the "canonicalDOT" contract) as a reusable helper importable from any _test.go file in the repo.
Package canonical provides an order-insensitive semantic comparator for DOT (Graphviz) output, realizing STATE.md decision DI-1 (the "canonicalDOT" contract) as a reusable helper importable from any _test.go file in the repo.
validator
Package validator provides validation infrastructure for C4 model integrity checks.
Package validator provides validation infrastructure for C4 model integrity checks.
view
Package view provides types and functions for generating scoped C4 architecture views.
Package view provides types and functions for generating scoped C4 architecture views.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL