Marrow

Topology schema

Every key in the Marrow 2 topology format, with types, defaults, and constraints.

The complete format. A machine-readable JSON Schema is served at /schema.json and can be regenerated with npm run marrow -- schema.

Unknown keys are errors (MRW-E005), not warnings. Prefix a key with x- to keep it private and ignored. See ADR-0006.

Document

marrow: 2                      # required — the integer 2
name: Marrow Global HQ         # required
description: One-line summary  # optional — becomes the watermark subtitle
author: NetOps Team            # optional — diagram author or architecture team
revision: v1.0                 # optional — revision string
date: "2026-10-19"             # optional — ISO date string
about:                         # optional — markdown string or structured facts/sections
  Overview: Enterprise backbone
  Architecture: |
    - Dual spine EVPN fabric
    - Redundant core interconnects
theme: dark                    # dark | light | auto   (default: dark)
density: standard              # compact | standard | spacious (default: standard)
scale: 1.0                     # float 0.4 to 2.5 (default: 1.0)
hero: core-sw-01               # optional — device featured when the diagram opens
title_block: {}                # optional — engineering title block metadata for static exports
inspector:                     # optional — custom inspector drawer tabs configuration
  tabs: []
zones: []                      # optional
devices: []                    # the nodes
links: []                      # the cables
KeyTypeRequiredNotes
marrowintegeryesMust be exactly 2. MRW-E003 if missing, MRW-E004 if another value
namestringyesDiagram title
descriptionstringnoWatermark subtitle
authorstringnoEngineering team, author, or architect
revisionstringnoRevision or version tag (e.g. v1.0, 2026.Q4)
datestringnoISO date string (YYYY-MM-DD)
aboutstring | mapnoDiagram-level documentation rendered in the About this diagram document dialog. Accepts markdown or structured sections/facts
title_blockobjectnoCAD title block configuration for static vector SVG and PNG exports
inspectorobjectnoDeclarative custom tab definitions for the right-hand inspector drawer
themedark | light | autonoDefault dark. auto follows the viewer's OS scheme; a host can also drive any embed's theme by postMessage (see Embedding → Theme)
densitycompact | standard | spaciousnoDefault standard. Scales node chips (48px / 64px / 80px), typography (10px / 12px / 14px), layer spacing, and port chips
scalefloat 0.4–2.5noGlobal layout scale multiplier. Default 1.0. Multiplies box padding, icon chip size, and layer spacing
herodevice idnoCamera focus on open
zoneslistno
deviceslist—
linkslist—

Identifiers

Every id matches [a-z0-9._-]+, starting with a letter or digit. Device ids are unique among devices; zone ids are unique among zones. Violations are MRW-E012 (invalid) and MRW-E008 (duplicate).

Zones

zones:
  - id: campus            # required, kebab-case
    name: Campus          # optional — defaults to id
    parent: emea          # optional — nest by referencing another zone id
    collapse: auto        # auto | open | closed      (default: auto)
    accent: "#7aa2ff"     # optional hex — tints the zone panel
KeyTypeRequiredNotes
ididentifieryes
namestringnoDefaults to id
parentzone idnoMust exist. Cycles are MRW-E016
collapseauto | open | closednoDefault auto
accenthex colornoe.g. "#7aa2ff"

Devices

devices:
  - id: core-sw-01
    name: Core Switch 1
    icon: cisco/switches/layer-3-switch
    tier: core
    zone: campus
    role: L3 core
    status: up
    mgmt_ip: 10.0.0.1
    ha_pair_with: core-sw-02
    hero: false
    pin: { x: 100, y: 200 }
    accent: "#7aa2ff"
    note: "Change freeze"
    config: "hostname core1"
    custom_fields:
      Serial Number: "FOC21345678"
      Rack: "DC-1 / Rack A1"
    sections:
      Firewall Policies: |
        | Rule | Protocol | Port | Action |
        | 10   | TCP      | 443  | Allow  |
        | 20   | TCP      | 80   | Allow  |
    interfaces:
      - name: Gi1/0/1
        description: uplink
        vlan: 10
        ip: 10.0.0.1
