DOT examples
Every example on this page is complete — paste any of them into Import as-is. Below each one is what it actually renders: the images are produced by Drawbridge itself, headless, from exactly the code shown (scripts/render-doc-examples.mjs) — not illustrations, output. A test (app/src/manual/api-reference.test.ts) runs every example through the real parser and checks its render is present, so an example that stops working fails the build before it can mislead you.
Three lines
digraph { internet -> firewall -> switch }
No attributes at all: the names themselves pick the icons, and the arrows make three tiers.
A small office
digraph office {
inet [kind=internet, label="Internet"]
fw1 [kind=firewall, label="FW-EDGE",
mgmtIp="192.0.2.1"]
core [kind="l3-switch", label="CORE-1"]
acc1 [kind=switch, label="ACC-1"]
acc2 [kind=switch, label="ACC-2"]
ap1 [kind="wifi-ap", label="AP-LOBBY"]
srv1 [kind=server, label="SRV-FILES"]
db1 [kind=database, label="DB-PROD"]
inet -> fw1
fw1:port1 -> core:ge1 [speed="10G"]
core -> {acc1 acc2} [speed="1G"]
acc1 -> ap1
acc2 -> {srv1 db1}
}
What to notice: quoted values wherever a hyphen appears ("l3-switch", "wifi-ap"), ports on the firewall–core link, a brace fan-out from the core, and a speed carried by a whole fan-out at once.
An HA firewall pair
digraph ha {
inet [kind=internet, label="Internet"]
fw1 [kind=firewall, label="FW-A",
notes="HA primary"]
fw2 [kind=firewall, label="FW-B",
notes="HA secondary"]
core [kind="l3-switch", label="CORE"]
{rank=same; fw1 fw2}
inet -> {fw1 fw2}
fw1:e -> fw2:w [bundle=ha, link=lag,
label="HA sync"]
fw1 -> core
fw2 -> core
}
The rank=same group keeps the pair on one row; the sync link leaves A's east side and enters B's west, and bundle=ha draws it as an aggregate ring.
Campus with an MCLAG distribution pair
digraph campus {
fw [kind=firewall, label="FW-CAMPUS"]
dist1 [kind="l3-switch", label="DIST-1"]
dist2 [kind="l3-switch", label="DIST-2"]
a1 [kind=switch, label="ACC-B1-01"]
a2 [kind=switch, label="ACC-B1-02"]
a3 [kind=switch, label="ACC-B2-01"]
{rank=same; dist1 dist2}
fw -> {dist1 dist2}
dist1 -> dist2 [label="ISL", link=lag]
dist1 -> {a1 a2 a3}
dist2 -> {a1 a2 a3}
}
Dual-homed access switches, a declared distribution tier, and an ISL that is drawn between the peers without stacking them — the same shape a FortiLink MCLAG dump has, written by hand.
A spine–leaf fabric
digraph fabric {
spine1 [kind="l3-switch", label="SPINE-1"]
spine2 [kind="l3-switch", label="SPINE-2"]
leaf1 [kind=switch, label="LEAF-1"]
leaf2 [kind=switch, label="LEAF-2"]
leaf3 [kind=switch, label="LEAF-3"]
leaf4 [kind=switch, label="LEAF-4"]
{rank=same; spine1 spine2}
spine1 -> {leaf1 leaf2 leaf3 leaf4} [speed="100G"]
spine2 -> {leaf1 leaf2 leaf3 leaf4} [speed="100G"]
}
Two fan-outs say the whole fabric. The spines share a row by declaration; the leaves land side by side beneath them on their own.
Link types and colors
digraph wan {
hq [kind=firewall, label="FW-HQ"]
br1 [kind=firewall, label="FW-BRANCH-1"]
br2 [kind=firewall, label="FW-BRANCH-2"]
mgmt [kind=server, label="MGMT-01"]
ap [kind="wifi-ap", label="AP-BR1"]
hq -> br1 [link=ipsec, label="VPN",
color="#7c3aed"]
hq -> br2 [link=ipsec, label="VPN",
color="#7c3aed"]
hq -> mgmt [link=mgmt]
br1 -> ap [link=wireless]
}
link= picks the connector style — IPsec tunnels, a management link, a wireless hop — and hex color= tints the strokes. Named colors would be ignored; hex is deliberate.
An inventory that fills the inspector
digraph assets {
fw1 [kind=firewall, label="FW-CORE-1",
mgmtIp="10.0.0.1",
serial="FG100FTK00000001",
model="FortiGate-100F",
location="DC1 rack 3",
notes="HA primary"]
sw1 [kind="l3-switch", label="SW-CORE-1",
mgmtIp="10.0.0.2",
serial="S448DFTF00000011",
model="FS-448D",
location="DC1 rack 3"]
srv1 [kind=server, label="ESX-01",
mgmtIp="10.0.4.10",
model="PowerEdge R650",
location="DC1 rack 5"]
fw1 -> sw1 -> srv1
}
Select any imported device and the inspector's asset fields are already filled — and they flow onward into the CSV inventory export.
An OT cell
digraph cell {
scada [kind="scada-server", label="SCADA-01"]
eng [kind="engineering-workstation",
label="ENG-01"]
hmi1 [kind=hmi, label="HMI-LINE-1"]
plc1 [kind=plc, label="PLC-LINE-1"]
plc2 [kind="safety-plc", label="SAFETY-1"]
io1 [kind="remote-io", label="IO-1"]
{rank=same; scada eng}
scada -> hmi1
eng -> plc1
hmi1 -> plc1 [link=conduit]
plc1 -> io1 [link=fieldbus]
plc1 -> plc2 [link=conduit]
}
The OT family — PLCs, HMIs, safety controllers, remote I/O — with conduit and fieldbus link styles.
The Purdue model as tiers
digraph purdue {
subgraph cluster_l4 {
purdue="4"
erp [kind=server, label="ERP"]
fw_it [kind=firewall, label="FW-IT"]
}
subgraph cluster_idmz {
purdue="3.5"
hist_dmz [kind=historian,
label="HIST-MIRROR"]
jump [kind="jump-host", label="JUMP-OT"]
}
subgraph cluster_l3 {
purdue="3"
fw_ot [kind=firewall, label="FW-OT"]
scada [kind="scada-server",
label="SCADA-01"]
hist [kind=historian, label="HIST-01"]
eng [kind="engineering-workstation",
label="ENG-01"]
}
subgraph cluster_l2 {
purdue="2"
hmi1 [kind=hmi, label="HMI-LINE-1"]
}
subgraph cluster_l1 {
purdue="1"
plc1 [kind=plc, label="PLC-LINE-1"]
sis1 [kind="safety-plc", label="SIS-1"]
}
subgraph cluster_l0 {
purdue="0"
io1 [kind="remote-io", label="IO-1"]
vfd1 [kind=vfd, label="VFD-1"]
sio1 [kind="remote-io", label="SIS-IO-1"]
}
erp -> fw_it
fw_it -> {hist_dmz jump}
{rank=same; hist_dmz jump}
hist_dmz -> fw_ot
jump -> fw_ot
fw_ot -> {scada hist eng}
{rank=same; scada hist eng}
scada -> hmi1
hmi1 -> {plc1 sis1} [link=conduit]
{rank=same; plc1 sis1}
plc1 -> sis1 [link=conduit]
plc1 -> {io1 vfd1} [link=fieldbus]
sis1 -> sio1 [link=fieldbus]
}
Declaring purdue="N" on a cluster is what draws the Purdue backdrop itself: the import places the palette's Purdue-levels underlay behind the topology, sizes it so every band fits its devices, and seats each cluster's members inside their band — ERP and its firewall in level 4, the historian mirror and jump host in the industrial DMZ (3.5), site operations in 3, down to the process I/O in 0. {rank=same} still keeps pairs level within their band, and conduit/fieldbus mark the OT link types. The device's level is legible from its y-position alone — which is the whole point of the Purdue annotation.
A FortiLink dump, exactly as pasted
Managed FortiLinks in current VDOM root:
FortiLink interface : fortilink
digraph {
graph [layout = dot, rankdir = TB]
node [shape = rectangle, style = filled, color = grey]
//fgt cluster
node [fillcolor = red]
FG200FTK00000010
//tier 1
node [fillcolor = blue]
S448DFTF00000011
S448DFTF00000012
//tier 2+
node [fillcolor = green]
edge [color = grey, arrowhead=none]
FG200FTK00000010 -> {S448DFTF00000011 S448DFTF00000012} [color=black]
S448DFTF00000011 -> S448DFTF00000012
S448DFTF00000011 -> {S124EF0000000013}
S448DFTF00000012 -> {S124EF0000000014}
{rank=same; FG200FTK00000010}
{rank=same; S448DFTF00000012 S448DFTF00000011}
}
This is the real shape of execute switch-controller get-physical-conn dot fortilink output (fabricated serials), CLI chrome included. The fillcolor defaults carry the roles — red imports as a firewall, blue and green as switches — the serials fill the asset fields, and the multi-node rank=same keeps the MCLAG pair level while their ISL is drawn between them.
Generated by an AI
The prompt on the chapter's first page produced this from "a small clinic: reception, two exam rooms with wifi, a records server behind its own firewall":
digraph clinic {
inet [kind=internet, label="Internet"]
fw_edge [kind=firewall, label="FW-EDGE"]
core [kind=switch, label="SW-CORE"]
ap_recep [kind="wifi-ap", label="AP-RECEPTION"]
ap_exam1 [kind="wifi-ap", label="AP-EXAM-1"]
ap_exam2 [kind="wifi-ap", label="AP-EXAM-2"]
fw_rec [kind=firewall, label="FW-RECORDS"]
srv_rec [kind=server, label="SRV-RECORDS"]
inet -> fw_edge -> core
core -> {ap_recep ap_exam1 ap_exam2}
core -> fw_rec -> srv_rec
}
Nothing exotic — which is the point: the format an AI reliably produces is the same one this whole chapter documents.