Just as a mountain trail is marked by cairns, software architecture is understood through a series of views, each revealing a different part of the landscape.
Cairn is an Elkjs (Eclipse Layout Kernel) based diagram-as-code tool specialised in these three software architecture views : logical, application and infrastructure, with the aim to follow the requirements of the methodology. This tool comes with a CLI and a browser playground, both providing template initializing for each type of diagram, validation, live previous, and export to SVG and PNG format.
Ongoing development - Pre-releases are available. Stable version will be release when all opened issues are resolved.
A large majority of the diagrams (logical, application, infrastructure view) in existing Software Architecture Documents I've worked are made with graphical diagramming softwares such as Drawio and the like. However, modifying diagrams manually in a GUI despite providing more control on the display, take time and migrating to diagram as code has proven complicated since most diagrams are rich and it's hard to preserve the same level of information with other solutions like C4.
Furthermore, complexe software architecture with many flows and component generated with existing diagram-as-code tools end up very large or with overlapping flow labels making them unreadable and therefore not possible to integrate in a techical architecture document that requires specifically a logical view, application, physical & infrastructure view and Cairn is made specially to answer this need by provided the following features :
| Features | Description |
|---|---|
| Readability through overlap-checked layout. | Label space is reserved during layout; overlaps are measured every build and shipped at 0, with a CI gate. Each flow stays a distinct arrow with its own label. Labels can also be numbered, with the full labels displayed in the legend. |
| Configurable dispositions | slide and page dispositions available to suit different presentation requirements |
| Spacial optimization | Cairn aims to optimize space as much as possible (Still working on improving this functionnality) |
| Typed diagrams with validation. | Each view defines its element kinds and rules; cairn validate reports syntax, schema, and completeness issues as source-located, coded diagnostics, with a JSON mode for CI. |
| Flow matrix. | cairn matrix exports the flow matrix as CSV, Markdown, or SVG — from any view, with the columns that view can fill. In infrastructure it splits protocol from port and annotates endpoints that sit in a network zone; headers localise via style { lang: fr }. The same table is available to embedders through compile(source, { matrix: true }). |
| Author-controlled positioning. | Layout is automatic, with three opt-in overrides for when it gets a diagram wrong: order: 2 sequences a top-level element along the disposition's own reading direction (left to right in wide, top to bottom in tall) and sorts siblings inside a container, A.right -> B.left pins which side of an element a flow leaves and arrives on, and the arrow glyph carries the line style (-> solid, --> dashed, ..> dotted). |
| Playground drag-to-adjust. | When the automatic layout needs a correction, the playground lets you drag elements, flow labels, a single route segment, or a flow's endpoint onto another side. Every drag is written back into the source as the matching hint — offset:, label-offset:, segment-offset:, ID.side — so the adjustment stays versionable text and re-renders identically from the CLI. A hovered flow label also offers a wrap button, which writes that flow's own flow-label-wrap:. |
| Enterprise-view extras. | Business objects on flows, an auto-generated legend, and a numbered-flow table via flow-text: numbered. |
| French or English output. | style { lang: fr } localizes band titles, legend, and matrix headers while keeping keywords English for portable sources (open to adding other languages if you find this usefull) |
| In-built themes and customizable colours | Whether using the default or a chosen in-built theme, element colours can be overriden for all elements of a given kind in the style block |
Cairn is not a replacement for general diagram tools; for flowcharts, sequence, or ER diagrams, Mermaid or D2 remain the better fit. For C4-level software-structure modeling, dedicated C4 tools like Structurizr or LikeC4 (c4model.com) are a mature choice.
As a result here's a comparaison of the same diagram done with D2 (ELK Layout) vs Cairn (tuned ELK Layout) :
| D2 (ELK Layout) | Cairn (ELK) |
![]() |
![]() |
| Link to D2 playground | Diagram in Cairn playground |
| I encountered overlapping issues for which I couldn't find a workaround | The overlapping labels have been addressed. |
Either use the cli or the playground (elements are and labels are manually draggable with DSL updated automatically with corresponding offsets).
Every image below is rendered by cairn CLI from a .cairn source in examples/ — plain SVG, zero label overlaps.
The example diagrams have been generated with AI and some of them purposely large to showcase how such diagrams are rendered with Cairn to handle overlap.
application STOREFRONT "Storefront" { logo: nextdotjs
module WEB "Web client" { logo: react }
}
queue EVENTS "Order events" { logo: apachekafka }
datastore ORDER_DB "Order store" { logo: postgresql }
module BILLING "Billing" { logo: "./logos/acme.svg" }
Six of the 37 built-in logos keep their own licence rather than simple-icons' CC0-1.0. A diagram that draws one embeds that artwork, so the SVG carries an attribution comment naming the mark, its source and its licence — keep it in the file, and check that an SVG optimiser is not set to strip comments. The other 31 add nothing. See THIRD-PARTY-NOTICES.md.
Colours are resolved at three levels — most specific wins. From lowest to highest priority:
- Theme defaults — per-kind colours defined by the selected theme (fill, stroke, text for each element kind).
- Diagram-level per-kind overrides — override colours for all elements of a given kind in the
styleblock:style { fill actor-group: #eef4fb stroke actor-group: #7a9cc4 dashed text block: #222233 background: #fffdf5 # canvas background } - Inline per-element styles — override colour for a single element:
block API "API gateway" { style { fill: #e8f5e9 stroke: #2e7d32 text: #1b5e20 } }
Flows can also be coloured inline:
COM_CTR -> OBS "Alerts…" { label: above stroke: dashed #a33 text: #a33 }
See examples/colors-custom.cairn for a full example:
Line style — carried by the arrow glyph:
TRIAGE -> TASKS (MQ, JSON) # solid — the default
SCORING --> REINSURER (SFTP, CSV) # dashed
TRIAGE ..> REINSURER (API_REST, JSON) # dotted
Attachment sides — pin either endpoint, or both, with left, right,
top or bottom:
FORM.right -> TRIAGE.left (API_REST, JSON) # both ends pinned
SCORING -> TASKS.right (MQ, JSON) # only the arrival pinned
SCORING.top -> TRIAGE.top (API_REST, JSON) # a backward flow, pinned both ends
Element ordering — order: is a statement in the element's own body, not a
style entry. Lower comes first along the disposition's reading direction: left
to right in wide/slide, top to bottom in tall/page. From
examples/placement/reading-order.cairn:
application BACKEND_L1 "Line 1 backend" {
order: 1
module MSG_L1 "Messaging\nhandler"
}
application BACKEND_L2 "Line 2 backend" {
order: 2
module MSG_L2 "Messaging\nhandler"
}
Inside a container the axis flips: order: sorts the siblings sharing a layer —
top to bottom in wide, left to right in tall. From
examples/positioning.cairn:
actor-group STAFF "Payment actors" {
actor OPERATOR "Payment operator" { order: 1 }
actor AUDITOR "Compliance auditor" { order: 2 }
}
More examples: examples/placement/ shows one shape four
ways (baseline.cairn, sides.cairn, reading-order.cairn,
flow-label.cairn), and examples/dispositions/ ships
the attachment-sides file as wide, tall, slide and page variants.
Label position — this one is style, so it lives in the style block and
resolves at the usual three levels (view default, diagram, inline per flow). From
examples/placement/flow-label.cairn:
style {
flow-label: above # on-line (default) | above | below
}
AGENT -> RECEIVE "File a claim" # above, from the style block
ASSIGN -> SETTLE "Request settlement" { label: below } # overridden per flow
TRACE -> STORE "Archive the trace" { label: on-line } # back to the default
on-line centres the text on the run behind a halo; above/below lift it clear
by a fixed offset first. Either way the label is settled afterwards, so the
zero-overlap gate holds. Every other diagram in this README shows the on-line
default.
Same diagram different disposition :
This example has a large number of flows on purpose in order to showcase how it renders. But in such a case, it's best to consider breaking the diagram into two diagrams : one more general one more zoomed in.
Cairn comes with a collection of built-in themes. Choose the one that best fits your presentation or documentation style:
| Classic |
Classic Dark |
Light |
| Dark |
Contrast |
Nord |
| Sand |
Slate |
Solarized |
Pick one from the command line with --theme, without editing the diagram:
cairn build my-system.cairn --theme nord
cairn themes # list the built-in names--theme also takes a JSON file. It extends a built-in and overrides only
what it names, so a usable theme is a few lines rather than fifty colours:
{
"extends": "dark",
"dark": true,
"pal": { "bg": "#0d1117", "nStroke": "#58a6ff" },
"accentColors": { "blue": "#58a6ff", "blueF": "#0d2136" }
}cairn build my-system.cairn --theme ./my-theme.jsonPrebuilt, self-contained binaries are published on every v* tag (no runtime needed). Pick your platform:
# macOS / Linux — curl installer
curl -fsSL https://raw.githubusercontent.com/R0kshan/cairn/main/packaging/install.sh | sh
# macOS / Linux — Homebrew
brew install R0kshan/tap/cairn
# Windows — Scoop
scoop bucket add cairn https://github.com/R0kshan/scoop-bucket
scoop install cairnFrom source (no release needed — requires Node ≥ 22.6):
git clone https://github.com/R0kshan/cairn && cd cairn
npm install
npm run cairn -- --help # or: node bin/cairn.js --helpOnce installed, the command is cairn. From a clone, run npm run cairn -- <command>.
cairn new -L my-system.cairn # -L logical · -A application · -I infrastructureThe chosen view is written into the file header (diagram logical …); every other command reads it from there.
cairn validate my-system.cairn # --format json for CI/agents · --strict to fail on warningsProblems are reported as source-located, coded diagnostics with a suggested fix:
error[E0210]: functional block outside any system (`ORPHAN`)
--> my-system.cairn:8:7
|
8 | block ORPHAN "Floating block"
| ^^^^^^
help: move this `block` inside a `layer`, `system` or `external`
cairn build my-system.cairn -o my-system.svg # -o optional; defaults to the same name, .svg
cairn build my-system.cairn --theme nord # or --theme ./my-theme.jsonOn validation errors nothing is written and the exit code is 1; warnings are printed but do not block.
cairn matrix my-infra.cairn --format csv # csv (default) | md | svg · -o to set the pathEvery view exports one; the columns follow the view (infrastructure keeps protocol and port, application keeps the protocol alone, logical keeps just the exchange).
cairn watch my-system.cairnRebuilds the SVG on save. On a compile error the SVG becomes an error panel (codes, lines, help), so an open preview never shows a stale diagram. Watch observes only the file it was launched on — run one per file. Pair it with an editor that auto-refreshes an open SVG.
cairn logos37 built-in logos — use as `logo: <name>` on an element:
angular apache apachekafka apachespark django
...
Anything else: point at a file — logo: "./logos/name.svg".
cairn explain E0240E0240 — The infrastructure view requires every flow to carry its protocol (and port if
relevant): the flow matrix is the primary output of this view. Add `(HTTPS/443)` after the label.
cairn version # or --version / -v
cairn version --licensesReleased binaries (the ones from Homebrew, Scoop, or the GitHub Releases page) print the exact tag they were built from. Running from a clone via npm run cairn prints package.json's version instead. The bare form prints one line and nothing else, so it stays safe to parse in a script.
--licenses adds the third-party notices for that exact build — elkjs under EPL-2.0, the Simple Icons artwork, and, for the released binaries only, the embedded Bun runtime and its LGPL-2.1 JavaScriptCore. It reads from inside the artifact, so it describes the copy you are running rather than whatever the repository says today.
That output is a convenience, not the licence itself. The full texts are installed with cairn: share/doc/cairn/ for the curl installer and Homebrew, the app directory for Scoop, the package root for npm. THIRD-PARTY-NOTICES.md is the long form, with per-icon attribution and the LGPL relink offer.
cairn is Apache-2.0. Its shipped artifacts also contain third-party code Apache-2.0 does not cover: elkjs (EPL-2.0) inlined into every bundle and binary, a curated set of Simple Icons paths vendored into the logos, and — in the release binaries only — the Bun runtime, which statically links JavaScriptCore under the LGPL.
Full texts live in licenses/, their provenance in licenses/README.md, and the long form in THIRD-PARTY-NOTICES.md.
Find your row. Most people are in the first one, and owe nothing.
| What you are doing | What you owe |
|---|---|
| Sharing a diagram | Nothing — unless it draws one of the six licensed logos below, and then cairn has already written the attribution into the SVG. Leave the comment in place. |
Depending on @r0kshan/cairn from npm |
Nothing. You distribute no copy of cairn; npm install fetches it with its notices intact. |
| Bundling cairn into your own artifact (webpack, Docker, Electron, single-file CLI) | Copy LICENSE, THIRD-PARTY-NOTICES.md and licenses/ out of the installed package into your distribution. |
| Redistributing a cairn binary | The same three files — and every installer has already put them on disk beside it (share/doc/cairn, the app directory, or the package root, by channel). |
Two things worth knowing:
- 6 of the 37 built-in logos —
angular,apache,apachekafka,apachespark,javascript,openjdk— keep their own licence instead of the project-wide CC0-1.0. The other 31 ask for nothing. - Converting a diagram to PNG, or pasting it into a slide deck, drops the attribution comment. The artwork is still there, so the attribution has to go somewhere you control: a caption, a credits slide, or
licenses/shipped alongside. This is the one case cairn cannot handle for you.
Diagrams you produce are yours — cairn claims nothing in them. The brands drawn by the built-in logos remain their owners' trademarks.
DIAGNOSTICS.md— every diagnostic code and its meaning.DSL_SPEC.md— the DSL syntax.ARCHITECTURE.md— how the pipeline fits together, for contributors.CONTRIBUTING.md— how to open a PR, and the gates it must pass.documentation/— architecture, invariants, DSL and internals.THIRD-PARTY-NOTICES.md— third-party licence notices, with per-icon attribution.

