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
| Key | Type | Required | Notes |
|---|---|---|---|
marrow | integer | yes | Must be exactly 2. MRW-E003 if missing, MRW-E004 if another value |
name | string | yes | Diagram title |
description | string | no | Watermark subtitle |
theme | dark | light | auto | no | Default dark. auto follows the viewer's OS scheme; a host can also drive any embed's theme by postMessage (see Embedding → Theme) |
hero | device id | no | Camera focus on open |
zones | list | no | |
devices | list | — | |
links | list | — |
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
| Key | Type | Required | Notes |
|---|---|---|---|
id | identifier | yes | |
name | string | no | Defaults to id |
parent | zone id | no | Must exist. Cycles are MRW-E016 |
collapse | auto | open | closed | no | Default auto |
accent | hex color | no | e.g. "#7aa2ff" |
Devices
| Key | Type | Required | Notes |
|---|---|---|---|
id | identifier | yes | Unique among devices |
name | string | no | Defaults to id |
icon | icon id | yes | Must exist — MRW-E010 with suggestions if not |
tier | core | distribution | access | edge | endpoint | management | no | Drives vertical rank |
zone | zone id | no | Must be declared — MRW-E013 |
role | string | no | Free text, shown in the inspector |
status | up | down | degraded | planned | unknown | no | Default up |
mgmt_ip | string | no | |
ha_pair_with | device id | no | Declare on both devices |
hero | boolean | no | Alternative to the document-level hero: |
pin | {x, y} | no | Fixes position, bypassing placement |
accent | hex color | no | |
note | string | no | Inspector + promotes the device to its own page on sites |
config | string | no | Config snippet; same promotion effect |
interfaces | list | no |
Interfaces
| Key | Type | Notes |
|---|---|---|
name | string | e.g. Gi1/0/1 |
description | string | |
vlan | integer | 1–4094 |
ip | string |
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
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.
| Key | Type | Required | Notes |
|---|---|---|---|
from | endpoint | yes | device-id or device-id:Port |
to | endpoint | yes | Same |
kind | copper | fiber | dac | wireless | virtual | wan | serial | no | Physical medium |
speed | 10M | 100M | 1G | 2.5G | 5G | 10G | 25G | 40G | 100G | 400G | no | Drives stroke width |
mode | access | trunk | routed | tunnel | peering | stack | no | Logical function — drives the symbol (see below) |
vlans | string | integer | no | VLAN set/range on a trunk, or the single VLAN on an access port, e.g. "10,20,30-39" |
native_vlan | integer 1–4094 | no | Native (untagged) VLAN on a trunk |
encap | ipsec | gre | vxlan | wireguard | dmvpn | mpls | l2tp | geneve | ssl | no | Overlay/VPN encapsulation on a tunnel |
protocol | ospf | ebgp | ibgp | bgp | eigrp | isis | rip | static | no | Routing/control-plane protocol on a routed or peering link |
lag | string | no | Bundle id — links sharing it between the same pair aggregate onto one strand |
lacp | active | passive | on | static | pagp | no | Aggregation control mode; pairs with lag |
status | up | down | degraded | planned | no | Default up. No unknown on links |
animated | boolean | no | Flowing dash — use sparingly |
label | string | no | Edge label (aggregate bundles auto-label ×N) |
note | string | no |
Logical link modes
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:
| Mode | Symbol | Companion keys | Reads like |
|---|---|---|---|
access | plain line, v<vlan> chip | vlans | an access switchport |
trunk | double rail + VLAN chip | vlans, native_vlan | switchport mode trunk |
routed | solid line + protocol chip | protocol | an L3 routed interface |
peering | dotted line + protocol chip | protocol | an OSPF/BGP adjacency |
tunnel | dashed line + 🔒 encap chip | encap | a site-to-site VPN / overlay |
stack | thick 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.
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.
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.
Warnings are advisory — usually worth fixing, never blocking.
Private annotations
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.