rediagram .app

Specification · .dgm 1.0

A diagram format meant to be written, not drawn

Every format we import was designed for a canvas with a mouse on it. Each fails a writer with no eyes the same way: it asks for geometry. This one asks for meaning.

Why it exists

draw.io, Excalidraw and tldraw are scene graphs: every element carries an absolute x and y, so a model has to invent coordinates whose consequences it cannot see. Visio adds an OPC zip and master indirection on top. Mermaid is the closest thing to writable and still couples the drawing to a layout direction, has no way to say what a box is, and resolves ambiguity by position.

In .dgm the author declares meaning and the renderer owns every pixel. There are no coordinates in the format at all.

A whole file

dgm: 1.0
diagram: architecture
title: Content site in production
direction: LR

zones:
  content: Content

nodes:
  reader: { label: Reader, role: actor, note: Browser }
  cdn:    { label: Cloudflare, note: "Pages · cache" }
  origin: { label: Astro Origin, note: SSR + MDX, focus: true }
  mdx:    { label: MDX Bundle, role: store, in: content }
  cms:    { label: Content CMS, role: store, in: content }

flow:
  - reader -> cdn: HTTPS
  - cdn -> origin: SSR
  - origin ~> mdx: read mdx
  - origin ~> cms: query
  - cdn ..> reader: resp

steps: [cdn, origin]

Keys

dgm
Format version. Absent means 1.0. See versioning below.
diagram
The visual type. Advisory — the renderer classifies from the graph anyway.
title
Printed as the heading. Optional; a filename is used when it is absent.
direction
TB, BT, LR or RL. A hint, not a coordinate. Defaults to TB.
brand
Slug of a saved brand profile. Ignored by the free tool, which stores nothing.
zones
Mapping of id to label. Drawn as a labelled container behind its members.
nodes
Mapping of id to definition. `id: Label` is the short form.
flow
List of connections. See the arrows below.
steps
Reading order, as a list of node ids. The one thing no other format can state.
axes
Quadrant only, and required there. `x` and `y`, each with a `low` and a `high`, naming what the two-by-two crosses. y high is the top, x high is the right.

Node fields

label
The name. The whole node may be written as a bare string instead.
role
process (default), decision, store, terminal, actor. Chooses the silhouette.
note
A detail line under the name, set in mono.
in
Zone id. Containment is declared on the child, so no membership list can drift.
focus
true marks the one node the diagram is about. Takes the brand colour.
fill
A hex colour for this one node, when the role is right and the colour is not.
value
A magnitude, for the chart kinds. A bar with no value is a box.
series
Further readings of the same thing: [before, after] for a slopegraph, one per snapshot for a bump.

Drawing fields

Four kinds are engineering drawings rather than figures — network, electrical, circuit and floorplan — and carry facts a figure does not. A drawing is 80% annotation: the model, the address, the rating, the cable, each in a fixed place. These fields hold them. They are ignored on every other kind.

attrs
The facts, as an ordered map in your discipline's words: { model: Catalyst 9300, addr: 10.0.99.2 }. Drawn under the name, one per line.
count
How many this one stands for. Twelve desktops are one stacked glyph and ×12, not twelve boxes.
icon
The device, named outright — router, switch, firewall, wifi, rack, nas, laptop… — for when the label is a hostname the matcher cannot read.
symbol
The electrical component this node IS: resistor, potentiometer, capacitor, capacitor-polarized, inductor, transformer, crystal, diode, zener, schottky, led, lamp, speaker, motor, heater, fuse, switch, battery, source-dc, source-ac, source-i, vcc, busbar, board, supply, meter, socket, breaker, rcd, isolator, earth, ground, test-point, antenna. Always beats the word match, because a wrong symbol is a different circuit.
at
[x, y] — where it is, for floorplan and circuit only, where position is the content. Bracketed: a bare 230,0 inside { } is read by YAML as 230 and a stray token. A circuit part needs nothing else: no size, no shape.
rotate
90 stands a circuit part upright. The wire then leaves its top and bottom rather than its flanks, which is what puts a shunt part between two rails instead of across one.
size
[w, h] — the rectangle a floor plan draws. A circuit does not need it; `rotate` says which way a part faces.
pins
On a LINK in a circuit, and required at any end that is a transistor, mosfet or op-amp: which pin it lands on, [from, to]. b/c/e for a transistor, g/d/s for a mosfet, p/n/out for an op-amp; "" at an end that is an ordinary two-lead part. A side is a direction and two pins can share one — on these parts only the pin is meaningful.
sides
On a LINK in a circuit: which lead each end joins, [from, to], as n, e, s or w. Two upright parts side by side share their TOP node — sides: [n, n] — and without it they are joined at the bottom, across the ground rail, which is a different circuit.

Links take three more, written as a mapping since the arrow shorthand cannot carry them: { from: sw, to: fw, directed: false, ports: [gi1/0/1, lan1], label: 1G, medium: fibre, pair: 2 }. ports is the interface at each end, in order, with "" where an end has none; medium is wired, fibre, wireless or vpn and is drawn as a stroke; pair says how many parallel links this stands for. Write network links with -- or directed: false — a LAN carries traffic both ways.

Zones take subtitle (a subnet, a VLAN, a board rating — drawn after the name) and style: zone | enclosure. On a network the zones are the tiers, top to bottom in the order declared; on an electrical drawing a zone styled enclosure is drawn as the board's boundary. A door on a floor plan is an edge between two rooms, placed where their rectangles touch; a junction on a schematic is derived wherever conductors meet. Neither is written by hand, so neither can be wrong.

Arrows

A relationship carries a kind, not just a line style, so "queries" and "returns" survive a round trip instead of collapsing to "arrow". Any connection may take : label.

a -> b
Primary flow. Solid, in the brand colour.
a ~> b
A call or a query — a request to something that answers.
a ..> b
A return or an async hand-off. Dashed, and it does not advance the reading order.
a -- b
A plain association with no direction.

A connection may also be written as a mapping — { from: a, to: b, kind: call, label: query } — which generators sometimes prefer. JSON is accepted wherever YAML is: JSON is what models emit most reliably, YAML is what people diff most comfortably, and both parse to the same document.

Versioning

The current version is 1.0, declared as dgm: 1.0 on the first line of every file we write.

  • Minor versions are additive. A reader on 1.0 must accept a 1.1 file and ignore what it does not recognise. Otherwise every field we ever add breaks every deployment that has not upgraded.
  • A major version is a promise being broken and is refused rather than guessed at, by name, in the error. We would keep reading the old major rather than orphan documents people have committed to a repository.
  • A file with no dgm: key is 1.0 and stays valid — the first version shipped before the key did.

Errors are repair instructions

The writer is usually a model fixing its own output, so a rejection names the thing that is wrong rather than the line number it is on: an edge pointing at an undeclared node is refused by that node's id, and a connection with no arrow is answered with the list of arrows. Nodes are a map keyed by id, so a duplicate id cannot be expressed at all.

Round trip

Every other artifact we produce is terminal: you get a picture and cannot get the diagram back, which is why a conversion carries a fidelity ledger at all. What we write as .dgm re-imports to the same drawing, and the writer is idempotent, so the file in a repository stops changing after one pass. Convert a .vsdx once, keep the .dgm, and never open Visio again.

Convert a file and download one · API reference