This is the full developer documentation for Diagrist
# Diagrist
> Diagrams as code, checked like code.
Diagrist turns small text files (`.dgr`) into diagrams. The language is strict on purpose: every statement, argument and attribute is listed in a table, a check names what it found and what is allowed, and the same file always gives the same SVG, byte for byte. So a diagram can be reviewed, diffed and fixed like code, by people who keep diagrams next to their code in git and by coding agents.
* Picture
Sign in. Sequence diagram (UML) with 3 participants and 5 messages. Participants from left to right: User (actor), :App, :AuthService. 1. User calls :App: open(). 2. :App calls :AuthService: login(name, password). Alternative \[valid]: 3. :AuthService replies to :App: token. Otherwise \[invalid]: 4. :AuthService replies to :App: error. 5. :App replies to User: page.
* Source
sign-in.dgr
```dgr
# A user signs in: the app asks the auth service, which answers with a token or an error.
diagram sequence "Sign in" subtitle="Valid or invalid credentials"
actor user "User"
participant app ":App"
participant auth ":AuthService"
msg user -> app "open()"
msg app -> auth "login(name, password)"
alt "valid"
msg auth --> app "token"
else "invalid"
msg auth --> app "error"
msg app --> user "page"
```
[Open in the editor ↗](https://live.diagrist.com/#v1:bZBBasQwDEX3PoVQNzMw6QECHcgZhnavJiIRk8iu5EyuX2xPoZTupC99-X2_wAC7s4HLrA6iPeSFgVIC8ru3Zs8LONtDRr7Asci4AKkfbA6H5AUIcryzQjQgBTaL9homodloA-evnXVkwJvMCqIIvn9mySu_4QetMhWf6KOWo_HEmoVWx0Bjjtbw8N3ZMCSyLKMk0lwZsR9S-iMXWuyHPS-3xoxh87md6a7NFhPr6dwGRSh69a1xFj0pbXyBRO5HtOmMgdYMWAkxAFRXWe9-7tX8ZcSrM-AzTRH-264_9Ovx7voMmWhmDN8)
Strict on purpose
A mistake is reported with its position, a stable code and what would be correct. Rules check the meaning too, such as a call that never gets a reply.
Same file, same picture
The output is deterministic down to the byte, so a picture only changes in a pull request when its source does.
Made for agents
The grammar of every kind and every finding come as JSON, and an agent skill tells an agent how to look things up, write, check and fix.
Stays on your machine
The command line runs locally. The live editor compiles in your browser and keeps the source in the address, so no diagram reaches a server.
## UML, C4 and code
[Section titled “UML, C4 and code”](#uml-c4-and-code)
The UML kinds are laid out by Diagrist itself. C4 diagrams place their elements in a named grid that the file declares, so the author decides what stands next to what; a `flow` numbers the steps of a path, and an `adr` explains a mark in a bar below the picture.
* Picture
Bike rental. Context diagram (C4 L1) with 6 elements in 0 groups. Without group: Rider, Service crew, Bike rental, Stations, Payment provider, Map service. Flow ride: (1) Rider → Bike rental: unlocks a bike, App \[synchronous] (2) Bike rental → Stations: opens the lock, MQTT \[synchronous] (3) Bike rental → Payment provider: charges the ride, HTTPS \[synchronous] Flow upkeep: (4) Stations → Bike rental: reports battery levels, MQTT \[asynchronous] (5) Bike rental → Service crew: plans jobs, App \[synchronous] Further edge: Bike rental → Map service: shows free bikes, HTTPS. E1 Who controls the locks? Chosen: The rental itself. Lock logic and firmware stay in one hand.
* Source
bike-rental.dgr
```dgr
# Bike rental, C4 level 1: the system as a black box with the people who use it and the systems around it.
diagram context "Bike rental" subtitle="System context, draft for discussion"
grid cols people,core,partners
grid rows top,main,bottom
person rider "Rider" tech="rents by app, pays per minute" at people/main
person service "Service crew" tech="moves and repairs bikes" at people/bottom
system rental "Bike rental" tech="app, rides, billing" at core/main emph mark=E1
system stations "Stations" tech="locks and chargers" at partners/top
system payments "Payment provider" tech="card and direct debit" at partners/main
system maps "Map service" tech="locations and routes" at partners/bottom
flow ride accent
rider -> rental "unlocks a bike" via=App
rental -> stations "opens the lock" via=MQTT
rental -> payments "charges the ride" via=HTTPS
flow upkeep primary
stations ~> rental "reports battery levels" via=MQTT
rental -> service "plans jobs" via=App
edge rental -> maps "shows free bikes" via=HTTPS
adr E1 "Who controls the locks?" = "The rental itself" => "Lock logic and firmware stay in one hand."
```
[Open in the editor ↗](https://live.diagrist.com/#v1:bZNBb9swDIXv_hUP2tVtUWCnAemwDgV2WIFuCbAzLTOxZlsSSDlpLvvtg2W7cYr6JMDk4-NH8hMeXcsQ9om6Et8_o-Mjd7j_gtQw9KyJe5CCUHVkW1ThFSeXmvw7cogd49QEDMpwCeTrVaKCJAy-hku3Re3oINTDBp_4NcGsKhvoUCWXOt6Y7VRzDitRC-0T9kFQO7WDqgveFAdxNWzodDZR2iBcRpLkWXT6LeGkSCGWPTlfViGl0BdFZNHgIa5mAQDze3wZrL7EttmY0ZqiOoNiLBHpPNYS9M4PiQ1Aaa59N-ovuspydJYBs51fVvhkVrp9OLJmUsKRnCgq17K-ObjozpbnKUyoRsdX5FbK2ejYmJaoXNc5f7jqixJGTNkvuI8NepJ283S_lNBEyQWvMNv5Zd5B6YJtJ_O2ITmw6FWFbH4ewl0KcRGOdO4zTvMyvRAlHCfwk7AlqbNu7YRtQs2VS-Zj4cx7Vu4pag4wzxQX-uad5bmrzDwMia9Mr5WXJdl34ZRJgqxlnwosC3PzsBrE4GceeYQGR0ebbzGO0VPMzcOKaYjsNZ_HmDVFP__a7a7CL6QmwFPCWHxK-LHbvWxng0NsmSOiuJ7kXOBS69_FpRGOQcZNppRYztOF60flc3fLApvYkVf8DZVeGiu4PvDKbaZvtBkvbS_MyyqvnFIteLqH-dOEfNQyHu0CQb8abGB2zZuoS8rd3mDzAPMz2BZdODibR7d30p9IeGzzDOcRPKMhX9-a4j8)
A mistake looks like this:
```text
order.dgr:4:29 L105 error: `participant` has no attribute `stereotyp`. Allowed: `stereotype=`, `tone=`.
participant pay ":Payments" stereotyp="external"
^^^^^^^^^^^^^^^^^^^^
```
[Gallery](/gallery/)Every example of the repository, ready to change in the live editor.
[Getting started](/docs/getting-started/)Build Diagrist, write a first file, check and build it.
[The language](/docs/language/)How a file is written, every kind, every diagnostic code.
[Live editor and embedding](/docs/editor/)Write in the browser, embed a diagram in Confluence.
[For agents](/docs/agents/)Grammar and findings as JSON, the agent skill, llms.txt.
[Live editor](https://live.diagrist.com/)live.diagrist.com: write and share a diagram in the browser.
# For agents
> How a coding agent works with Diagrist - the grammar and findings as JSON, the agent skill, llms.txt and every page as Markdown.
Diagrist is written for coding agents as much as for people ([ADR 0005](https://github.com/diagrist/diagrist/blob/main/docs/adr/0005-agent-first-language.md)). Every statement, argument, attribute and flag has a description and an example, and the pages, the JSON and the skill are generated from the same tables that check the diagrams, so they cannot drift from what Diagrist accepts.
## The loop
[Section titled “The loop”](#the-loop)
An agent works the way a person does, only faster: look the grammar up, write the file, check it until nothing is reported, format it and build it.
```sh
dgr grammar sequence --json # every statement of the kind as data
dgr check order.dgr --json # every finding as data
dgr fmt order.dgr
dgr build order.dgr --png # look at the picture
```
`dgr grammar --json` prints each statement with its arguments, clauses, attributes, flags, doc and examples. The same data is on this site as `/grammar/.json`, such as [/grammar/sequence.json](/grammar/sequence.json), and it validates against the [JSON Schema](https://github.com/diagrist/diagrist/blob/main/schemas/grammar.schema.json).
`dgr check --json` reports each finding with its code, position and the values its message is built from, so a tool does not have to parse text:
```json
{
"diagrist-cli": 1,
"command": "check",
"ok": false,
"file": "order.dgr",
"diagnostics": [
{
"file": "order.dgr",
"code": "L105",
"severity": "error",
"span": { "line": 4, "column": 29, "length": 20 },
"values": { "statement": "participant", "key": "stereotyp", "allowed": "`stereotype=`, `tone=`" },
"message": "`participant` has no attribute `stereotyp`. Allowed: `stereotype=`, `tone=`."
}
]
}
```
## The agent skill
[Section titled “The agent skill”](#the-agent-skill)
The skill tells an agent how to work with Diagrist and holds a short reference of every kind. It is at [/skill.md](/skill.md); put it where your agent looks for skills, for Claude Code for example:
```sh
mkdir -p .claude/skills/diagrist
curl -o .claude/skills/diagrist/SKILL.md https://diagrist.com/skill.md
```
## The docs as text
[Section titled “The docs as text”](#the-docs-as-text)
[/llms.txt](/llms.txt) lists the pages of this site for language models, [/llms-full.txt](/llms-full.txt) holds all of them in one file and [/llms-small.txt](/llms-small.txt) a shorter version. Every page is also available as Markdown at its address plus `.md`, such as [/docs/getting-started.md](/docs/getting-started.md).
# Command line
> The Diagrist command line: format, check and build .dgr files, and look up the grammar of a kind.
The Diagrist command line: format, check and build .dgr files, and look up the grammar of a kind.
Installed as `dgr` and `diagrist`. Usage: `dgr [arguments] [options]`; the command comes first.
| Command | What it does |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`fmt`](#dgr-fmt) | Formats .dgr files in place. |
| [`check`](#dgr-check) | Checks a diagram against the grammar and the rules of its kind and prints every finding, those about one picture of a file with variants marked with the variant. |
| [`build`](#dgr-build) | Renders a diagram as SVG, and with `--png` also as PNG: the base picture as `.svg` and each variant as `..svg`. |
| [`grammar`](#dgr-grammar) | Shows what a diagram kind allows. |
| [`help`](#dgr-help) | Explains the commands. |
## Options for every command
[Section titled “Options for every command”](#options-for-every-command)
| Option | Meaning |
| ------------------- | --------------------------------------------------------------------------------------------------------------- |
| `--json` | Print one JSON document on stdout instead of text, for agents and tools. Its form is stable, see “JSON output”. |
| `--locale ` | Language of messages and diagnostics. Default: `en`. |
| `-h, --help` | Show the help of the command instead of running it. |
Without a command:
| Option | Meaning |
| ----------- | ----------------------------- |
| `--version` | Print the version of the CLI. |
## Exit codes
[Section titled “Exit codes”](#exit-codes)
| Code | Meaning |
| ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0 | Done: no finding with severity error. Warnings alone do not fail. |
| 1 | A finding with severity error, or `fmt --check` found a file that is not formatted. |
| 2 | The call cannot be carried out: unknown command or option, missing or extra argument, a value out of range, unreadable or unwritable file, a PNG that cannot be rendered. Also an unexpected error inside dgr, which is a bug and printed in one line on stderr. |
## dgr fmt
[Section titled “dgr fmt”](#dgr-fmt)
```sh
dgr fmt [options]
```
Formats .dgr files in place. Two spaces per indent level and at most one blank line; token order, aligned columns and comments stay as written. A file with read errors is left unchanged, because formatting it could drop lines.
| Argument | Meaning |
| ------------ | ------------------------- |
| `` | The .dgr files to format. |
| Option | Meaning |
| --------- | ------------------------------------------------------------------------------------ |
| `--check` | Change nothing; exit with 1 if a file is not formatted. For CI and pre-commit hooks. |
Examples:
```sh
# Format one file in place.
dgr fmt diagrams/order.dgr
# Fail when a file is not formatted, without changing it.
dgr fmt --check diagrams/*.dgr
```
With `--json`, besides the fields of every document:
| Field | Meaning |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `files` | One entry per file, `{ file, status }`. `status` is `formatted` (rewritten), `unchanged` (already formatted), `unformatted` (only with `--check`: formatting would change it) or `invalid` (has read errors, left unchanged). |
| `diagnostics` | Read errors and warnings of all files, see “Diagnostics”. |
## dgr check
[Section titled “dgr check”](#dgr-check)
```sh
dgr check [options]
```
Checks a diagram against the grammar and the rules of its kind and prints every finding, those about one picture of a file with variants marked with the variant. Writes nothing.
| Argument | Meaning |
| -------- | ----------------------- |
| `` | The .dgr file to check. |
Examples:
```sh
# Print the findings; exit with 1 on an error.
dgr check diagrams/order.dgr
# The same as one JSON document, for agents.
dgr check diagrams/order.dgr --json
```
With `--json`, besides the fields of every document:
| Field | Meaning |
| ------------- | -------------------------------- |
| `file` | The checked file. |
| `diagnostics` | All findings, see “Diagnostics”. |
## dgr build
[Section titled “dgr build”](#dgr-build)
```sh
dgr build [options]
```
Renders a diagram as SVG, and with `--png` also as PNG: the base picture as `.svg` and each variant as `..svg`. Nothing is written when the diagram has an error; warnings are printed and do not stop the build. With `--watch` it builds again after every change of the source or of a file it reads, through `use` or a value naming a file.
| Argument | Meaning |
| -------- | ------------------------ |
| `` | The .dgr file to render. |
| Option | Meaning |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-o, --out ` | Where to write the SVG of a diagram with one picture, or of the one `--variant` picks. Default: next to the source, with `.svg` instead of `.dgr`. The PNG goes beside the SVG. |
| `--variant ` | Build only the picture of this variant, as `..svg` or where `-o` says. |
| `--png` | Also write a PNG with the name of the SVG and `.png`. Rendered without a browser and with the bundled fonts, so the same diagram gives the same bytes on every machine. |
| `--scale ` | Pixels of the PNG per SVG unit, from 0.25 to 8; 2 doubles width and height. Default: 2. Needs `--png`. |
| `--watch` | Build, then build again each time the source or a file it reads changes, until Ctrl+C. Each build prints what it wrote and its findings, with `--json` one document per build. Errors in the diagram and a source that is gone for a moment do not end the watch; only a call refused at the first build does. Ends with the exit code of the last build. |
Examples:
```sh
# Write diagrams/order.svg, and one SVG per variant beside it.
dgr build diagrams/order.dgr
# Write the SVG somewhere else.
dgr build diagrams/order.dgr -o site/order.svg
# Also write diagrams/order.png, twice the size of the SVG.
dgr build diagrams/order.dgr --png
# Write the PNG at three times the size of the SVG, e.g. for print.
dgr build diagrams/order.dgr --png --scale 3
# Write only diagrams/order.next.svg.
dgr build diagrams/order.dgr --variant next
# Write SVG and PNG again on every save.
dgr build diagrams/order.dgr --png --watch
```
With `--json`, besides the fields of every document:
| Field | Meaning |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `file` | The rendered source. |
| `output` | Path of the first written SVG, the base unless `--variant` picks another; `null` when nothing was written. |
| `png` | Path of the PNG beside `output`, `null` without `--png` or when nothing was written. |
| `pictures` | Every picture asked for: `{ variant, output, png }`, `variant` `null` for the base, the paths `null` when nothing was written. |
| `diagnostics` | All findings, see “Diagnostics”. |
## dgr grammar
[Section titled “dgr grammar”](#dgr-grammar)
```sh
dgr grammar [kind] [options]
```
Shows what a diagram kind allows. It lists the header, statements, arguments, attributes, flags and line forms, each with its doc and examples. Without a kind it lists the kinds. Derived from the same tables that check the diagrams.
| Argument | Meaning |
| -------- | ---------------------------------- |
| `[kind]` | A diagram kind such as `sequence`. |
Examples:
```sh
# List the diagram kinds.
dgr grammar
# The grammar of sequence diagrams as data, for agents.
dgr grammar sequence --json
```
With `--json`, besides the fields of every document:
| Field | Meaning |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kinds` | Without a kind: `[{ kind, doc }]` for every kind. |
| `grammar` | With a kind: its grammar as data, `{ kind, doc, header, statements }`; each statement lists `keyword`, `doc`, `examples`, `arguments`, `clauses`, `attributes`, `flags`, `children` and its line form `lines`. Each table is described once; elsewhere a child is `{ keyword, ref }`, where `ref` is a JSON Pointer into `grammar` such as `/statements/9`. It validates against the JSON Schema in `schemas/grammar.schema.json`. |
## dgr help
[Section titled “dgr help”](#dgr-help)
```sh
dgr help [command] [options]
```
Explains the commands. Without a command it lists them all, with the options every command takes and the exit codes.
| Argument | Meaning |
| ----------- | -------------------------------------- |
| `[command]` | The command to explain, such as `fmt`. |
Examples:
```sh
# List the commands.
dgr help
# Arguments, options and examples of `fmt`; the same as `dgr fmt --help`.
dgr help fmt
# All command tables as data, for agents.
dgr help --json
```
With `--json`, besides the fields of every document:
| Field | Meaning |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `commands` | The tables of the explained commands: `{ name, doc, usage, arguments, options, examples, json }`, all of them without a command. |
| `options` | Options every command takes, in the same form as the options of a command. |
| `rootOptions` | Options of `dgr` without a command. |
| `exitCodes` | `[{ code, doc }]`. |
## JSON output
[Section titled “JSON output”](#json-output)
With `--json` every call prints exactly one JSON document on stdout, also when it is refused. Fields in every document:
| Field | Meaning |
| -------------- | ------------------------------------------------------------------------------------------------------------- |
| `diagrist-cli` | Version of this JSON form, currently `1`. It is raised only when a field changes meaning or goes away. |
| `command` | The command that ran, `null` for `dgr --version` and when no command was recognized. |
| `ok` | `true` exactly when the exit code is 0. |
| `error` | Only when the call was refused (exit code 2): `{ code, message }`, with a code `C…` that is stable for tools. |
| `version` | Only for `dgr --version`: the version of the CLI. |
The fields of each command are listed in its section.
### Diagnostics
[Section titled “Diagnostics”](#diagnostics)
| Field | Meaning |
| ---------- | ----------------------------------------------------------------------------------------------- |
| `file` | Path of the file the finding is in: the source as given on the command line, or a file it uses. |
| `picture` | Only for a finding about one picture of a file with variants: the id of the variant. |
| `code` | Stable code such as `L001`; the prefix names the package or plugin that reports it. |
| `severity` | `error` (exit code 1) or `warning`. |
| `span` | `{ line, column, length }`: 1-based line and column, length in characters. |
| `values` | The values the message is built from, by name. Use these instead of parsing the message. |
| `message` | The text in the requested locale. |
# Live editor and embedding
> Write a diagram in the browser at live.diagrist.com, share it as a link and embed it in Confluence or any page with an iframe.
The [live editor](https://live.diagrist.com/) shows the code on the left and the picture on the right, drawn again as you type. Findings appear below the code and as marks in it, with the same codes and texts as `dgr check`. Every kind of the docs can be drawn.
## Nothing reaches a server
[Section titled “Nothing reaches a server”](#nothing-reaches-a-server)
The editor compiles in your browser, in a Web Worker, and draws the PNG there too, with resvg compiled to WebAssembly. The source is kept in the address, behind the `#`: compressed with deflate and written as base64url, like `https://live.diagrist.com/#v1:…`. Browsers never send the part after the `#` to a server, not even as the referrer, so the diagram stays on your machine. Nothing is stored, and there are no accounts and no analytics.
The address is the document. Bookmark it, put it in a ticket or send it to a colleague, and the diagram opens as you left it. The PNG and SVG the editor downloads are the same bytes the command line writes.
## Share and download
[Section titled “Share and download”](#share-and-download)
The bar above the editor has these buttons (the editor speaks German for now):
| Button | What it does |
| ------------- | --------------------------------------------------------------------------- |
| Link kopieren | Copies the address with the source, to share the diagram. |
| Einbetten | Opens the address and the HTML for an iframe, see below. |
| SVG, PNG | Download the picture. |
| .dgr | Downloads the source, to keep it next to your code and build it with `dgr`. |
## Embed a diagram
[Section titled “Embed a diagram”](#embed-a-diagram)
Every diagram has a second address, `https://live.diagrist.com/embed#v1:…`, that shows only the picture, scaled to the frame, with a link back to the editor. **Einbetten** gives you that address and an HTML snippet:
```html
```
In Confluence, type `/iframe`, paste the address and set a height. Any other page that allows iframes takes the HTML as it is. Only the embed page may be framed by other sites; the editor itself may not.
The embedded picture is drawn in the reader’s browser from the address, so it changes only when you replace the address. To change a diagram, open it with the link below the picture, edit it, and embed the new address.
# Getting started
> Build Diagrist from source, write a first .dgr file, check it, build the picture and keep it checked in CI.
Diagrist is not on npm yet, so for now it is built from source. You need Node 22.11 or later and pnpm 11; Corepack picks the pnpm version from `package.json`.
```sh
git clone https://github.com/diagrist/diagrist.git
cd diagrist
pnpm install
pnpm build
alias dgr="node $PWD/packages/cli/dist/main.js"
```
To try the language without installing anything, use the [live editor](https://live.diagrist.com/): it compiles in the browser and shows the picture as you type.
## A first diagram
[Section titled “A first diagram”](#a-first-diagram)
A `.dgr` file starts with `diagram`, the kind and a title. Each following line is one statement that starts with its keyword; statements inside a block, like the operands of `alt`, are indented below it. Save this as `order.dgr`:
Place an order. Sequence diagram (UML) with 3 participants and 5 messages. Participants from left to right: Customer (actor), :Shop, :Payments. 1. Customer calls :Shop: order(items). 2. :Shop calls :Payments: authorize(amount). Alternative \[approved]: 3. :Payments replies to :Shop: ok. Otherwise \[declined]: 4. :Payments replies to :Shop: declined. 5. :Shop replies to Customer: confirmation.
```dgr
diagram sequence "Place an order"
actor customer "Customer"
participant shop ":Shop"
participant pay ":Payments"
msg customer -> shop "order(items)"
msg shop -> pay "authorize(amount)"
alt "approved"
msg pay --> shop "ok"
else "declined"
msg pay --> shop "declined"
msg shop --> customer "confirmation"
```
[Open in the editor ↗](https://live.diagrist.com/#v1:bY9LCsMwDET3PoXQKlnkAl1k0wsEegLhqIlpbLmyUkhPX_KBtNCdmHkzg_pAg1KEws-Zk2fAbiLPQAlEe1Z05E0U_FxMIivg9bjQZVILPmRKBmWUDHi5jZJ_jUwL4KWjJXKygi6W4Sxr2iO4bVXBOJZ6Zza9afc8zTaKhjdXFGVOVqOjyQApZ5UX9-gA1tAKN2fpY9V5KgzYs59C2sl_7Omf40379baXdA8ayYIk_AA)
Which statements a kind has, and what each takes, is on the page of the kind, such as [sequence](/docs/language/sequence/), and in the terminal:
```sh
dgr grammar sequence
```
## Check it
[Section titled “Check it”](#check-it)
`dgr check` reads the file against the grammar of its kind and runs the rules of the kind. Every finding names its position, a stable code and what would be correct:
```text
order.dgr:4:29 L105 error: `participant` has no attribute `stereotyp`. Allowed: `stereotype=`, `tone=`.
participant pay ":Payments" stereotyp="external"
^^^^^^^^^^^^^^^^^^^^
```
Errors stop the build. Warnings point at something that is drawn but probably not meant, such as a call that never gets a reply, and the picture is still drawn.
## Build the picture
[Section titled “Build the picture”](#build-the-picture)
```sh
dgr build order.dgr # writes order.svg
dgr build order.dgr --png # also order.png
dgr build order.dgr --watch # builds again on every save
```
The PNG is drawn by resvg compiled to WebAssembly with the bundled fonts, so it needs no browser and gives the same bytes on every machine.
## Format it
[Section titled “Format it”](#format-it)
`dgr fmt` formats files in place: two spaces per indent level, at most one blank line, everything else as you wrote it. `--check` changes nothing and fails when a file is not formatted.
```sh
dgr fmt diagrams/*.dgr
```
## In CI
[Section titled “In CI”](#in-ci)
`dgr check` exits with 1 when the file has an error, and `dgr fmt --check` when a file is not formatted. Run both in the pipeline that also runs your tests, and a broken diagram cannot be merged. `check` takes one file at a time:
```sh
dgr fmt --check diagrams/*.dgr
for file in diagrams/*.dgr; do dgr check "$file" || exit 1; done
```
The [command line reference](/docs/cli/) lists every command, option and exit code, and the form of the JSON output.
# The Diagrist language
> A .dgr file holds one diagram. Its first line names the kind and the title, diagram sequence "Checkout", followed by the attributes of the header. Every other line is a statement: a keyword, then its arguments in the order the table gives, then flags, clauses and key=value attributes in any order.
A `.dgr` file holds one diagram. Its first line names the kind and the title, `diagram sequence "Checkout"`, followed by the attributes of the header. Every other line is a statement: a keyword, then its arguments in the order the table gives, then flags, clauses and `key=value` attributes in any order.
Texts stand in double quotes and may contain `\"`. Ids are single words of letters, digits, `_` and `-` that start with a letter; a statement declares an id, and other statements refer to it by that word. An attribute value is a single word or a text in quotes, as in `stereotype="web app"`.
Indentation nests: a line belongs to the nearest line above it with less indent, so the statements of a block such as a fragment are indented below it. `dgr fmt` writes two spaces per level and keeps at most one blank line. `#` starts a comment that runs to the end of the line.
## Kinds
[Section titled “Kinds”](#kinds)
Each kind has its own page with every statement, its parts, examples and the codes of its rules.
| Kind | Plugin | What it draws |
| ---------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`sequence`](/docs/language/sequence/) | `uml` | A UML sequence diagram (UML 2.5.1, chapter 17): lifelines from left to right in source order, messages and fragments from top to bottom. |
| [`usecase`](/docs/language/usecase/) | `uml` | A UML use case diagram (UML 2.5.1, chapter 18): actors outside, use cases inside their subject, relations as straight lines. |
| [`activity`](/docs/language/activity/) | `uml` | A UML activity diagram (UML 2.5.1, chapters 15 and 16): nodes and edges in layers from top to bottom, one vertical swimlane per `partition`. |
| [`class`](/docs/language/class/) | `uml` | A UML class diagram (UML 2.5.1, chapters 9 to 12): classes, interfaces and enumerations with their members, packages, and the relations between them. |
| [`state`](/docs/language/state/) | `uml` | A UML state machine diagram (UML 2.5.1, chapter 14): states and pseudostates joined by transitions, composite states with substates or orthogonal regions, entry and exit points on their border. |
| [`context`](/docs/language/context/) | `architecture` | A C4 system context diagram (level 1): the system as one box, the people who use it and the systems around it, placed in a named grid. |
| [`container`](/docs/language/container/) | `architecture` | A C4 container diagram (level 2): the applications and data stores of a system, grouped by responsibility, placed in a named grid. |
| [`component`](/docs/language/component/) | `architecture` | A C4 component diagram (level 3): the components inside a container and what they depend on, placed in a named grid. |
| [`code`](/docs/language/code/) | `code` | A code diagram: excerpts of real source files as highlighted cards, one below the other, with marked lines, numbered notes below each card and arrows from a call to what it calls. |
## Statements of every kind
[Section titled “Statements of every kind”](#statements-of-every-kind)
Every kind also has `use`, `variant`, `set` and `drop`. A file holds one base picture and any number of variants: a `variant` block repeats only the lines that differ, so as-is and to-be, phases or option A and B stay one source. A statement of the kind inside a variant adds a line, `set` changes the clauses, attributes and flags of a declared line, `drop` removes one. `from` builds a variant on one declared above it, and `diff` draws it as a comparison with what it builds on, if the kind can (C4 can).
Where a line goes: `set` keeps the line in its place, and a line declared again after its `drop` takes the dropped line’s place. A new line goes before the next line of its keyword that the variant declares again, otherwise after the last line of its keyword, so lines keep the order they are written in. The grid and `at` are inherited, so an element keeps its cell in every picture.
`use "file.dgr"` puts the lines and variants of another file of the same kind before the file’s own, so a decision paper can build on a shared base. Paths are relative to the file, stay inside the folder `dgr` runs in and name no hidden file. `dgr build` writes the base as `.svg` and each variant as `..svg`; `--variant` builds one.
### `use`
[Section titled “use”](#use)
```text
use
```
Puts the statements and variants of another `.dgr` file of the same kind before this file’s own, as if they were written here, so a variant may build on them. The other file’s header is not taken. `use` lines stand directly after the header.
Written at the top level.
| Part | Kind | Value | Meaning |
| -------- | -------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `` | argument | a text in double quotes like `"Order service"` | Path of the file relative to this one. It stays inside the folder `dgr` runs in and names no hidden file or folder. |
Example:
```dgr
use "radverleih.dgr"
```
### `variant`
[Section titled “variant”](#variant)
```text
variant [from ] [subtitle=] [diff]
```
One more picture from the same base, written as the lines that differ: a statement of the kind adds a line, `set` changes one and `drop` removes one. A new line goes before the next line of its keyword the variant declares again, else after the last line of its keyword, so lines keep the order they are written in. `dgr build` writes it as `..svg`.
Written at the top level. Indented below it: `set`, `drop`.
| Part | Kind | Value | Meaning |
| ------------ | --------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `` | argument | an identifier like `api` (letters, digits, `_` and `-`, starting with a letter) | Id of the variant, part of the file name of its picture. |
| `` | argument | a text in double quotes like `"Order service"` | Title of its picture, in place of the title of the header. |
| `from ` | clause | the id of a `variant` declared in this diagram | A variant declared above to build on; without it the variant builds on the base. |
| `subtitle=` | attribute | a text in double quotes like `"Order service"` | Subtitle of its picture, in place of the one of the header. |
| `diff` | flag | | Draws the picture as a comparison with what it builds on: added, removed and changed lines marked, with a legend. Kinds that cannot draw it report K103. |
Examples:
```dgr
variant target "Target 2027" subtitle="For review" diff
variant a "Option A: stream through the backend" from target
```
### `set`
[Section titled “set”](#set)
```text
set
```
Changes the line that declares `id`, where it stands. What follows the id is checked against that line’s own table: a clause or attribute written again replaces its value, a flag written turns it on. Arguments such as the label cannot be set: drop the line and declare it again.
Written only inside `variant`.
| Part | Kind | Value | Meaning |
| ------ | -------- | -------------------------------------------------------------------- | ----------------------------- |
| `` | argument | the id a line at the top level of the picture declares, such as `db` | The id of the line to change. |
Examples:
```dgr
set db tech="PostgreSQL 17"
set portal status=deprecated
```
### `drop`
[Section titled “drop”](#drop)
```text
drop [arguments]
```
Removes one line of the picture: its keyword and as many of its first arguments as make it unique, matched from the left. It must match exactly one line, and no line left may refer to an id it removes. A line declared again after its `drop` takes its place. Lines inside a block are not dropped singly: drop the block and declare it again.
Written only inside `variant`.
| Part | Kind | Value | Meaning |
| ------------- | ----------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `` | argument | a single word without quotes | The keyword of the line, such as `node`. |
| `[arguments]` | optional argument | the first words, texts and operators of the line as written, such as `backend -> portal` | Its first arguments as written, as many as make it unique. |
Examples:
```dgr
drop node portal
drop edge backend -> portal
drop grid rows
```
## The grammar as data
[Section titled “The grammar as data”](#the-grammar-as-data)
`dgr grammar --json` prints the grammar of a kind as data, derived from the same tables as these pages. It validates against the JSON Schema [`schemas/grammar.schema.json`](https://github.com/diagrist/diagrist/blob/main/schemas/grammar.schema.json). Editors and the language server ask `complete` from `@diagrist/lang` what may be written at a position; it reads the same tables.
## Diagnostic codes
[Section titled “Diagnostic codes”](#diagnostic-codes)
Every finding has a stable code whose letter names who reports it: `L` the language, `K` and `G` the kernel, and one letter per plugin, such as `U` for `uml`. The codes of a kind are on its page. The messages below show each value as `{name}`; with `--json` the same values come as `values`, so tools need not parse the text.
The language reports these while it reads a file against the tables of its kind:
| Code | Message |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `L001` | Text is missing its closing quote. |
| `L002` | A line starts with a keyword, not with {found}. |
| `L003` | Indent with spaces, not tabs. |
| `L004` | Attribute {key} is set twice; the last value wins. |
| `L005` | The character {found} cannot stand in a diagram: the picture is SVG, and XML has no way to write it. Delete it; tabs, line breaks and every printable character are allowed. |
| `L006` | Statements nest at most {limit} levels deep, and this one would be deeper; it and the lines indented below it are not read. Move it up a level or split the diagram. |
| `L100` | A diagram starts with `diagram {kind} "Title"`. Registered kinds: {allowed}. Example: `{example}` |
| `L101` | Unknown keyword `{found}` here; did you mean `{suggestion}`? Allowed: {allowed}. |
| `L102` | `{statement}` needs {name}: {expected}. Example: `{example}` |
| `L103` | `{found}` is not valid for {name}; expected {expected}. |
| `L104` | `{found}` is not expected in `{statement}`. Allowed after the arguments: {allowed}. Example: `{example}` |
| `L105` | `{statement}` has no attribute `{key}`. Allowed: {allowed}. |
| `L106` | `{statement}` takes no indented lines without a keyword. Allowed child keywords: {allowed}. |
| `L107` | `{found}` is not declared; {name} must be {expected}. Declared: {known}. |
| `L108` | The id `{found}` is already declared in line {first}. Ids are unique within a diagram. |
| `L109` | `use` stands directly after the header, before `{found}` in line {line}. Example: `use "base.dgr"` |
| `L110` | `{file}` is a `{found}` diagram; `use` in a `{kind}` diagram needs a `{kind}` diagram. |
| `L111` | `{file}` uses itself through {chain}. Remove one `use`. |
| `L112` | `drop {found}` matches no line of the picture `{variant}` builds on. Lines with keyword `{keyword}`: {known}. Example: `{example}` |
| `L113` | `drop {found}` matches the lines {lines}. Add arguments until exactly one matches, for example `{example}`. |
| `L114` | `{id}` is dropped in variant `{variant}`, but {name} of this line still refers to it. Drop this line too, or keep `{id}`. |
| `L115` | `set {id}` changes nothing. Write at least one attribute, clause or flag of `{keyword}`: {allowed}. Example: `set {id} {example}` |
| `L116` | `variant {found}` builds on `{from}`, which is declared later in line {line}. Declare `{from}` first. |
| `L117` | `{found}` is already declared in line {first}. In a variant, change it with `set {found} ...` or remove it with `drop {keyword} {found}`. |
| `L118` | `{file}` was not handed over, so it cannot be read. Hosts read every file a diagram needs before they compile it; `dgr` does so and reports C018 when it cannot. |
| `L119` | `{found}` is no allowed path: {case}. A path is relative to the diagram, stays inside the folder `dgr` runs in and names no hidden file or folder. Example: `"../shared/base.dgr"` |
| `L120` | `{file}` would be the {depth}th file in a chain of `use`, and a chain holds at most {max}. Use the file that holds the lines directly, such as `use "../shared/base.dgr"`. |
The kernel reports these about kinds, icons and the placement of what the layout drew:
| Code | Message |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `G400` | `{first}` overlaps `{second}`. Shorten a text, or change what the layout places here. |
| `G401` | `{key}` reaches outside the picture. |
| `G402` | `{key}` does not fit into `{within}`. Shorten the text or give `{within}` more room. |
| `G403` | `{key}` reaches into the frame `{frame}` without belonging to it, so the picture says it is part of it. Make `{key}` a member of `{frame}`, or split the frame. |
| `K100` | No plugin draws the diagram kind `{found}`. Registered kinds: {allowed}. A diagram starts with `diagram "Title"`. |
| `K101` | The kind `{kind}` is registered twice, by {plugins}; the first one is used. |
| `K102` | The picture uses the icon `{icon}`, which the style table does not have. Icons: {known}. |
| `K103` | The kind `{kind}` draws no diff pictures; remove `diff` from `variant {variant}`. Kinds with diff pictures: {allowed}. |
| `K104` | A text of the picture reads `{text}`, but its runs read `{runs}`. This is a bug of the plugin, not a mistake in the diagram: the runs of a text join to its text. |
| `K110` | The plugin `{plugin}` failed while drawing the kind `{kind}`: {error}. This is a bug, not a mistake in the diagram; please report it with the diagram at . |
# activity
> A UML activity diagram (UML 2.5.1, chapters 15 and 16): nodes and edges in layers from top to bottom, one vertical swimlane per partition. Diagrist lays it out by itself: layers by the longest path, alternatives symmetric below their decision, parallel branches side by side below their fork, a hand-over into another lane level and horizontal when its line is free, loops back up on the right.
A UML activity diagram (UML 2.5.1, chapters 15 and 16): nodes and edges in layers from top to bottom, one vertical swimlane per `partition`. Diagrist lays it out by itself: layers by the longest path, alternatives symmetric below their decision, parallel branches side by side below their fork, a hand-over into another lane level and horizontal when its line is free, loops back up on the right.
This page lists everything an `activity` diagram may contain. The same grammar is available as data with `dgr grammar activity --json`, and [the overview](/docs/language/) explains how lines, texts and attributes are written and lists the codes every kind shares.
## The first line
[Section titled “The first line”](#the-first-line)
```text
diagram activity "Title" [subtitle=] [look=] [locale=]
```
Every activity diagram starts with this line.
| Part | Kind | Value | Meaning |
| ----------- | --------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `subtitle=` | attribute | a text in double quotes like `"Order service"` | Second line below the title. |
| `look=` | attribute | one of `plain`, `cards` | `plain` (default) draws the UML notation, `cards` the same layout with actions, objects and signals as cards and toned lane heads; start, ends, diamonds, bars and hourglass stay UML symbols. |
| `locale=` | attribute | one of `en`, `de` | Language of the texts Diagrist adds to the picture and of the description: `en` (default) or `de`. |
Example:
```dgr
diagram activity "Title"
```
## Statements
[Section titled “Statements”](#statements)
Each statement is one line that starts with its keyword. Statements inside blocks are indented below them. Like every kind, `activity` also has `use`, `variant`, `set` and `drop`, described in [the overview](/docs/language/#statements-of-every-kind).
| Statement | Where | What it does |
| --------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`partition`](#partition) | top level | A partition, drawn as a vertical swimlane with its name in a head. |
| [`interruptible`](#interruptible) | top level | An interruptible activity region: a dashed, rounded frame around the nodes with `interruptible=id`. |
| [`expansion`](#expansion) | top level | An expansion region: a dashed, rounded frame around the nodes with `expansion=id`, run once per element of a collection, with its mode as `«iterative»`, `«parallel»` or `«stream»` at the top left. |
| [`initial`](#initial) | top level | An initial node: a filled circle where the flow starts. |
| [`final`](#final) | top level | An activity final node: a circle with a filled core. |
| [`flowfinal`](#flowfinal) | top level | A flow final node: a circle with a cross. |
| [`action`](#action) | top level | An action: a rounded box with its text; texts wider than 208 pixels wrap into two lines. |
| [`decision`](#decision) | top level | A decision node: a diamond with one incoming edge and alternatives going out, each with a guard on its `go`. |
| [`merge`](#merge) | top level | A merge node: a diamond that brings alternative branches together without waiting for each other, with exactly one outgoing edge. |
| [`fork`](#fork) | top level | A fork node: a bar across the flow that starts all its outgoing branches at the same time. |
| [`join`](#join) | top level | A join node: a bar across the flow that waits for all incoming branches and then continues on its one outgoing edge. |
| [`object`](#object) | top level | An object node: a rectangle with the object type, its state below in brackets. |
| [`datastore`](#datastore) | top level | A data store: an object node with `«datastore»` above its name, whose data outlive the activity. |
| [`buffer`](#buffer) | top level | A central buffer: an object node with `«centralBuffer»` above its name that collects objects from several flows. |
| [`send`](#send) | top level | A send signal action: a pentagon pointing right with the signal. |
| [`accept`](#accept) | top level | An accept event action: a box with a notch on the left that waits for a signal. |
| [`wait`](#wait) | top level | An accept time event action: an hourglass with the time beside it; the flow waits there until the time has come. |
| [`go`](#go) | top level | An activity edge from `from` to `to`. |
| [`handler`](#handler) | top level | An exception handler: when the protected action throws an exception of the type, the handler action takes over, and the flow goes on at the output of the protected action. |
| [`adr`](#adr) | top level | An architecture decision in the bar below the picture: question, the chosen option after `=`, and the consequence after `=>`. |
### `partition`
[Section titled “partition”](#partition)
```text
partition