KeyTypeRequiredNotes
ididentifieryesUnique among devices
namestringnoDefaults to id
iconicon idyesMust exist — MRW-E010 with suggestions if not
tiercore | distribution | access | edge | endpoint | managementnoDrives vertical rank
zonezone idnoMust be declared — MRW-E013
rolestringnoFree text, shown in the inspector
statusup | down | degraded | planned | unknownnoDefault up
mgmt_ipstringno
ha_pair_withdevice idnoDeclare on both devices
herobooleannoAlternative to the document-level hero:
pin{x, y}noFixes position, bypassing placement
accenthex colorno
notestringnoInspector + promotes the device to its own page on sites
configstringnoConfig snippet; same promotion effect
custom_fieldsmap of string: primitivenoKey-value pairs displayed in the inspector facts drawer
sectionsmap of string: contentnoCollapsible tabs in inspector; content can be markdown tables, key-value objects, or arrays of row objects
interfaceslistno

Interfaces

KeyTypeNotes
namestringe.g. Gi1/0/1
descriptionstring
vlaninteger1–4094
ipstring

Interfaces are documentation. The port in a link endpoint is geometry. They don't have to match, but agreement makes a much better inspector.

links:
  - from: core-sw-01:Gi1/0/1     # "device-id:Port" — port optional
    to: acc-sw-01:Gi0/48
    kind: fiber                  # the physical medium
    speed: 10G
    mode: trunk                  # the logical function (drives the symbol)
    vlans: "10,20,30"            # VLANs carried on the trunk
    native_vlan: 1
    lag: po1
    lacp: active
    status: up
    animated: true
    label: "×2"
    note: "packet loss 12%"

A link has two independent axes: the physical medium (kind, speed) and the logical function (mode and its refining attributes). They compose — a 10G fiber cable can be a trunk, an access port, or a routed L3 link.

KeyTypeRequiredNotes
fromendpointyesdevice-id or device-id:Port
toendpointyesSame
kindcopper | fiber | dac | wireless | virtual | wan | serialnoPhysical medium
speed10M | 100M | 1G | 2.5G | 5G | 10G | 25G | 40G | 100G | 400GnoDrives stroke width
modeaccess | trunk | routed | tunnel | peering | stacknoLogical function — drives the symbol (see below)
vlansstring | integernoVLAN set/range on a trunk, or the single VLAN on an access port, e.g. "10,20,30-39"
native_vlaninteger 1–4094noNative (untagged) VLAN on a trunk
encapipsec | gre | vxlan | wireguard | dmvpn | mpls | l2tp | geneve | sslnoOverlay/VPN encapsulation on a tunnel
protocolospf | ebgp | ibgp | bgp | eigrp | isis | rip | staticnoRouting/control-plane protocol on a routed or peering link
lagstringnoBundle id — links sharing it between the same pair aggregate onto one strand
lacpactive | passive | on | static | pagpnoAggregation control mode; pairs with lag
statusup | down | degraded | plannednoDefault up. No unknown on links
animatedbooleannoFlowing dash — use sparingly
labelstringnoEdge label (aggregate bundles auto-label ×N)
notestringnoFreeform note shown in link inspector drawer
custom_fieldsmap of string: primitivenoKey-value pairs displayed in link inspector drawer
sectionsmap of string: contentnoCollapsible tabs in link inspector drawer

mode declares what the cable carries; the compiler renders each as a distinct symbol (see ADR-0009). It is declared, never inferred — the engine will not guess trunk from a port name. The refining attributes read like device config:

ModeSymbolCompanion keysReads like
accessplain line, v<vlan> chipvlansan access switchport
trunkdouble rail + VLAN chipvlans, native_vlanswitchport mode trunk
routedsolid line + protocol chipprotocolan L3 routed interface
peeringdotted line + protocol chipprotocolan OSPF/BGP adjacency
tunneldashed line + lock encap chipencapa site-to-site VPN / overlay
stackthick bar, stack chip—a stack/backplane cable

