Marrow

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.html

The threshold

  devices > 150  OR  top-level zones with devices > 5   →  multi-page site
  otherwise                                             →  single file

Both 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 index

Also 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, or edge — 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 · zones15,591 · 18,812 · 1,017
Sites194
Pages emitted15,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.yaml

Scale 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.svg

The 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.png

Exported 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.
Static SVG Blueprint export featuring the CAD Title Block, active topology Auto-Legend, and forced port labels
Vector SVGCAD Title Block, dynamic Auto-Legend, and full port visibility
Open raw SVG ↗

URL export parameters

Configure static export overlays using URL search parameters:

ParameterValuesDescription
export_titleon (default), offToggle engineering title block stamping
export_legendon (default), offToggle dynamic auto-legend box
export_portson (default), offToggle 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.html

For embedding in an existing docs site or wiki, see Embedding diagrams.