Output shapes
A single self-contained file, or a multi-page site — what triggers each, what's inside, and the guarantees both hold to.
marrow build produces one of two shapes. You don't choose; the topology's size
does — with one override.
npm run build input/branch.yaml # let the planner decide
npm run build -- input/branch.yaml --single # force one file (-- : flag ahead)
npm run build -- input/branch.yaml -o dist/net.htmlThe threshold
devices > 150 OR top-level zones with devices > 5 → multi-page site
otherwise → single fileBoth are checked; either one trips it. Five zones is fine, six is a site.
Single file
One .html in output/. Styles, the ~20 KB runtime client, and the icon
sprites for the icons you actually used are all inlined.
Best for: anything you want to attach to a ticket, mail to a vendor, drop in a Confluence page, or open from a USB stick on a plane.
Multi-page site
A directory:
output/global-enterprise/
├── index.html overview — zones as summary tiles
├── zones/
│ ├── campus.html one page per TOP-LEVEL zone (covers its subtree)
│ └── dc-east.html
├── devices/
│ ├── core-sw-01.html device-360 pages
│ └── dist-sw-01.html
├── inventory.html sortable, paginated table of everything
└── assets/
├── styles.css
├── runtime.js
└── search-data.js cross-page search indexAlso fully offline — file:// works, no server required.
Which zones get pages
Only top-level zones — a zone with no parent. Its page covers its entire
subtree. So nesting depth is how you control page granularity: if emea > london > ldn-dc1 is your hierarchy, you get one emea page, not three.
Which devices get pages
A device earns a dedicated page if it is:
- tier
core,distribution, oredge— the infrastructure that people look up, or - carrying a
note, or - carrying a
config
Which is a good reason to write notes: documentation is what promotes a device to its own page.
Ghost nodes
A link that crosses a page boundary terminates in a dimmed, dashed "ghost" stub that navigates to the device's own page. Nothing is silently dropped at a page edge.
At scale
The multi-page shape exists so that no single page grows with the network. A device page carries its own neighborhood, the shared assets are written once, and everything else is another file you never load until you open it. That holds all the way up.
The reference topology — a fictional global enterprise, the At scale demo on the home page — is the stress test:
| Devices · links · zones | 15,591 · 18,812 · 1,017 |
| Sites | 194 |
| Pages emitted | 15,775 (194 zone · 15,579 device · overview · inventory) |
| Total output | ~2.4 GB on disk |
| A device page | ~140 KB — ~12 KB gzipped, which is what a host serves |
| Build time | ~3 minutes |
Two things that matter in practice:
Served size ≠ disk size. Pages are near-identical structurally, so gzip/brotli — which every static host applies — collapses them hard. The 2.4 GB on disk is a git-and-deploy concern, not a bandwidth one.
Very large builds need more heap. Node's default heap tops out somewhere around this size and the build aborts with an out-of-memory error. Raise it for the run:
NODE_OPTIONS=--max-old-space-size=12288 npm run build -- input/global-enterprise.yamlScale the number with the topology (~12 GB comfortably covers 15k devices). Small and mid-size networks never need this.
A folder this big is more than GitHub Pages' soft 1 GB repo limit will take. Object storage (S3 + CloudFront, R2, GCS) or your own web server host it without complaint. The publishing commands below are unchanged — it is still just files.
Guarantees, both shapes
Deterministic. The same YAML produces byte-identical output. Commit built diagrams and the diff is empty unless the network changed. Content-hashed page builds mean a rebuild only touches pages whose inputs moved.
Overlap-free. The geometry linter runs before render; an L1 defect fails the build rather than shipping a broken picture.
Self-contained. No network requests when opened. No CDN, no web fonts, no
telemetry, no analytics. brand.link and brand.github are hyperlinks a reader
may click — they are not loads.
If you need a site-shaped output for a small topology (say you want the
inventory table), there is no --site flag — the planner decides. Conversely
--single will force a large topology into one file, but expect a heavy page:
the multi-page split exists because renderers and readers both struggle past a
few hundred devices.
Rendering tiers
Under the hood there are two renderers, chosen by size:
- Hero tier — SVG. Full fidelity, crisp at any zoom, exportable to SVG.
- Scale tier — a canvas host. Used when the node count would make SVG unusable.
Both draw the same scene and are kept visually identical; you shouldn't be able to tell which one you got except by the device count.
Static blueprint exports (SVG and PNG)
Marrow diagrams can be exported to standalone vector SVG or high-resolution 2x Retina PNG documents directly from the UI menubar (Export > Export SVG / Export PNG), the Command Center (⌘K > Export), or programmatically via headless CLI commands.
Headless Static SVG Export (Zero Browser Overhead)
When generating diagrams in CI/CD, documentation pipelines, or embedding directly into GitHub READMEs, Marrow can compile vector SVGs directly without launching a headless browser:
# Instantaneous standalone SVG export
npm run marrow -- export input/branch-hq.yaml --format svg -o output/branch.svg
# Or directly during build
npm run marrow -- build input/branch-hq.yaml --static-svg -o output/branch.svgThe resulting .svg file is fully self-contained:
- Tight Bounding-Box Calculation: Automatically trims outer canvas gutters and sets an accurate
viewBox. - Inlined Design Tokens: Embeds light and dark theme CSS custom properties (
--bg,--ink,--line) so the graphic renders cleanly without external dependencies. - Embedded Vector Symbols: Resolves all hardware icons into
<symbol>definitions in<defs>.
Headless Snapshotting (Playwright)
To capture full interactive canvas states or 2x Retina PNGs:
npm run snapshot input/branch-hq.yaml -o output/branch-blueprint.pngExported diagrams include an architectural blueprint footer:
- Engineering Title Block: Stamps diagram title, author, date, revision, and organization mark in a compartmentalized engineering grid.
- Dynamic Auto-Legend: Scans the active diagram and renders vector swatches for only the link modes (trunk, tunnel, peering, routed) and physical mediums (fiber, copper, DAC) actually present in the topology.
- Forced Port Label Readability: Interface port chips (
Te0/0,Gi0/1) remain visible across the entire exported canvas regardless of interactive camera zoom level.
URL export parameters
Configure static export overlays using URL search parameters:
| Parameter | Values | Description |
|---|---|---|
export_title | on (default), off | Toggle engineering title block stamping |
export_legend | on (default), off | Toggle dynamic auto-legend box |
export_ports | on (default), off | Toggle forced port label visibility |
Publishing
The output is just files:
# static host
aws s3 sync output/global-enterprise s3://docs-bucket/network/
# or commit it next to your docs
cp output/branch.html docs/network/branch.htmlFor embedding in an existing docs site or wiki, see Embedding diagrams.