Aggregation is orthogonal to mode: give the members a shared lag id (plus an optional lacp mode) and they bundle onto one strand whose chip is the bundle id (PO1). A LAG can be a trunk, an access, or a routed port-channel.

# an LACP port-channel carrying a trunk
- { from: core-1:Te1/0/1, to: core-2:Te1/0/1, lag: po1, lacp: active, mode: trunk }
- { from: core-1:Te1/0/2, to: core-2:Te1/0/2, lag: po1, lacp: active, mode: trunk }
# a site-to-site IPsec VPN and a VXLAN overlay
- { from: fw-a:tun0, to: fw-b:tun0, mode: tunnel, encap: ipsec }
- { from: leaf-1, to: leaf-2, mode: tunnel, encap: vxlan }
# eBGP peering to an upstream
- { from: edge-rt, to: isp-rt, mode: peering, protocol: ebgp, kind: wan }

Attributes that don't match the mode are advisory warnings, not errors (MRW-W105) — e.g. encap on a non-tunnel link, or lacp without a lag.

Endpoint parsing

Only the first colon splits device from port.

core-sw-01:Gi1/0/1   →  device "core-sw-01", port "Gi1/0/1"
core-sw-01           →  device "core-sw-01", no port

Since ids are kebab-case, they never contain a colon, so this is unambiguous. A malformed endpoint is MRW-E014; an unresolvable device is MRW-E009.

Referential rules

All enforced by validate:

  • Every zone and device reference must exist.
  • Ids are unique within their kind.
  • A port reused across links → warning MRW-W101.
  • An unlinked device → warning MRW-W103.
  • An empty zone → warning MRW-W102.
  • A link from a device to itself → warning MRW-W104.
  • A zone hierarchy cycle → error MRW-E016.

About documentation

The about: key configures the interactive About this diagram document dialog modal. It accepts either raw Markdown or a dictionary of structured sections:

# Form A: Structured dictionary of sections (facts or Markdown)
about:
  Overview: Enterprise multi-vendor routing lab connecting Cisco and Arista nodes.
  Architecture Notes: |
    - Spine/leaf EVPN-VXLAN fabric
    - Redundant LACP bundles across all distribution uplinks
    - OSPF Area 0 backbone transit
  Contacts:
    NOC: "noc@example.com"
    Lead Architect: "Jane Doe"
# Form B: Freeform Markdown document
about: |
  ## Data Center Fabric Overview
  This topology documents the migration from classic three-tier architecture
  to a modern spine-and-leaf Clos network.
  
  | Tier | Platform | Uplinks |
  | :--- | :--- | :--- |
  | Spine | Arista 7050X3 | 100G QSFP28 |
  | Leaf | Cisco Nexus 93180 | 40G QSFP+ |

When omitted, the modal opens with default topology specifications (diagram mode, inventory device count, active interconnects, and compiler schema version) alongside a tip explaining how to add custom documentation.

Custom inspector tabs

Topologies can declare custom tabs for the right-hand inspector drawer via inspector.tabs:

inspector:
  tabs:
    - id: bgp
      label: BGP Routing
      target: device          # device | edge | both (default: device)
      field: bgp_neighbors    # maps to device.custom_fields.bgp_neighbors or sections
    - id: telemetry
      label: Telemetry
      target: both
      fields: [latency, drops, optical_budget]
KeyTypeRequiredDescription
ididentifieryesUnique identifier for the inspector tab
labelstringyesTitle displayed on the inspector drawer tab button
targetdevice | edge | bothnoTarget element type (default: device)
fieldstringnoSingle custom field or section key to display in the tab
fieldslist of stringsnoMultiple field keys bundled into a structured facts table

Private annotations

devices:
  - id: core-sw-01
    icon: cisco/switches/layer-3-switch
    x-owner: netops
    x-cmdb-id: CI0004421

Any x- prefixed key at any level is preserved in the file and ignored by the compiler.

Inert legacy keys

x-v1-config: may appear in files produced by marrow import. It records the archived v1 configuration (engines:, layouts:, output_filename:) and has zero effect in v2. Delete it.