This page is the reference for every engine in flowss that software engineers, architects, security reviewers and hardware designers reach for: the general diagram-as-code languages (Mermaid, PlantUML, Graphviz, D2, Nomnoml, Pikchr), the engines that check behaviour (Sequence & interaction, State machine, Petri Nets), the architecture notations (Structurizr, Cloud architecture, ArchiMate, SysML v2, nwdiag, Server Rack), data models (DBML, ERD), domain and threat modelling (Data flow diagram, Event storming), protocols and hardware (Packet & bit fields, packetdiag, Bytefield, WaveDrom, Symbolator, WireViz, Railroad), text-art and sketch engines (SVGBob, Ditaa, Excalidraw), graph networks (Cytoscape) and profiling (Flame Graph). For each one you get what it draws, a minimal working example you can paste into the Studio, the syntax and options that matter, what flowss checks for you, and a link to the engine's own reference page at /docs/engines/ followed by its id.
At a glance
| Engine | Use it for | Where it is drawn | Reference |
|---|---|---|---|
| Mermaid | Flowcharts, sequence, class, ER, state, C4, mind maps and 30-plus other types in one familiar syntax | In your browser | mermaid |
| PlantUML | Strict UML and many specialty diagrams | A PlantUML server | plantuml |
| Graphviz (DOT) | Any graph, DAG or dependency tree with fine layout control | In your browser | graphviz |
| D2 | Clean architecture diagrams with nested containers | In your browser | d2 |
| Nomnoml | Sketchy, whiteboard-style UML | In your browser | nomnoml |
| Pikchr | Compact documentation diagrams from an algebra of boxes and arrows | Outside render service | pikchr |
| Sequence & interaction | Sequence diagrams checked against the call stack | Inside flowss | sequence |
| State machine (FSM) | Finite state machines checked for reachability and determinism | Inside flowss | statemachine |
| Petri Nets | Concurrency, shared resources and synchronisation, executed | Inside flowss | petri |
| Structurizr DSL | C4 models with several views of one model | Outside render service | structurizr |
| Cloud architecture | AWS, Azure, Google Cloud and Kubernetes, checked | Inside flowss | cloudarch |
| ArchiMate | Enterprise architecture to ArchiMate 3.2 | Inside flowss | archimate |
| SysML v2 (structure) | Parts, ports and interfaces in the SysML v2 textual notation, checked | Inside flowss | sysml |
| Network (nwdiag) | Network topology with subnets and addresses | Outside render service | nwdiag |
| Server Rack | Rack elevations with occupancy, power and weight checked | Inside flowss | rack |
| DBML | Relational schemas, dbdiagram.io-compatible | Inside flowss | dbml |
| ERD | Minimal entity-relationship diagrams | Outside render service | erd |
| Data flow diagram | Gane–Sarson DFDs and STRIDE-style trust boundaries | Inside flowss | dfd |
| Event storming | Domain-driven design workshop boards | Inside flowss | eventstorm |
| Packet & bit fields | Protocol headers and register maps, checked | Inside flowss | packet |
| Packet diagrams (packetdiag) | Column-based packet layouts | Outside render service | packetdiag |
| Bytefield | RFC-style byte layouts in a Clojure-flavoured language | Outside render service | bytefield |
| WaveDrom | Digital timing diagrams, logic trees and register fields | Inside flowss | wavedrom |
| Symbolator (HDL) | Block symbols from VHDL or Verilog declarations | Outside render service | symbolator |
| WireViz | Cable harnesses with a bill of materials | Outside render service | wireviz |
| Railroad / syntax diagram | Grammars and API syntax, json.org style | Inside flowss | railroad |
| SVGBob (ASCII) | ASCII art turned into clean lines | Outside render service | svgbob |
| Ditaa (ASCII) | ASCII art with colour fills | Outside render service | ditaa |
| Excalidraw | Hand-drawn whiteboard scenes from Excalidraw JSON | Outside render service | excalidraw |
| Cytoscape (Network) | Large knowledge, dependency and biological networks | In your browser | cytoscape |
| Flame Graph | CPU and performance profiles from folded stacks | Inside flowss | flamegraph |
Every engine on this page works on every plan, including Free. Only the optional AI features (describing a diagram in words, AI conversion between engines, Explain & fix, automatic repair and Fix on a readiness finding) need AI: there is no AI on Free, your own AI key works from Starter, and hosted AI points start on Plus. Outside-service engines also have a daily render allowance, described below. See Plans and what they include.
Working with these engines
Opening an engine
- In the Studio, click Engines & templates at the top of the tool rail (or press
/), or the engine name at the top of the code sheet. The Library flyout opens with two tabs, Engines and Templates; pick the engine from the Engines tab. Search works by name, by job ("sequence", "threat model", "rack") and by the tool you are replacing ("Visio", "dbdiagram"). - From any engine reference page, click Open in Studio. It opens the Studio with that engine selected, which is the same as visiting
/studio?engine=followed by the engine id. - From the template gallery, filter by engine. Add
?engine=and an id to the gallery address to open it pre-filtered, for example/templates?engine=dbml.
Clicking an engine in the library replaces the current figure's source with that engine's starter (its first template). There is no confirmation, but the switch is recorded as one undo step, so ⌘Z (Ctrl+Z on Windows and Linux) brings your previous source back. To keep a diagram and move it to another engine, use Convert to another engine… instead, or start a New figure (⌥⌘N) first. See Choosing an engine.
What each engine reference page shows
Each page at /docs/engines/ followed by an id shows the engine's description and what it is compatible with (for example "Compatible with Mermaid 11"), four facts (the number of Templates, the Source language, the Workstation it opens in and its Engine id), an In the same space line naming the tools people usually come to it from, a Syntax at a glance sample where one exists, and up to six Sample templates with an All link to the rest. The main button reads Open in followed by the workstation's name, which for every engine on this page is the Studio (Open in Studio). Engines that follow an outside notation also show an Upstream docs button that opens that notation's own documentation in a new tab; the per-engine sections below give the same address.
Where the picture is drawn
| Group | Engines on this page | What it means for you |
|---|---|---|
| Inside flowss | Sequence, State machine, Petri Nets, Cloud architecture, ArchiMate, SysML v2, Server Rack, DBML, Data flow diagram, Event storming, Packet & bit fields, WaveDrom, Railroad, Flame Graph | Works offline. The source never leaves flowss. The render API can draw it on a server, so thumbnails, previews and CI renders all work |
| In your browser | Mermaid, Graphviz, D2, Nomnoml, Cytoscape | Works offline in the browser. The headless render API cannot draw these. Use the Studio, or an embed for every one of them except Cytoscape |
| Outside render service | PlantUML, Pikchr, Structurizr DSL, nwdiag, ERD, packetdiag, Bytefield, Symbolator, WireViz, SVGBob, Ditaa, Excalidraw | Needs a connection, and the source is sent to the service to be drawn. A line under the code sheet header says so: "Drawn by Kroki, a third-party render service: this source is sent to it." or, for PlantUML, "Drawn by a PlantUML server (the public plantuml.com unless self-hosted): this source is sent to it." |
Outside-service renders are counted per day: 300 on Free and Starter, 1,000 on Plus, 2,500 on Ultra and 5,000 on Enterprise, reset at midnight UTC. PlantUML and the other services are counted separately. Visitors who are not signed in share an allowance of 300 a day per network address (and per service) and are asked to sign in when it runs out. Each settled edit counts as a render (the Studio waits for a short pause in your typing before it sends one); an identical source is remembered while the tab stays open and is not sent again, so undo and redo cost nothing. The largest source accepted is 30,000 characters for PlantUML and 100,000 characters for the other services.
Outside-service renders pass through flowss on their way to the service: your browser talks only to flowss, never to the render service directly. The source is not stored or logged on the way through. See Privacy and your data.
Embeds and the render API. An embedded figure (see Embedding and the render API) draws every engine on this page that is drawn inside flowss, plus Mermaid, PlantUML, Graphviz, D2, Nomnoml, Pikchr and SVGBob. Cytoscape, Structurizr, nwdiag, ERD, packetdiag, Bytefield, Symbolator, WireViz, Ditaa and Excalidraw are not drawn in an embed: the embed shows a placeholder card with an Open in workstation button instead, so for those engines publish an exported SVG (see Exporting your work). Embeds of outside-service engines are counted as anonymous renders against the viewer's network address. The headless render API draws only the engines drawn inside flowss: it refuses an in-browser engine with the code needs-browser and an outside-service engine with needs-render-service.
Warning: If your source is confidential, prefer an engine drawn inside flowss or in your browser. DBML instead of ERD, D2 or Mermaid C4 instead of Structurizr, and Packet & bit fields instead of packetdiag or Bytefield keep the source on your machine.
Size budgets
Before it draws, the Studio checks the size of the source against a budget for the engine, so a pasted giant cannot freeze the page. A source over budget is not drawn, and the figure explains why.
| Engines | Budget (bytes of source) |
|---|---|
| Mermaid | 50,000 |
| Graphviz, D2 | 250,000 |
| PlantUML | 30,000 |
| Pikchr, Structurizr, nwdiag, ERD, packetdiag, Bytefield, Symbolator, WireViz, SVGBob, Ditaa, Excalidraw | 100,000 |
| Nomnoml, Cytoscape | 100,000 |
| Sequence, State machine, Cloud architecture, ArchiMate, SysML v2, Server Rack, Packet & bit fields | 300,000 |
| Flame Graph | 150,000 (it grows with the profile, one line per stack) |
| Petri Nets, DBML, Data flow diagram, Event storming, WaveDrom, Railroad | 60,000 |
Bytes are counted in UTF-8, so accented letters and symbols count as two or more. The message reads "Source is … — past the … safe-rendering budget for … Rendering this in the browser would freeze the page." (sizes shown in KB) and suggests splitting the figure into smaller figures, rendering it on a server through the render API, or trimming the source. Most native engines also have their own, smaller caps on what they draw (listed under Limits and known constraints); those caps are reached first and the figure names which one was hit.
Checks, diagnostics and readiness
flowss reads the source of every engine on this page and checks it, in three places:
- Inside the figure. The native engines (Sequence, State machine, Cloud architecture, SysML v2, Server Rack, Packet & bit fields and others) print a headline and a findings list under the drawing, and mark the elements they are about. Exports carry these findings.
- The Diagnostics strip under the code sheet runs line-level checks for Mermaid flowcharts and DBML schemas, and appears for any engine the moment a render fails. Each finding with a line number jumps the editor to that line.
- The readiness chip in the Studio's status panel. Every engine on this page has a checker that feeds it, so a document whose checks fail cannot read as Sound. Open the chip's detail to see what is Blocking or Holding it at a level, and use Fix beside a finding to hand just that one to the flowss Studio Agent.
An engine that could not read a line never presents its analysis as complete. It lists the line, and verdicts such as "clean" are withheld until the whole document is read. See Flow-systems analysis in Studio for the shared behaviour and the readiness levels.
Formatting and conversion
Format source (on the code sheet, or ⇧⌥F in the editor) re-indents Mermaid, PlantUML, Graphviz, D2, DBML, Structurizr, nwdiag, packetdiag, WaveDrom, Cytoscape and Excalidraw sources. The JSON engines (WaveDrom, Cytoscape, Excalidraw) are re-indented token by token, so every number and string keeps exactly the digits you wrote. Formatting is deliberately unavailable for engines whose whitespace is part of the drawing, such as SVGBob, Ditaa, WireViz, ERD, Pikchr, Nomnoml, Symbolator and Bytefield. When it cannot format safely it leaves the source alone and says why:
| Message | Why |
|---|---|
| "Formatting left the source alone — braces or strings do not balance yet, and a formatter that guesses would corrupt the diagram." | A brace or quote is still open |
| "Mermaid … diagrams are laid out by their indentation — formatting is disabled so the structure stays yours." | Mermaid mindmap, timeline, gantt, sankey-beta, block-beta, packet-beta, C4Context, C4Container, C4Component, C4Deployment and architecture-beta |
| "This Mermaid sub-type has no formatter yet — the source was left alone." | A Mermaid diagram type the formatter does not model. The formatter handles flowcharts, sequenceDiagram, classDiagram, stateDiagram, erDiagram, requirementDiagram, journey, pie, quadrantChart, xychart-beta and gitGraph; everything else (for example C4Dynamic, kanban or treemap) is left alone |
| "PlantUML … blocks are laid out by their own text — formatting is disabled so the layout stays yours." | @startsalt, @startditaa, @startjson or @startyaml |
| "This D2 file uses block strings — formatting is disabled so their contents stay exact." | The D2 source contains a block string (a value opened with a vertical bar, such as a Markdown label) |
| "Invalid JSON: …" | A WaveDrom, Cytoscape or Excalidraw source that does not parse |
Convert to another engine… offers instant conversions, with no AI and no AI points, between these pairs (listed under Instant at the top of the menu):
| From | Instant conversion to |
|---|---|
| Mermaid (flowcharts only) | D2, PlantUML |
| D2 | Mermaid |
| Graphviz | D2 |
| DBML | Mermaid ER, ERD |
After an instant conversion flowss measures what carried over. When everything did, the result is simply applied; when only styling was lost, the result is applied and a warning names what did not carry ("Converted with the built-in … transpiler, which could not carry …"); when a node, connection, label, group or field would be lost, the result is not applied, the menu shows the same sentence, and you are offered Convert with the flowss Studio Agent instead. The Mermaid instant conversions are offered only while the source is a flowchart (flowchart or graph); for any other Mermaid diagram type the menu lists only Convert with the flowss Studio Agent to…. Every other pair is listed under Convert with the flowss Studio Agent to…, a searchable list of every engine, and needs AI: hosted AI points (from Plus) or your own AI key (from Starter). See AI points and limits and Bring your own AI key. See Importing and converting.
General-purpose diagram languages
Mermaid
The most widely supported diagram-as-code language, and the Studio's default. flowss runs Mermaid 11, so any Mermaid snippet from a README, wiki or another tool pastes in unchanged.
Engine id mermaid · Drawn in your browser · Reference Mermaid · Upstream docs https://mermaid.js.org/intro/
flowchart LR
A([Order received]) --> B{Paid?}
B -->|yes| C[Pick and pack]
B -->|no| D[Send reminder]
D --> B
C --> E([Shipped])
The first word of the source chooses the diagram type:
| First line | Diagram |
|---|---|
flowchart or graph with a direction (LR, RL, TB, TD, BT) | Flowchart |
sequenceDiagram | Sequence diagram |
classDiagram | Class diagram |
stateDiagram-v2 | State diagram |
erDiagram | Entity-relationship diagram |
C4Context, C4Container, C4Component, C4Dynamic, C4Deployment | C4 architecture |
architecture-beta | Cloud and service architecture with icons |
block or block-beta | Block diagram |
packet or packet-beta | Packet layout |
requirementDiagram | SysML-style requirements |
gitGraph | Git branching |
gantt | Gantt chart (bars only, see Good to know) |
timeline | Timeline |
journey | User journey |
kanban | Kanban board |
mindmap | Mind map |
quadrantChart | 2×2 quadrant chart |
pie | Pie chart |
xychart or xychart-beta | Bar and line chart |
sankey or sankey-beta | Sankey |
radar-beta | Radar chart |
treemap | Treemap |
venn-beta | Venn diagram |
wardley-beta | Wardley map |
ishikawa | Fishbone |
eventmodeling | Event model |
cynefin-beta | Cynefin framework |
swimlane-beta | Swimlane |
treeView-beta | Tree view |
railroad-beta, railroad-ebnf-beta, railroad-abnf-beta, railroad-peg-beta | Railroad grammar |
Types ending in -beta are Mermaid's own experimental diagrams and may change as Mermaid evolves.
Options. A front-matter block at the top of a diagram can ask for the ELK layout, which gives cleaner orthogonal routing on large flowcharts:
---
config:
layout: elk
---
flowchart LR
A --> B --> C
If the layout module cannot load, the diagram falls back to Mermaid's default layout. Diagrams render with Mermaid's strict security level, so click callbacks and raw HTML in labels are not executed. The Studio's theme and styling controls apply on top; see Styling, themes and layout.
What flowss checks. The Diagnostics strip lints Mermaid flowcharts:
| Finding | Severity |
|---|---|
| A node that cannot be reached from any start node | Warning |
| The graph splits into disconnected pieces | Warning |
| A node with no outgoing edges that is not drawn as a terminal shape | Info |
| A node used in edges but never given a label, so it renders as its bare id | Info |
| An edge declared more than once (it renders as overlapping arrows) | Info |
| A node with an edge to itself | Info |
| The source is not a flowchart, so there is nothing to lint | Info |
The readiness checker reads further: a decision with only one labelled way out, a decision whose branches are partly labelled, an id given two different labels (Mermaid keeps one silently), a bare id that is a case variant of a labelled one, a process that starts and never ends, sankey nodes that send out more than they take in, pie titles whose stated total the slices do not reach, quadrant points outside 0 to 1, Gantt after references to tasks that do not exist, unreachable states and missing [*] starts in state diagrams, unbalanced activations in sequence diagrams, class-name typos and inheritance cycles, ER entities with no attribute block, and mind-map indentation that does not nest. Diagram types it has no checks for are reported as unchecked, never as clean.
Good to know.
- Mermaid draws at most 500 connections in a flowchart. A bigger diagram shows "Mermaid draws at most 500 connections, and this diagram has more. Nothing in it is wrong — it is larger than Mermaid will lay out. Split it into smaller figures, or draw it with Graphviz or Weave."
- Mermaid has the richest click-to-edit support: all of the Diagram tools (Delete, Duplicate, Add after, Add child, Colour, Bigger) work on it. See Data, timeline, TikZ and the diagram tools.
- Explain — prose & alt text in the Export menu writes a plain-language description and an alt-text sentence from Mermaid source, with no AI.
- Mermaid's
ganttandsequenceDiagramdraw what they are given. For a schedule with a computed critical path use Gantt & critical path, and for a sequence checked against its call stack use Sequence & interaction below.
PlantUML
The classic UML language, with more than twenty specialty diagram types, rendered by a PlantUML server.
Engine id plantuml · Drawn by a PlantUML server · Reference PlantUML · Upstream docs https://plantuml.com/
@startuml
actor User
participant "Web app" as Web
database DB
User -> Web : log in
activate Web
Web -> DB : find user
DB --> Web : user row
alt password ok
Web --> User : session cookie
else wrong password
Web --> User : 401
end
deactivate Web
@enduml
PlantUML covers class, sequence, use case, activity, state, component, deployment, object, timing, mind map, WBS, Gantt, Salt wireframes, JSON and YAML views, EBNF and regex diagrams, ditaa, cloud architecture sprites (AWS, Azure, Google Cloud, Kubernetes, Elastic), ArchiMate, EIP and C4. Wrap a document in @startuml and @enduml. The PlantUML server still draws a source with no @start line, reading it as if it were wrapped, but the readiness check reports the missing @startuml, so add the pair.
What flowss checks. A structural reader catches what usually breaks a PlantUML document before the server sees it: @startuml and @enduml that do not pair up, an empty document, a block (such as alt, loop, if, fork, box) opened and never closed or closed without being opened, activations that never close, an activity diagram that starts and never stops, participants created on the spot by a message rather than declared, and !include content that is not in the document and so cannot be checked. Server syntax errors are reported as "PlantUML syntax error on line N: …" with the line highlighted.
Good to know. PlantUML is the one engine on this page drawn by a dedicated PlantUML server rather than in flowss or your browser. It has its own daily render allowance (the same numbers as the other outside services) and accepts at most 30,000 characters. The direct Diagram tools do not edit PlantUML; use the Edit label popover or Ask the flowss Studio Agent….
Graphviz (DOT)
The industry-standard graph language. Use it when layout control matters more than style: DAGs, dependency graphs, call graphs, finite automata and trees.
Engine id graphviz · Drawn in your browser · Reference Graphviz (DOT) · Upstream docs https://graphviz.org/documentation/
digraph Services {
rankdir=LR;
node [shape=box, style="rounded,filled", fillcolor="#eef2ff"];
subgraph cluster_edge {
label="Edge";
cdn [label="CDN"];
lb [label="Load balancer"];
}
api [label="API"];
db [label="Postgres", shape=cylinder];
cdn -> lb -> api;
api -> db [label="SQL"];
}
Graphs are laid out with Graphviz's hierarchical dot layout, in a background worker so a large graph does not freeze the editor. A layout that takes longer than 20 seconds is stopped with "Graphviz layout timed out — the graph may be too complex"; the next edit starts a fresh layout. Use rankdir (TB, LR, BT, RL), node [...] and edge [...] defaults, subgraph cluster_name { ... } for boxed groups, rank=same for alignment and the usual node shapes, colours and styles.
What flowss checks. DOT ignores attributes it does not understand without a word, so flowss reads the source and reports the silent mistakes:
- A chained statement's attributes apply to every edge in the chain, not just the last:
a -> b -> c [label="x"]draws "x" twice. - A node named after a DOT keyword (such as
node,edgeorgraph, in any case) is read as a defaults statement rather than as a node. fillcolorwithoutstyle=filledis kept and never used.- An edge naming a record port the record does not declare.
- One id given two different labels (DOT keeps the last), and a bare id that is a case variant of a labelled one.
- An attribute set on the kind of object that never reads it.
lheadorltailwithoutcompound=true.- A labelled subgraph whose name does not start with
cluster, so its box is never drawn. strictmerging two declarations of one edge.
Good to know. Delete, Colour, rename and Ask the flowss Studio Agent… work on Graphviz from the Diagram tools. Graphviz converts instantly to D2.
D2
A modern declarative language for architecture diagrams, with concise arrows and nested containers.
Engine id d2 · Drawn in your browser · Reference D2 · Upstream docs https://d2lang.com/
direction: right
users: Users {shape: person}
cloud: AWS {
lb: Load balancer
api: API
db: Postgres {shape: cylinder}
lb -> api
api -> db: queries
}
users -> cloud.lb: HTTPS
cloud.db.style.fill: "#fef3c7"
flowss draws D2 by translating it to Graphviz and laying it out locally, so the source never leaves your browser. It supports the D2 most people write:
| Syntax | Meaning |
|---|---|
a -> b, a <- b, a <-> b, a -- b | Arrow, reverse arrow, two-way arrow, plain line |
a -> b -> c | A chain; every hop is drawn |
a -> b: label | A labelled connection |
name: Title | A node with a display label |
name: Title {shape: cylinder} or name.shape: cylinder | A shape |
name.style.fill: "#hex" and name.style.stroke: "#hex" | Fill and stroke colours |
group: Title { ... } | A container, nested to any depth |
group.child | A path into a container, including edges into it |
direction: right | Layout direction: right (default), left, down or up |
# comment | A comment |
Supported shape values are rectangle, square, oval, ellipse, circle, diamond, hexagon, parallelogram, cylinder, queue, page, document, step, callout, stored_data, cloud, person and package. Several are drawn as the nearest equivalent: cloud as an ellipse, person as an egg shape, page, document and callout as a folded note, stored_data as a cylinder, queue as a divided record box, step as a house shape and package as a tabbed folder.
What is not drawn. D2 features beyond that subset are named in a caption under the figure rather than approximated: layers, scenarios, steps, classes, vars, imports, grid-* layouts, edge references such as (a -> b)[0], attribute blocks on connections, and style keys other than fill and stroke (for example opacity, font-size, stroke-dash, shadow, 3d). A shape with no equivalent is drawn as a dashed box and named.
Some valid D2 is read as something else, and the readiness check says so rather than passing it:
| You write | What happens |
|---|---|
A multi-line attribute block (db: Postgres {, then shape: cylinder on its own line, then }) | It is read as a container holding a box captioned with the value. Use the one-line form db: Postgres {shape: cylinder} or db.shape: cylinder |
| A multi-line block on a connection | The arrow is not drawn at all |
| A block string (a value opened with a vertical bar, such as a Markdown label) | The box is captioned with the delimiter, and the lines inside are drawn as boxes of their own |
A selector such as *.style.stroke or _ | It is drawn as a box named * or _, and nothing is selected |
direction: inside a container | It turns the whole figure, not just that container: there is one layout direction per figure |
Two D2 shapes that share one drawn equivalent (for example cylinder and stored_data) | Both are drawn the same, and the check notes it |
What flowss checks. Connectors with nothing usable on one side, a container opened and never closed, an invalid direction (it declares a box captioned with the value instead), empty containers, one name drawn both as a box and as a container, ids that differ only by case, edges into an empty container or from a node to itself, colours that are not valid, and a label, shape or colour stated twice (only the last is drawn).
Good to know. D2 converts instantly to Mermaid. Block strings disable Format source so their contents stay exact. The Diagram tools do not edit D2; use the Edit label popover or Ask the flowss Studio Agent….
Nomnoml
Sketchy, hand-drawn-looking UML from a very small language. Popular for talks and papers.
Engine id nomnoml · Drawn in your browser · Reference Nomnoml · Upstream docs https://nomnoml.com/
#direction: right
[<actor>Customer] -> [Order]
[Order|id: int; total: decimal|place(); cancel()]
[Order] +-> [OrderLine]
[<abstract>Payment] <:- [CardPayment]
[Order] -> [<abstract>Payment]
- A classifier is a name in square brackets. Compartments are separated by
|, and members inside a compartment by;. [<type>Name]chooses a built-in style:abstract,actor,choice,class,database,end,frame,hidden,input,instance,label,lollipop,note,package,pipe,receiver,reference,sender,socket,start,state,sync,table,transceiverandusecase.- Common associations:
->association,-->dependency,-:>generalisation,--:>implementation,+->composition,o->aggregation,--note link,-/-hidden. - Directives start at column 0 with
#:#direction: right,#fill,#stroke,#font,#fontSize,#spacing,#padding,#gutter,#edges: hard,#background,#title,#zoom,#ranker,#arrowSize,#bendSize,#lineWidth,#leading,#gravity,#edgeMargin,#acyclicer: greedyand#fillArrows. Custom styles are declared with#.name: ...and used as[<name>Title].
What flowss checks. A parse error (Nomnoml draws nothing at all when it meets an operator it does not know, such as <>), a body line that starts with # and is therefore swallowed as a directive, one name drawn as two boxes because it was written inside and outside a package, a <type> with no style (it falls back to a plain class box), unknown or repeated directives, a custom #.name: style asking for a visual= that does not exist or that no classifier uses, #import: lines (they are never followed), and the four types (start, end, hidden, sync) that draw an icon and discard their title.
Good to know. Nomnoml supports Delete, Duplicate, Add after, Add child and Colour in the Diagram tools.
Pikchr
A small language from the author of SQLite for documentation diagrams: boxes, arrows, ovals, cylinders and splines placed by a simple algebra.
Engine id pikchr · Drawn by an outside render service · Reference Pikchr · Upstream docs https://pikchr.org/
box "Client" fit
arrow right 200% "HTTPS" above
box "API" fit
arrow right 200% "SQL" above
cylinder "Postgres" fit
Give an object a label to refer to it later (C: box "Client", then arrow from C.east). Labels start with an upper-case letter.
What flowss checks. A reference to a label that was never declared, with the nearest declared label suggested, a label declared twice (references above the second declaration reach the first object, references below reach the second), a label declared and never used (a note, not a fault), and a document that draws no objects.
Behaviour and interaction
Sequence & interaction
UML sequence diagrams that are checked against the call stack they describe. Use it when an interaction is a specification someone will build from.
Engine id sequence · Drawn inside flowss · Reference Sequence & interaction
sequence "Checkout"
actor Shopper
participant Web "Storefront"
participant Orders "Order service"
database DB "Orders DB"
Shopper -> Web : place order
Web -> Orders : POST /orders
Orders -> DB : insert order
DB reply Orders : order id
alt on payment.status : "ok", "declined"
when "ok"
Orders reply Web : 201 created
when "declined"
Orders reply Web : 402 payment required
end
Web reply Shopper : confirmation
Messages state what they mean. -> is a synchronous call, the word reply is a return and the word async is a fire-and-forget. Nothing is read off an arrowhead.
| Statement | Meaning |
|---|---|
sequence "Title" or title "Title" | The figure's title |
participant, actor, database, boundary, control, entity, queue, collections followed by an id and an optional quoted label | Declares a lifeline. Add concurrent after the label to mark a service that can safely handle parallel requests |
A -> B : label | A call |
B reply A : label | A reply |
A async B : label | A fire-and-forget message |
A create B "Label" | A creates lifeline B |
A destroy B, or destroy B | Ends lifeline B with a cross |
activate X, deactivate X | Activation bars, checked against the call stack |
alt "label" or alt on subject : "v1", "v2" … when guard … end | Alternatives. Each when (or else) opens a branch. With on, the subject and its domain let flowss check the guards |
opt, loop, break, critical … end | Other fragments. Their body starts straight away; opt may also take an else |
par … branch … end | Parallel branches, each opened by branch |
ref over A, B : "Name" | A reference to another interaction, treated as opaque |
note over A, B : text, note left of A : text, note right of A : text | Notes |
divider "text" or separator "text" | A horizontal divider |
# comment | A comment, only at the start of a line |
What flowss checks. Seventeen checks, listed under the figure: a reply returned to a participant that never made the call, a reply with no call, a call answered on only one branch of an alt, a call never answered, a deadlock (a cycle of calls none of which can be answered), a message to a lifeline after its destruction or before its creation, a lifeline created or destroyed twice, two par branches addressing one service that is not marked concurrent, two alt guards that accept the same input, a band of inputs no branch handles, activation claims that disagree with the call stack, unused participants, lifelines used but never declared, duplicate ids and fragments never closed.
Good to know.
- Arrows imported from Mermaid or PlantUML (such as
->>,-->>,-x) are drawn, but because they do not say whether they are calls or replies, the stack checks stop there and say the interaction is unchecked rather than guessing. - A message to an undeclared lifeline is never dropped: the lifeline is created, drawn and reported.
- An unclosed fragment stays open in the figure (drawn with an open bottom edge) and is reported.
loop 100000is a label, never an instruction to repeat rows.
State machine (FSM)
Finite state machines with reachability and determinism decided, not just drawn.
Engine id statemachine · Drawn inside flowss · Reference State machine (FSM)
title "Order lifecycle"
state Idle
state Pending label "Awaiting payment"
state Paid
state Shipped
state Delivered final
state Cancelled final label "Cancelled by customer"
initial Idle
transition Idle -> Pending on "place_order"
transition Pending -> Paid on "payment" [funds ok] / "send receipt"
transition Pending -> Cancelled on "cancel"
transition Paid -> Shipped on "dispatch"
transition Shipped -> Delivered on "deliver"
| Statement | Meaning |
|---|---|
state ID | A state. Add final and label "Text" in either order |
initial ID | The initial state |
transition A -> B on "event" | A transition. Add [guard] and / "action" after the event |
title "Text" | The title |
Ids may contain hyphens (in-progress, on-hold), or be quoted to contain spaces. Guards may contain brackets, such as [queue[0] != null]. # and // start a comment outside quotes.
What flowss checks. A transition naming a state that was never declared (the arrow is still drawn and reported), an initial naming an undeclared state, nondeterminism (two transitions leaving one state on the same event with nothing to tell them apart), duplicate transitions or states, two states whose boxes read the same, states that cannot be reached from the initial state, non-final states with no way out, a machine in which no final state can be reached, and a machine with no initial state. Every arrow the document writes is drawn, and none is drawn through a box.
Petri Nets
Places, transitions and tokens, executed rather than drawn: the net is explored and its behaviour reported.
Engine id petri · Drawn inside flowss · Reference Petri Nets
title Two charging bays
place Free tokens 2 "Free bays"
place Waiting tokens 3 "Cars waiting"
place Charging "Charging"
place Done "Charged"
transition Start "Start charging"
transition Finish "Finish"
Waiting -> Start
Free -> Start
Start -> Charging
Charging -> Finish
Finish -> Free
Finish -> Done
| Statement | Meaning |
|---|---|
place NAME [tokens N] ["Label"] [@ x,y] | A place, optionally with initial tokens and a fixed position |
transition NAME ["Label"] [@ x,y] | A transition |
NAME -> NAME [weight N] ["Label"] | An arc, optionally weighted. A chain such as a -> b -> c (spaced or not) is one arc per arrow: a to b, then b to c |
title text | The title |
Without coordinates, nodes are laid out by depth. If any node has @ x,y, given positions are used as written (one unit is about 90 pixels). A node used in an arc but never declared is created, with its kind inferred from the other end of the arc.
What flowss computes. Whether each place is bounded (by a coverability tree, so "unbounded" is a proof), the reachable markings, deadlocks (reachable markings where nothing can fire), dead transitions (transitions that can never fire), whether every transition can fire somewhere, the maximum tokens each place ever holds (1 means a safe place), and the conservation laws (P-invariants) the net always obeys, in your own place names. When a search reaches its limit, the affected answers are reported as not established rather than as passes. Diagram Intelligence (⇧⌘X) adds conflicts and clean-termination notes.
Architecture and infrastructure
Structurizr DSL
The workspace-based C4 language: define people, software systems, containers and components once, then declare several views of the same model.
Engine id structurizr · Drawn by an outside render service · Reference Structurizr DSL · Upstream docs https://docs.structurizr.com/dsl
workspace {
model {
customer = person "Customer"
shop = softwareSystem "Web shop" {
web = container "Web app" "Serves the storefront" "Next.js"
db = container "Database" "Orders and users" "PostgreSQL"
}
customer -> web "Browses and buys"
web -> db "Reads and writes"
}
views {
systemContext shop "Context" {
include *
autolayout lr
}
container shop "Containers" {
include *
autolayout lr
}
}
}
What flowss checks. No workspace, no model, an unclosed block, a relationship or view naming an identifier that was never declared (with the nearest declared name suggested), an identifier declared twice, a workspace with no views, a view with no include, elements declared without an identifier (no relationship or include can name them), and elements nothing connects to. Parts of the DSL this reader does not model, such as !include, styles, branding and deployment environments, are counted and listed as not judged rather than passed. If the workspace sets !identifiers hierarchical, flowss says it did not check references rather than checking them against the wrong set.
Tip: Structurizr sends its source to an outside service. For a confidential C4 model, Mermaid's C4Context family or D2 keeps the source local.
Cloud architecture
Cloud and platform architecture across AWS, Azure, Google Cloud and Kubernetes, with 331 resource types, real containment and typed connections, and checks on whether the architecture is true.
Engine id cloudarch · Drawn inside flowss · Reference Cloud architecture
title "Web shop"
provider aws
internet {
user customers "Customers"
}
cloud prod "Production" {
region eu-west-1 {
network vpc "Main VPC" cidr 10.0.0.0/16 {
alb lb "Load balancer"
zone eu-west-1a {
subnet app-a "App A" private cidr 10.0.1.0/24 {
ec2 web-a "Web server"
}
subnet data-a "Data A" isolated cidr 10.0.11.0/24 {
rds db "Orders DB" multi-az
}
}
}
}
}
customers -> lb : https 443
lb -> web-a : http 8080
web-a -> db : postgres 5432
This example deliberately produces one finding: the database claims multi-az but everything it has sits in one zone, eu-west-1a.
Containers use braces rather than indentation, nested up to eight deep: <kind> <id> ["Label"] [attributes] { … }.
| Container kind | Also accepted as |
|---|---|
cloud (account) | account, subscription, project |
region | |
network | vpc, vnet, virtual-network |
zone | az, availability-zone |
subnet | Takes public, private or isolated (or access public) |
cluster, namespace | |
group | rg, resource-group, sg, security-group |
onprem | dc, datacenter, on-prem, on-premises |
internet | public, www. internet { } needs no id |
Container attributes: cidr <range>, note "…", label "…", provider <name> and account, subscription or project values.
Resources are <type> <id> ["Label"] [attributes]. Types come from the document's provider (aws, azure, gcp, k8s or generic; amazon, microsoft, google and kubernetes work too). Examples: AWS ec2, lambda, s3, rds, aurora, dynamodb, alb, nlb, cloudfront, api-gateway, sqs, sns, eks; Azure vm, aks, functions, sql, cosmos, blob, front-door, key-vault; Google Cloud compute-engine, gke, cloud-run, cloud-sql, bigquery, pubsub, cloud-storage; Kubernetes pod, deployment, service, ingress, statefulset, configmap, secret, pvc; generic server, database, cache, queue, load-balancer, user, browser, mobile-app, saas, third-party-api, payment-provider. A type can be qualified with its provider, such as aws:rds or kubernetes:service. Resource attributes: count <n>, tier <word>, note "…", label "…", and the redundancy claims ha, multi-az, multiaz or replicated.
Connections are <from> <operator> <to> [: label] [protocol] [port] [via <id>]:
| Operator | Meaning |
|---|---|
-> | Request traffic |
<-> | Traffic both ways |
=> | Data movement |
~> | Asynchronous |
..> | Depends on |
-.> | Control plane |
<=> | Replication |
Word forms work too: calls, sends-to, reads, writes, depends-on, replicates-to, manages. <- and <= reverse the direction.
What flowss checks.
| Finding | Meaning |
|---|---|
| Exposed datastore | A stateful resource reachable from the internet along a traffic path with no ingress on it |
| Datastore in a public subnet | A stateful resource sitting directly in a public subnet |
| Single zone | Something claiming redundancy that sits in exactly one zone |
| Boundary crossed | A connection crossing a network boundary with no gateway named (via <id>) |
| Addressing | A subnet outside its own network, overlapping ranges, a prefix naming a host rather than a block, or non-routable space |
| Orphan | A resource connected to nothing |
| Undeclared endpoint | A connection naming an id that was never declared. It is kept and reported, never invented |
| Duplicate id, unknown type | Reported. An unknown type is still drawn, as a gap in the vocabulary rather than an error in your architecture |
A subnet with no public, private or isolated is treated as unstated, never silently as private, and the exposure check says which answers that cost it.
ArchiMate
Enterprise architecture to ArchiMate 3.2: business, application, technology, motivation, strategy, implementation and migration layers, with all eleven relationships drawn in the specification's line styles and end decorations.
Engine id archimate · Drawn inside flowss · Reference ArchiMate
title "Order to cash"
business-actor Customer "Customer"
business-process Handle "Handle order"
business-service OrderSvc "Order fulfilment"
business-object Order "Customer order"
layer application
component CRM "CRM system"
service CrmSvc "Customer data access"
layer technology
node AppSrv "Application server"
Customer associated-with Handle
Handle realizes OrderSvc
Handle accesses Order
CrmSvc serves Handle
CRM realizes CrmSvc
AppSrv serves CRM
Elements are <type> <id> ["Label"]. The layer comes from the type, so the band and the colour always agree. Full type names include stakeholder, driver, assessment, goal, outcome, principle, requirement, constraint, meaning, value (motivation); resource, capability, course-of-action, value-stream (strategy); business-actor, business-role, business-collaboration, business-interface, business-process, business-function, business-interaction, business-event, business-service, business-object, contract, representation, product (business); application-component, application-collaboration, application-interface, application-function, application-interaction, application-process, application-event, application-service, data-object (application); node, device, system-software, technology-collaboration, technology-interface, path, communication-network, technology-function, technology-process, technology-interaction, technology-event, technology-service, artifact, equipment, facility, distribution-network, material (technology); work-package, deliverable, implementation-event, plateau, gap (implementation and migration); grouping and location. Two-word spellings (business process) work too, and shorthands such as actor, role, component, data, object, network, software, action, valuestream, workpackage and group are accepted. layer <name> only sets the default for bare words that exist in several layers: process, service, function, event, interface, collaboration and interaction. A fully qualified type always wins.
Relationships are <source> <relationship> <target> [: label], by name or operator. Operators need spaces around them.
| Relationship | Line in the figure | Words | Operators |
|---|---|---|---|
| Composition | Solid, filled diamond at the source | composed-of, composes, consists-of | *->, *-- |
| Aggregation | Solid, hollow diamond at the source | aggregates, groups | o->, o-- |
| Assignment | Solid, ball at the source, arrow at the target | assigned-to, performs, deployed-on | @-> |
| Realization | Dotted, hollow triangle | realizes, realises | See below |
| Serving | Solid, open arrow | serves, used-by, provides-to | --> |
| Access | Dotted, small open arrow | accesses, reads, writes, reads-writes, rw | ..>, .> |
| Influence | Dashed, open arrow | influences, affects, contributes-to | ~~>, ~> |
| Triggering | Solid, filled arrow | triggers, then, precedes | ->> |
| Flow | Dashed, filled arrow | flows-to, sends, transfers | ==>, => |
| Specialization | Solid, hollow triangle | specializes, specialises, is-a, kind-of | See below |
| Association | Solid, no ends | associated-with, relates-to | --, - |
Realization also accepts the operators ..|> and .|>, and specialization accepts --|> and -|>. Every relationship also accepts its own name (composition, serving, flow and so on).
A bare -> is refused rather than guessed, because in ArchiMate it could mean serving, triggering, flow or assignment. The line is reported with the words that would fix it.
What flowss checks. A composition or aggregation cycle (two things that each contain the other), elements with no relationship, the same relationship written twice, endpoints that were never declared (reported, never invented), self-relationships and unreadable lines, which are listed under the figure in the error colour.
SysML v2 (structure)
Model-based systems engineering in a scoped subset of the standard OMG SysML v2 textual notation. flowss draws the interconnection view and checks whether the system can actually be wired.
Engine id sysml · Drawn inside flowss · Reference SysML v2 (structure)
package FuelSystem {
item def Fuel;
port def FuelPort {
out item feed : Fuel;
}
interface def FuelLine {
end supplier : FuelPort;
end consumer : ~FuelPort;
}
part def Tank { port outlet : FuelPort; }
part def Engine { port inlet : ~FuelPort; }
part def Vehicle {
part tank : Tank;
part engine : Engine;
interface feedLine : FuelLine connect tank.outlet to engine.inlet;
}
}
The spellings are the standard's: package, part def, port def, item def, interface def, part, port, item, in, out, inout, ~ for conjugation, :> (and subsets) for subsetting, :>> (and redefines) for redefinition, multiplicities such as [0..*], connect … to …, interface … connect … to … and flow of X from … to …. Identifiers with spaces use single quotes.
What flowss checks. Thirteen questions the picture cannot show: unresolved types and connection ends, a missing conjugation, two ports that both supply and never consume (a direction conflict), an interface bound the wrong way round (ends swapped), interface end counts, flow direction, a flow carrying a payload its destination does not accept, unconnected ports, items that arrive at a boundary port and go nowhere inside, composition cycles that no instance could ever build, a redefinition that names nothing it replaces, and a redefinition that widens what it replaces. Checks with nothing to examine (for example, no flows declared) are reported as not applicable rather than as passes.
Good to know. Behaviour blocks (state def, action def, requirement def, calc def and their usages) are outside the subset. They are listed as not read, with the reason, rather than silently skipped. An import of a standard library such as ISQ::* produces gaps rather than dozens of false errors.
Network (nwdiag)
Network topology from a block-style language: networks as horizontal lanes, hosts on them, addresses printed beside them.
Engine id nwdiag · Drawn by an outside render service · Reference Network (nwdiag) · Upstream docs http://blockdiag.com/en/nwdiag/
nwdiag {
network dmz {
address = "203.0.113.0/24";
web01 [address = "203.0.113.11"];
web02 [address = "203.0.113.12"];
}
network internal {
address = "10.0.2.0/24";
web01 [address = "10.0.2.11"];
db01 [address = "10.0.2.21"];
}
}
A host listed in several networks is multi-homed and drawn across them. Whole networks may be written on one line.
What flowss checks. Two hosts with the same address, a host address outside its network's subnet, an address that is not numeric, a network declared twice, a host repeated in one network, a network with no address (so no address in it can be checked), nodes that belong to no network, unread statements, a missing nwdiag { block, unclosed blocks, empty networks and links naming unknown nodes.
Server Rack
Data-centre rack elevations, checked for whether the rack can actually be built.
Engine id rack · Drawn inside flowss · Reference Server Rack
title "Rack A1"
units 42
power 8000 W
weight 900 kg
device "Patch panel" at 42 height 1 power 0 weight 3
device "ToR switch" at 41 height 1 power 320 weight 9
device "Compute node 01" at 34 height 2 power 620 weight 26
device "Storage shelf" at 24 height 4 power 780 weight 48
device "UPS" at 4 height 3 power 0 weight 96
| Statement | Meaning |
|---|---|
units N | Rack height in U (1 to 120). If omitted, 42 is assumed and reported |
power N W | The rack's power budget in watts |
weight N kg | The rack's weight limit in kilograms |
device "Label" at U [height H] [power W] [weight KG] | A device whose top sits at unit U, H units tall (default 1) |
title "Text" | The title |
Labels may be double-quoted, single-quoted or bare up to the word at. Unit suffixes (320 W, 320W, 9 kg, 2U) are accepted, but a suffix that belongs to another quantity (power 300kg) is refused and said. Numbers must be whole and written without thousand separators.
What flowss checks. Two devices claiming the same unit (drawn one over the other, so the figure would otherwise show one), a device reaching above the top or below U 1 (never silently clamped), the declared draw against the power budget, the declared mass against the weight limit, figures stated twice, and values that are not positive whole numbers. It also reports free capacity and the tallest device that would still fit.
Data models
DBML
The Database Markup Language from dbdiagram.io, drawn natively with right-angle connectors that start and end on the exact column being referenced.
Engine id dbml · Drawn inside flowss · Reference DBML · Upstream docs https://dbml.dbdiagram.io/home/
Project shop {
database_type: 'PostgreSQL'
Note: 'Online shop schema'
}
Enum order_status {
pending
paid
shipped
}
Table users {
id integer [pk, increment]
email varchar [unique, not null]
created_at timestamp [default: `now()`]
}
Table orders {
id integer [pk]
user_id integer [ref: > users.id, not null]
status order_status
total decimal(10,2)
indexes {
user_id
}
}
TableGroup sales {
users
orders
}
| Syntax | Meaning |
|---|---|
Table name { column type [settings] } | A table. Table schema.name, as alias and [headercolor: #3498db] are supported |
| Column settings | pk or primary key, not null, null, unique, increment, default: …, note: '…', ref: > table.column |
Ref: a.col > b.col, Ref name { … } | References. > many-to-one, < one-to-many, - one-to-one, <> many-to-many. Composite references use (a, b) |
Enum name { … } | An enum, usable as a column type |
indexes { … } | Indexes. A composite primary key here ((a, b) [pk]) marks the key columns in the drawing |
TableGroup name { … } | A group drawn around its tables |
Project name { … }, Note: '…' | Project details and notes. Triple-quoted strings span lines |
What flowss checks. The Diagnostics strip lints every schema:
| Finding | Severity |
|---|---|
| A table declared more than once (names are case-insensitive) | Error |
| A column declared more than once in a table | Error |
| A reference to a table that is not defined | Error |
| A reference to a column that does not exist | Error |
| A table with no columns | Warning |
| A table with no primary key | Warning |
| A reference joining columns of different types | Warning |
| An enum no column uses | Warning |
| A redundant index entry | Warning |
| A foreign key with no index | Info |
| A table with no relationships | Info |
| A schema with several tables and not a single note | Info |
| Table names mixing snake_case and camelCase | Info |
Diagram Intelligence (⇧⌘X) adds foreign-key cycles, self-references and orphan tables.
Good to know. DBML converts instantly to Mermaid ER and to ERD. Explain — prose & alt text describes a DBML schema in plain words without AI. Anything the parser could not read is listed as a warning rather than silently dropped.
ERD
A minimal entity-relationship language with dependable layout, compatible with BurntSushi's erd.
Engine id erd · Drawn by an outside render service · Reference ERD · Upstream docs https://github.com/BurntSushi/erd
title {label: "Clinic"}
[Patient]
*id
name
+clinic_id
[Clinic]
*id
name
[Visit]
*id
+patient_id
date
Patient *--1 Clinic {label: "registered at"}
Patient 1--* Visit
[Entity]opens an entity. Names may be quoted with backticks, single or double quotes to contain spaces.- Each line under it until the next blank line or entity is an attribute. Prefix
*for a key and+for a foreign key. - A relation is two names with a cardinality pair:
?zero or one,1exactly one,*zero or more,+one or more. Add{label: "…"}for a label. title {…},header {…},entity {…}andrelationship {…}blocks set global options.
What flowss checks. A malformed entity header (a missing bracket would otherwise turn into an attribute), an attribute before any entity, an unknown cardinality, a relation naming an entity that was never declared (one changed letter is enough), duplicate entities, attributes or relations, entities with no attributes or no key, and entities related to nothing.
Domain modelling and threat modelling
Data flow diagram
Structured-analysis DFDs in the Gane–Sarson notation, from context diagram to level 2 and beyond, with trust boundaries that make the same figure a STRIDE threat model.
Engine id dfd · Drawn inside flowss · Reference Data flow diagram
title "Payments — level 1"
level 1
entity Customer
entity "Card network"
process 1 "Accept order"
process 2.1 "Authorise payment"
store D1 "Orders"
boundary "PCI zone" { 2.1 }
Customer -> 1 : "order + card details"
1 -> D1 : "new order"
D1 -> 1 : "order status"
1 -> 2.1 : "auth request"
2.1 -> "Card network" : "ISO 8583"
"Card network" -> 2.1 : "auth result"
2.1 -> 1 : "approved"
1 -> Customer : "receipt"
entity, process and store declare elements; boundary "Name" { … } draws a trust zone; A -> B : "label" draws a flow and <-> draws flows both ways; level N sets the level (0 is a context diagram) and findings off hides the analysis strip. It flags black holes (inputs and no outputs), miracles (outputs and no inputs), read-only and write-only stores, unconnected elements, illegal flows (entity to store, entity to entity, store to store) and every flow that crosses between trust zones. The full list, with examples, is in Flow-systems analysis in Studio.
Event storming
Alberto Brandolini's event-storming board in its canonical colours, with the domain-driven-design gaps computed from the board.
Engine id eventstorm · Drawn inside flowss · Reference Event storming
title "Order fulfilment"
context "Ordering"
actor Customer
aggregate Order
command "Place order" by Customer on Order emits "Order placed"
event "Order placed" from "Place order"
readmodel "Basket summary"
external "Payment gateway"
hotspot "What if the card is authorised twice?"
policy "When order placed, reserve stock" then "Reserve stock"
context "Warehouse"
aggregate Reservation
command "Reserve stock" by "Fulfilment service" on Reservation
event "Stock reserved" from "Reserve stock"
Orange events, blue commands, yellow actors, purple policies, pink external systems, green read models, lilac aggregates and red hotspots; each context is a swimlane. It reports events no command produces, commands that produce no event, commands with no aggregate, aggregates with no events, policies with a missing trigger or command, and every flow that crosses between contexts. See Flow-systems analysis in Studio for the full syntax.
Note: For threat modelling from the adversary's side, the Attack tree engine rolls up cost, skill and detection to the cheapest attack. It is documented with the risk engines in Process, operations and quality engines.
Protocols, hardware and syntax
Packet & bit fields
Protocol headers and register bit-field layouts at 8, 16, 32 or 64 bits a word, in either bit order, with the layout checked.
Engine id packet · Drawn inside flowss · Reference Packet & bit fields
title "UDP header"
width 32
bitorder msb
words 2
0-15: Source port
16-31: Destination port
32-47: Length
48-63: Checksum
| Statement | Meaning |
|---|---|
width N | Bits per word: 8, 16, 32 (default) or 64 |
bitorder msb or bitorder lsb | Which end bit 0 is. msb (also msb0, big, network, rfc) puts bit 0 leftmost, the RFC convention. lsb (also lsb0, little) puts bit 0 rightmost, the datasheet convention. The figure always states which is in force |
words N | How many words the layout has. Without it, the extent is inferred from where fields start |
A-B: Label | A field over an absolute bit range. Separators -, .., en or em dashes and to all work, and reversed ranges (31-28) are accepted |
+N: Label | N bits starting at the bit after the previous field's highest index. Explicit ranges move this cursor too, so the two forms mix |
B: Label | A single bit |
Label = value | A fixed value printed inside the field, such as Version = 4 |
Bit indices are absolute across the whole layout, and the ruler prints them that way.
What flowss checks. Overlapping fields (drawn hatched and named), runs of bits no field claims, fields that run past the declared extent, and unreadable lines, which are counted in the headline. A layout that was not read whole can never be reported as having passed.
Packet diagrams (packetdiag)
RFC-style packet layouts in the packetdiag column language.
Engine id packetdiag · Drawn by an outside render service · Reference Packet diagrams (packetdiag) · Upstream docs http://blockdiag.com/en/nwdiag/packetdiag-examples.html
packetdiag {
colwidth = 32;
0-15: Source port
16-31: Destination port
32-63: Sequence number
64-95: Acknowledgment number
}
colwidth is the number of bits drawn per row; each field is a closed bit range and a label. flowss checks the arithmetic packetdiag will not: bit ranges that overlap, gaps between fields, reversed ranges, unread lines, partial final rows, unknown attributes, and a missing colwidth (packetdiag then uses its own default width, which may put two of your fields on one row).
Bytefield
Byte-field and packet-layout diagrams written as Clojure-style forms, the kind of figure found in RFCs and file-format specifications.
Engine id bytefield · Drawn by an outside render service · Reference Bytefield · Upstream docs https://github.com/Deep-Symmetry/bytefield-svg
(defattrs :bg-amber {:fill "#fef3c7"})
(draw-column-headers)
(draw-box "Magic" [:bg-amber {:span 4}])
(draw-box "Version" {:span 2})
(draw-box "Flags" {:span 2})
(draw-box "Length" {:span 8})
(draw-bottom)
Because the source is evaluated, one unbalanced bracket ends the whole render. flowss reads the forms first and points at unclosed or mismatched brackets, unclosed strings, near-miss function names, unknown attribute keywords and unreadable spans, so you get a line to fix instead of a runtime message. It also notes, without counting them as faults, a layout with no (draw-column-headers) or no (draw-bottom), and attributes defined with defattrs and never used.
WaveDrom
Digital timing diagrams, logic trees and register bit fields from WaveDrom JSON, the standard notation in hardware documentation.
Engine id wavedrom · Drawn inside flowss · Reference WaveDrom · Upstream docs https://wavedrom.com/
{"signal": [
{"name": "clk", "wave": "p......."},
{"name": "req", "wave": "0.1..0.."},
{"name": "data", "wave": "x.345x..", "data": ["a", "b", "c"]},
{"name": "ack", "wave": "0...1.0."}
]}
A document is one JSON object with exactly one of signal (timing diagram), assign (logic tree) or reg (bit field). config, head, foot, edge and gaps decorate it. A register looks like this:
{"reg": [
{"bits": 8, "name": "data"},
{"bits": 4, "name": "addr"},
{"bits": 3, "name": "mode", "attr": "RW"},
{"bits": 1, "name": "en"}
]}
What flowss checks. WaveDrom itself never refuses, so flowss measures what it actually drew: wave characters outside the alphabet (they become don't-care bands), data labels with no data box to sit in, data boxes with no label, a phase that shifts a lane out of view, edge arrows naming undeclared nodes, nodes declared twice, register fields with no bits or more bits than the register, unknown lane keys, and a document with none of signal, assign or reg (a one-letter typo such as signals would otherwise draw nothing).
Symbolator (HDL)
Component symbols drawn from VHDL entity or Verilog module declarations, as found in chip datasheets and FPGA documentation.
Engine id symbolator · Drawn by an outside render service · Reference Symbolator (HDL) · Upstream docs https://kevinpt.github.io/symbolator/
library ieee;
use ieee.std_logic_1164.all;
entity counter is
generic (WIDTH : positive := 8);
port (
--# {{clocks|Clocking}}
clk : in std_logic;
reset : in std_logic;
--# {{data|Data}}
count : out std_logic_vector(WIDTH-1 downto 0)
);
end entity;
Ports are placed left or right by direction and grouped by --# {{name|Title}} section markers. flowss checks VHDL entities for ports with no direction (left off the symbol), duplicate ports, malformed section markers, generics used but never declared, an end name that does not match the entity, unclosed parentheses and entities with no ports. Verilog modules are drawn but not checked, and flowss says so.
WireViz
Cable-harness documentation: connectors, cables and the connections between pins and wires, drawn with conductor colours and a bill of materials.
Engine id wireviz · Drawn by an outside render service · Reference WireViz · Upstream docs https://github.com/wireviz/WireViz
connectors:
X1:
type: D-Sub
subtype: female
pinlabels: [DCD, RX, TX, DTR, GND, DSR, RTS, CTS, RI]
X2:
type: D-Sub
subtype: female
pinlabels: [DCD, RX, TX, DTR, GND, DSR, RTS, CTS, RI]
cables:
W1:
wirecount: 3
length: 0.3
colors: [WH, BN, GN]
connections:
-
- X1: [2, 3, 5]
- W1: [1, 2, 3]
- X2: [3, 2, 5]
A connection set joins pin i of the first item to wire i of the cable to pin i of the next, so every list in a set must be the same length. Pin lists accept ranges such as [1-4]. flowss checks that the YAML parses, that the lists in each set have equal lengths (a short list would silently join later pins to the wrong conductor), that pin numbers exist on their connectors and are not repeated, that each connection set has at least two ends, that every name a connection uses is declared, that a cable's colors list matches its wirecount, and that every connector and cable is used. Keys it does not model are counted and listed as not judged.
Railroad / syntax diagram
Railroad (syntax) diagrams for grammars and API syntax, in the json.org style.
Engine id railroad · Drawn inside flowss · Reference Railroad / syntax diagram
value ::= object | array | string | number | "true" | "false" | "null"
object ::= "{" [ pair { "," pair } ] "}"
pair ::= string ":" value
array ::= "[" [ value { "," value } ] "]"
| Syntax | Meaning |
|---|---|
rule ::= body | One production per line, each drawn as its own diagram under its name. :=, = and -> also work |
| Items side by side | A sequence |
| Alternatives separated by a vertical bar | A choice, drawn stacked |
[ x ] | Optional (a bypass) |
{ x } | Repetition, zero or more (a loop) |
( … ) | Grouping, usually around a choice |
"if" or 'if' | A terminal, drawn as a rounded pill |
| Bare identifier | A nonterminal, drawn as a rectangle |
For example, sign ::= ( "+" | "-" ) digit { digit } is a group holding a two-way choice, then one digit, then any number of further digits. Anything unquoted that is not an identifier (such as , or :) is treated as a terminal too. # and // comment lines are ignored. Unclosed brackets are closed automatically and reported.
What flowss checks. Lines that are not a production (a rule wrapped onto a second line is not read, so keep each production on one line), brackets that had to be repaired, and rules defined twice.
Text-art, sketch and graph engines
SVGBob (ASCII)
ASCII art promoted to crisp SVG: boxes from dashes and pipes, arrows from ->, diagonals from / and \. Ideal for figures that also live in READMEs and code comments.
Engine id svgbob · Drawn by an outside render service · Reference SVGBob (ASCII) · Upstream docs https://ivanceras.github.io/svgbob-editor/
+--------+ +---------+ +----------+
| Client |---->| API |---->| Database |
+--------+ +---------+ +----------+
|
v
.---------.
| Queue |
'---------'
Ditaa (ASCII)
The same idea as SVGBob, with colour-fill markers inside boxes (cRED, cGRE, cBLU, cPNK, cYEL, cBLK, or c followed by three hex digits such as c1AB) and shape tags: {d} document, {s} storage, {io} input or output, {mo} manual operation, {tr} trapezoid, {c} choice and {o} ellipse.
Engine id ditaa · Drawn by an outside render service · Reference Ditaa (ASCII) · Upstream docs https://github.com/stathissideris/ditaa
+--------+ +-------+ +-------+
| cBLU | | | | {s} |
| Client +---->| API +---->| DB |
| | | cGRE | | |
+--------+ +-------+ +-------+
What flowss checks (both engines). Characters that look like the drawing but are not: en dashes or box-drawing lines instead of -, box-drawing bars instead of |, arrow symbols instead of ->, no-break spaces and tabs (which the editor and the renderer expand to different widths). For Ditaa it also flags colour codes and tags it does not know. Box geometry is not checked, because legitimate corners, junctions and rounded corners make that unreliable.
Tip: If a pasted ASCII diagram loses its connectors, an editor has probably auto-corrected hyphens into dashes. The readiness detail names the line.
Excalidraw
Excalidraw scenes rendered as SVG with the hand-drawn look. Paste a .excalidraw export from excalidraw.com, or write the JSON yourself.
Engine id excalidraw · Drawn by an outside render service · Reference Excalidraw · Upstream docs https://docs.excalidraw.com/docs/codebase/json-schema
{
"type": "excalidraw",
"version": 2,
"elements": [
{"id": "box", "type": "rectangle", "x": 0, "y": 0, "width": 160, "height": 70,
"strokeColor": "#1e1e1e", "backgroundColor": "transparent",
"boundElements": [{"type": "text", "id": "label"}]},
{"id": "label", "type": "text", "x": 40, "y": 25, "width": 80, "height": 20,
"text": "Idea", "fontSize": 20, "containerId": "box"}
],
"appState": {"viewBackgroundColor": "#ffffff"}
}
What flowss checks. Bindings held at only one end (a label with a containerId whose box does not list it, or an arrow binding the shape does not record), dangling bindings, duplicate ids, unknown element types, zero-size elements and documents that are not Excalidraw scenes.
Note: For drawing freehand and having it cleaned up, use the Sketch studio.
Cytoscape (Network)
Large networks (knowledge graphs, service dependencies, biological interaction networks) from Cytoscape.js JSON, laid out interactively.
Engine id cytoscape · Drawn in your browser · Reference Cytoscape (Network) · Upstream docs https://js.cytoscape.org/
{
"elements": [
{"data": {"id": "api", "label": "API"}},
{"data": {"id": "auth", "label": "Auth"}},
{"data": {"id": "db", "label": "DB"}},
{"data": {"source": "api", "target": "auth"}},
{"data": {"source": "api", "target": "db"}}
],
"layout": {"name": "breadthfirst"}
}
elementsis an array of nodes and edges (or an object withnodesandedges). A top-level{"nodes": [...], "edges": [...]}works too.layoutchooses the layout. The default iscose(force-directed); Cytoscape's built-ingrid,circle,concentric,breadthfirst,randomandpresetlayouts are also available.stylereplaces the default stylesheet. Without it, nodes showdata(label)and edges are grey arrows.- Click a node on the canvas to rename it.
A document with no elements is refused with an explanation of the expected shape. Diagram Intelligence (⇧⌘X) reads Cytoscape networks, and when one looks like a process it also reports lead time, throughput and the bottleneck.
Performance
Flame Graph
Flame graphs from folded-stack profiles (Brendan Gregg format), the standard view for CPU and performance profiling.
Engine id flamegraph · Drawn inside flowss · Reference Flame Graph
title "Request handler CPU profile"
main;parse;tokenize 20
main;parse;build_ast 35
main;eval;apply 30
main;render 25
Each line is a stack from root to leaf, frames separated by ;, followed by a sample count (decimals allowed). Frame width is proportional to total samples and depth grows upward, with an "all" bar across the bottom. Lines starting with # or // are comments.
What flowss checks. A line with no sample count is reported rather than dropped (every width is a share of the total, so a dropped stack would silently widen the others), and empty frames between two semicolons are reported. Stacks deeper than 1,200 levels are cut, and the figure says how many levels were not drawn.
Choosing between similar engines
| You need | Choose | Rather than |
|---|---|---|
| A diagram that must also render in Markdown, a wiki or another tool | Mermaid | Anything flowss-native |
| Textbook UML for an audience that expects it | PlantUML | Mermaid class or sequence diagrams |
| A sequence someone will implement, with replies and guards checked | Sequence & interaction | Mermaid or PlantUML sequence diagrams |
| A state machine you need to be correct | State machine (FSM) | Mermaid stateDiagram-v2 |
| Concurrency and resource contention | Petri Nets | A state machine |
| One C4 model with several views | Structurizr DSL | Separate D2 or Mermaid diagrams |
| Cloud infrastructure with exposure and redundancy checked | Cloud architecture | D2 or Mermaid architecture-beta |
| Enterprise architecture to a standard | ArchiMate | PlantUML ArchiMate sprites |
| Interfaces and port directions that must be right | SysML v2 (structure) | PlantUML component diagrams |
| A relational schema | DBML | ERD or Mermaid ER |
| A register or protocol header that must tile exactly | Packet & bit fields | packetdiag or Bytefield |
| Layout control on any graph | Graphviz | D2 |
| A network of thousands of nodes | Cytoscape | Graphviz |
For the full decision guide across every engine, see Choosing an engine.
Engineers also reach for engines documented on other pages: Attack tree, Design structure matrix, Fault tree, Reliability block diagram, Queueing network, Ladder logic and GRAFCET / SFC in Process, operations and quality engines; Digital logic and Circuit schematic in Maths, science and academic engines; Raw SVG for hand-written SVG; and Gantt & critical path and DMN in Business, strategy and planning engines. For a drag-and-drop canvas instead of code, use Weave.
Tips
- Start from a template. Every engine on this page has templates. Open the Templates tab in the engine library, or follow All under Sample templates on the engine's reference page.
- Declare before you use. Sequence, State machine, Cloud architecture and ArchiMate all report undeclared names rather than inventing them. Declaring every participant, state, resource or element first keeps the findings list short.
- State the facts the checks need. Mark subnets
public,privateorisolated; givealtfragments a subject and domain withon; give racksunits,powerandweight; give packet layoutswordsandbitorder. Each unlocks a check that otherwise reports a gap. - Keep confidential sources local. Engines drawn inside flowss or in your browser never send your source anywhere.
- Export to SVG when you are done with an outside-service engine, so the figure survives if the service is ever unreachable. The readiness detail suggests the same.
- Use Diagram Intelligence (
⇧⌘X) on Mermaid, Graphviz, D2, PlantUML, Structurizr, Cytoscape, ERD, DBML, Petri Nets and State machine figures for structural metrics and insights. - Write railroad rules on one line each. A production wrapped onto a second line is not read.
Limits and known constraints
| Engine | Limit |
|---|---|
| Mermaid | At most 500 connections in a flowchart |
| PlantUML | 30,000 characters per source; daily render allowance by plan |
| Outside render services | 100,000 characters per source; daily render allowance by plan; needs a connection |
| Sequence & interaction | 60,000 characters and 4,000 lines read; 18 lifelines drawn; 500 messages; fragments nested 12 deep; 8 branches per fragment; 16 open calls on one lifeline; a figure at most 24,000 pixels tall; labels clipped at 120 characters |
| State machine | 40,000 characters and 1,500 lines; 120 states drawn; 600 transitions; labels 80 characters |
| Petri Nets | 240 nodes, 900 arcs and 4,000 lines; 20,000 markings explored |
| Cloud architecture | 6,000 lines; 400 resources; 120 containers; 900 connections; containers nested 8 deep; labels 72 characters |
| ArchiMate | 4,000 lines; 400 elements; 900 relationships |
| SysML v2 | 200,000 characters and 6,000 lines; 600 definitions; 4,000 features; 2,000 connections |
| Server Rack | 40,000 characters and 1,200 lines; racks up to 120U; 240 devices |
| Packet & bit fields | 4,000 lines; 256 fields; 96 words; bit indices up to 65,535; labels 64 characters |
| Data flow diagram | 64 elements; 200 flows; 12 trust boundaries |
| Event storming | 12 contexts; 240 elements; 4,000 lines |
| Flame Graph | 1,200 stack levels drawn |
When a limit is reached, the figure says so and any verdict that depends on the missing part is withheld.
Other constraints:
- The render API draws only engines drawn inside flowss. Mermaid, Graphviz, D2, Nomnoml and Cytoscape need a browser, and outside-service engines need the Studio. Embeds draw Mermaid, Graphviz, D2, Nomnoml, PlantUML, Pikchr and SVGBob, but not Cytoscape or the other outside-service engines.
- An engine drawn inside flowss or in your browser never sends its source to a render service; for an outside-service engine every settled edit is one render against your daily allowance.
- D2 is drawn from a supported subset; constructs outside it are named in a caption rather than drawn.
- Graphviz uses the hierarchical
dotlayout. - SysML v2 covers structure only; behaviour definitions are listed as not read.
- Symbolator checks VHDL only; Verilog is drawn but not checked.
- The direct Diagram tools edit Mermaid, Nomnoml and Graphviz only. Other engines use the Edit label popover or Ask the flowss Studio Agent….
Troubleshooting
| What you see | What it means | What to do |
|---|---|---|
| "Mermaid draws at most 500 connections, and this diagram has more. …" | The flowchart is bigger than Mermaid will lay out | Split it into several figures, or use Graphviz or Weave |
| "PlantUML syntax error on line N: …" | The PlantUML server could not parse that line | Fix the line; check that blocks such as alt and if are closed |
| "Diagram render limit reached for your plan (N/day on …). It resets at midnight UTC; a higher plan raises the cap." or "PlantUML render limit reached for your plan …" | You have used today's outside-service renders (PlantUML is counted separately from the others) | Wait until midnight UTC, upgrade, or switch to an engine drawn inside flowss |
| "Diagram render limit reached (N/day for anonymous use). Sign in to lift this cap." or "PlantUML render limit reached (N/day for anonymous use). …" | Anonymous renders from your network are used up | Sign in |
| "Source is … — past the … safe-rendering budget for … Rendering this in the browser would freeze the page. …" | The source is over the engine's size budget (see Size budgets) | Split the figure, trim the source, or render it on a server with the render API |
| A conversion note: "Converted with the built-in … transpiler, which could not carry …" | An instant conversion dropped something | Check the result. If structure would have been lost, the instant result is not applied; use Convert with the flowss Studio Agent instead |
| "Mermaid … diagrams are laid out by their indentation — formatting is disabled so the structure stays yours." | Format source never re-indents indentation-sensitive Mermaid types | Indent by hand |
| "Diagram source is too large (>100,000 characters)" or "PlantUML source is too large (>30,000 characters)" | The source exceeds the service's limit | Split the diagram |
| "The diagram render service is rate-limiting this deployment. Try again shortly." | The outside service is busy | Try again in a moment; nothing is wrong with your diagram |
| "The diagram render service could not be reached." | No connection to the outside service | Check your connection, or use a local engine |
| "The diagram render service refused the request (N). This is a configuration or access problem, not your diagram." | The render service turned the request away for a reason that has nothing to do with your source | Try again later; if it persists on a self-hosted deployment, ask your administrator to check the render service. Nothing in your diagram needs changing |
| "Graphviz layout timed out — the graph may be too complex" | A Graphviz or D2 layout ran for more than 20 seconds | Split the graph into smaller figures, or draw a very large network with Cytoscape |
| A figure shows a placeholder card with Open in workstation in an embed or project view | The engine cannot be drawn there (Cytoscape and most outside-service engines) | Open it in the Studio, or publish an exported SVG instead |
| A D2 caption naming constructs that were not drawn | The source uses D2 features outside the supported subset | Rewrite with containers and connections, or accept the caption |
| "Formatting left the source alone — braces or strings do not balance yet, …" | Format source will not guess on an unbalanced document | Close the brace or quote, then format |
| "This D2 file uses block strings — formatting is disabled so their contents stay exact." | D2 block strings are present | Format by hand |
| A Cytoscape figure refuses with "This Cytoscape document has no elements to draw. …" | The JSON has no elements, nodes or edges | Add elements in the shape the message shows |
| A Sequence figure says the interaction is unchecked | Messages use imported arrows that do not state call or reply | Rewrite them with ->, reply and async |
| A Cloud architecture subnet is reported as unstated | The subnet has no public, private or isolated | Add one, so the exposure check can run |
An ArchiMate line is reported for using -> | A bare arrow is ambiguous in ArchiMate | Use a relationship word such as serves, triggers, flows-to or assigned-to |
| A Railroad rule is missing | The production was wrapped onto two lines | Put the whole production on one line |
| An ASCII diagram loses connectors | Look-alike characters (en dashes, box-drawing lines, tabs) | Replace them with plain hyphens, vertical bars and spaces |
If a render fails, the error box offers Explain & fix, and automatic repair may try up to twice. Automatic repair does not run on failures caused by an outside service or a limit, because nothing in your diagram is wrong and a rewrite would only change it for no reason. Automatic repair uses AI, so it runs only when AI is available on your plan, and it is on by default; to keep a broken diagram exactly as you wrote it, open the Studio's Settings, find Repair broken diagrams automatically in the Personalization & privacy section, and click Turn auto-repair off. Any repair it makes can be undone. See The Studio editor.
Related pages
- Choosing an engine
- The full engine catalogue
- The Studio editor
- Starting from a template
- Importing and converting
- Data, timeline, TikZ and the diagram tools
- Flow-systems analysis in Studio
- Business, strategy and planning engines
- Process, operations and quality engines
- Charts and data-visualisation engines
- Maths, science and academic engines
- Weave
- BPMN
- Embedding and the render API
- Exporting your work
- Plans and what they include
