Skip to content

Guides & reference

Software and architecture engines

Syntax, options, checks and limits for Mermaid, PlantUML, Graphviz, D2, sequence, state machine, cloud, ArchiMate, SysML, DBML, packet and 25 more engines.
Sculptural study of connected forms and structured ideas

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

EngineUse it forWhere it is drawnReference
MermaidFlowcharts, sequence, class, ER, state, C4, mind maps and 30-plus other types in one familiar syntaxIn your browsermermaid
PlantUMLStrict UML and many specialty diagramsA PlantUML serverplantuml
Graphviz (DOT)Any graph, DAG or dependency tree with fine layout controlIn your browsergraphviz
D2Clean architecture diagrams with nested containersIn your browserd2
NomnomlSketchy, whiteboard-style UMLIn your browsernomnoml
PikchrCompact documentation diagrams from an algebra of boxes and arrowsOutside render servicepikchr
Sequence & interactionSequence diagrams checked against the call stackInside flowsssequence
State machine (FSM)Finite state machines checked for reachability and determinismInside flowssstatemachine
Petri NetsConcurrency, shared resources and synchronisation, executedInside flowsspetri
Structurizr DSLC4 models with several views of one modelOutside render servicestructurizr
Cloud architectureAWS, Azure, Google Cloud and Kubernetes, checkedInside flowsscloudarch
ArchiMateEnterprise architecture to ArchiMate 3.2Inside flowssarchimate
SysML v2 (structure)Parts, ports and interfaces in the SysML v2 textual notation, checkedInside flowsssysml
Network (nwdiag)Network topology with subnets and addressesOutside render servicenwdiag
Server RackRack elevations with occupancy, power and weight checkedInside flowssrack
DBMLRelational schemas, dbdiagram.io-compatibleInside flowssdbml
ERDMinimal entity-relationship diagramsOutside render serviceerd
Data flow diagramGane–Sarson DFDs and STRIDE-style trust boundariesInside flowssdfd
Event stormingDomain-driven design workshop boardsInside flowsseventstorm
Packet & bit fieldsProtocol headers and register maps, checkedInside flowsspacket
Packet diagrams (packetdiag)Column-based packet layoutsOutside render servicepacketdiag
BytefieldRFC-style byte layouts in a Clojure-flavoured languageOutside render servicebytefield
WaveDromDigital timing diagrams, logic trees and register fieldsInside flowsswavedrom
Symbolator (HDL)Block symbols from VHDL or Verilog declarationsOutside render servicesymbolator
WireVizCable harnesses with a bill of materialsOutside render servicewireviz
Railroad / syntax diagramGrammars and API syntax, json.org styleInside flowssrailroad
SVGBob (ASCII)ASCII art turned into clean linesOutside render servicesvgbob
Ditaa (ASCII)ASCII art with colour fillsOutside render serviceditaa
ExcalidrawHand-drawn whiteboard scenes from Excalidraw JSONOutside render serviceexcalidraw
Cytoscape (Network)Large knowledge, dependency and biological networksIn your browsercytoscape
Flame GraphCPU and performance profiles from folded stacksInside flowssflamegraph

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

GroupEngines on this pageWhat it means for you
Inside flowssSequence, State machine, Petri Nets, Cloud architecture, ArchiMate, SysML v2, Server Rack, DBML, Data flow diagram, Event storming, Packet & bit fields, WaveDrom, Railroad, Flame GraphWorks 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 browserMermaid, Graphviz, D2, Nomnoml, CytoscapeWorks 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 servicePlantUML, Pikchr, Structurizr DSL, nwdiag, ERD, packetdiag, Bytefield, Symbolator, WireViz, SVGBob, Ditaa, ExcalidrawNeeds 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.

EnginesBudget (bytes of source)
Mermaid50,000
Graphviz, D2250,000
PlantUML30,000
Pikchr, Structurizr, nwdiag, ERD, packetdiag, Bytefield, Symbolator, WireViz, SVGBob, Ditaa, Excalidraw100,000
Nomnoml, Cytoscape100,000
Sequence, State machine, Cloud architecture, ArchiMate, SysML v2, Server Rack, Packet & bit fields300,000
Flame Graph150,000 (it grows with the profile, one line per stack)
Petri Nets, DBML, Data flow diagram, Event storming, WaveDrom, Railroad60,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:

MessageWhy
"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):

FromInstant conversion to
Mermaid (flowcharts only)D2, PlantUML
D2Mermaid
GraphvizD2
DBMLMermaid 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 lineDiagram
flowchart or graph with a direction (LR, RL, TB, TD, BT)Flowchart
sequenceDiagramSequence diagram
classDiagramClass diagram
stateDiagram-v2State diagram
erDiagramEntity-relationship diagram
C4Context, C4Container, C4Component, C4Dynamic, C4DeploymentC4 architecture
architecture-betaCloud and service architecture with icons
block or block-betaBlock diagram
packet or packet-betaPacket layout
requirementDiagramSysML-style requirements
gitGraphGit branching
ganttGantt chart (bars only, see Good to know)
timelineTimeline
journeyUser journey
kanbanKanban board
mindmapMind map
quadrantChart2×2 quadrant chart
piePie chart
xychart or xychart-betaBar and line chart
sankey or sankey-betaSankey
radar-betaRadar chart
treemapTreemap
venn-betaVenn diagram
wardley-betaWardley map
ishikawaFishbone
eventmodelingEvent model
cynefin-betaCynefin framework
swimlane-betaSwimlane
treeView-betaTree view
railroad-beta, railroad-ebnf-beta, railroad-abnf-beta, railroad-peg-betaRailroad 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:

FindingSeverity
A node that cannot be reached from any start nodeWarning
The graph splits into disconnected piecesWarning
A node with no outgoing edges that is not drawn as a terminal shapeInfo
A node used in edges but never given a label, so it renders as its bare idInfo
An edge declared more than once (it renders as overlapping arrows)Info
A node with an edge to itselfInfo
The source is not a flowchart, so there is nothing to lintInfo

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 gantt and sequenceDiagram draw 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, edge or graph, in any case) is read as a defaults statement rather than as a node.
  • fillcolor without style=filled is 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.
  • lhead or ltail without compound=true.
  • A labelled subgraph whose name does not start with cluster, so its box is never drawn.
  • strict merging 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:

SyntaxMeaning
a -> b, a <- b, a <-> b, a -- bArrow, reverse arrow, two-way arrow, plain line
a -> b -> cA chain; every hop is drawn
a -> b: labelA labelled connection
name: TitleA node with a display label
name: Title {shape: cylinder} or name.shape: cylinderA shape
name.style.fill: "#hex" and name.style.stroke: "#hex"Fill and stroke colours
group: Title { ... }A container, nested to any depth
group.childA path into a container, including edges into it
direction: rightLayout direction: right (default), left, down or up
# commentA 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 writeWhat 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 connectionThe 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 containerIt 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, transceiver and usecase.
  • 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: greedy and #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.

StatementMeaning
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 labelDeclares a lifeline. Add concurrent after the label to mark a service that can safely handle parallel requests
A -> B : labelA call
B reply A : labelA reply
A async B : labelA fire-and-forget message
A create B "Label"A creates lifeline B
A destroy B, or destroy BEnds lifeline B with a cross
activate X, deactivate XActivation bars, checked against the call stack
alt "label" or alt on subject : "v1", "v2" … when guard … endAlternatives. Each when (or else) opens a branch. With on, the subject and its domain let flowss check the guards
opt, loop, break, critical … endOther fragments. Their body starts straight away; opt may also take an else
par … branch … endParallel 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 : textNotes
divider "text" or separator "text"A horizontal divider
# commentA 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 100000 is 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"
StatementMeaning
state IDA state. Add final and label "Text" in either order
initial IDThe 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
StatementMeaning
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 textThe 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 kindAlso accepted as
cloud (account)account, subscription, project
region
networkvpc, vnet, virtual-network
zoneaz, availability-zone
subnetTakes public, private or isolated (or access public)
cluster, namespace
grouprg, resource-group, sg, security-group
onpremdc, datacenter, on-prem, on-premises
internetpublic, 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>]:

OperatorMeaning
->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.

FindingMeaning
Exposed datastoreA stateful resource reachable from the internet along a traffic path with no ingress on it
Datastore in a public subnetA stateful resource sitting directly in a public subnet
Single zoneSomething claiming redundancy that sits in exactly one zone
Boundary crossedA connection crossing a network boundary with no gateway named (via <id>)
AddressingA subnet outside its own network, overlapping ranges, a prefix naming a host rather than a block, or non-routable space
OrphanA resource connected to nothing
Undeclared endpointA connection naming an id that was never declared. It is kept and reported, never invented
Duplicate id, unknown typeReported. 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.

RelationshipLine in the figureWordsOperators
CompositionSolid, filled diamond at the sourcecomposed-of, composes, consists-of*->, *--
AggregationSolid, hollow diamond at the sourceaggregates, groupso->, o--
AssignmentSolid, ball at the source, arrow at the targetassigned-to, performs, deployed-on@->
RealizationDotted, hollow trianglerealizes, realisesSee below
ServingSolid, open arrowserves, used-by, provides-to-->
AccessDotted, small open arrowaccesses, reads, writes, reads-writes, rw..>, .>
InfluenceDashed, open arrowinfluences, affects, contributes-to~~>, ~>
TriggeringSolid, filled arrowtriggers, then, precedes->>
FlowDashed, filled arrowflows-to, sends, transfers==>, =>
SpecializationSolid, hollow trianglespecializes, specialises, is-a, kind-ofSee below
AssociationSolid, no endsassociated-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
StatementMeaning
units NRack height in U (1 to 120). If omitted, 42 is assumed and reported
power N WThe rack's power budget in watts
weight N kgThe 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
}
SyntaxMeaning
Table name { column type [settings] }A table. Table schema.name, as alias and [headercolor: #3498db] are supported
Column settingspk 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:

FindingSeverity
A table declared more than once (names are case-insensitive)Error
A column declared more than once in a tableError
A reference to a table that is not definedError
A reference to a column that does not existError
A table with no columnsWarning
A table with no primary keyWarning
A reference joining columns of different typesWarning
An enum no column usesWarning
A redundant index entryWarning
A foreign key with no indexInfo
A table with no relationshipsInfo
A schema with several tables and not a single noteInfo
Table names mixing snake_case and camelCaseInfo

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, 1 exactly one, * zero or more, + one or more. Add {label: "…"} for a label.
  • title {…}, header {…}, entity {…} and relationship {…} 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
StatementMeaning
width NBits per word: 8, 16, 32 (default) or 64
bitorder msb or bitorder lsbWhich 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 NHow many words the layout has. Without it, the extent is inferred from where fields start
A-B: LabelA field over an absolute bit range. Separators -, .., en or em dashes and to all work, and reversed ranges (31-28) are accepted
+N: LabelN bits starting at the bit after the previous field's highest index. Explicit ranges move this cursor too, so the two forms mix
B: LabelA single bit
Label = valueA 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 } ] "]"
SyntaxMeaning
rule ::= bodyOne production per line, each drawn as its own diagram under its name. :=, = and -> also work
Items side by sideA sequence
Alternatives separated by a vertical barA 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 identifierA 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"}
}
  • elements is an array of nodes and edges (or an object with nodes and edges). A top-level {"nodes": [...], "edges": [...]} works too.
  • layout chooses the layout. The default is cose (force-directed); Cytoscape's built-in grid, circle, concentric, breadthfirst, random and preset layouts are also available.
  • style replaces the default stylesheet. Without it, nodes show data(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 needChooseRather than
A diagram that must also render in Markdown, a wiki or another toolMermaidAnything flowss-native
Textbook UML for an audience that expects itPlantUMLMermaid class or sequence diagrams
A sequence someone will implement, with replies and guards checkedSequence & interactionMermaid or PlantUML sequence diagrams
A state machine you need to be correctState machine (FSM)Mermaid stateDiagram-v2
Concurrency and resource contentionPetri NetsA state machine
One C4 model with several viewsStructurizr DSLSeparate D2 or Mermaid diagrams
Cloud infrastructure with exposure and redundancy checkedCloud architectureD2 or Mermaid architecture-beta
Enterprise architecture to a standardArchiMatePlantUML ArchiMate sprites
Interfaces and port directions that must be rightSysML v2 (structure)PlantUML component diagrams
A relational schemaDBMLERD or Mermaid ER
A register or protocol header that must tile exactlyPacket & bit fieldspacketdiag or Bytefield
Layout control on any graphGraphvizD2
A network of thousands of nodesCytoscapeGraphviz

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, private or isolated; give alt fragments a subject and domain with on; give racks units, power and weight; give packet layouts words and bitorder. 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

EngineLimit
MermaidAt most 500 connections in a flowchart
PlantUML30,000 characters per source; daily render allowance by plan
Outside render services100,000 characters per source; daily render allowance by plan; needs a connection
Sequence & interaction60,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 machine40,000 characters and 1,500 lines; 120 states drawn; 600 transitions; labels 80 characters
Petri Nets240 nodes, 900 arcs and 4,000 lines; 20,000 markings explored
Cloud architecture6,000 lines; 400 resources; 120 containers; 900 connections; containers nested 8 deep; labels 72 characters
ArchiMate4,000 lines; 400 elements; 900 relationships
SysML v2200,000 characters and 6,000 lines; 600 definitions; 4,000 features; 2,000 connections
Server Rack40,000 characters and 1,200 lines; racks up to 120U; 240 devices
Packet & bit fields4,000 lines; 256 fields; 96 words; bit indices up to 65,535; labels 64 characters
Data flow diagram64 elements; 200 flows; 12 trust boundaries
Event storming12 contexts; 240 elements; 4,000 lines
Flame Graph1,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 dot layout.
  • 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 seeWhat it meansWhat to do
"Mermaid draws at most 500 connections, and this diagram has more. …"The flowchart is bigger than Mermaid will lay outSplit it into several figures, or use Graphviz or Weave
"PlantUML syntax error on line N: …"The PlantUML server could not parse that lineFix 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 upSign 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 somethingCheck 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 typesIndent 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 limitSplit the diagram
"The diagram render service is rate-limiting this deployment. Try again shortly."The outside service is busyTry again in a moment; nothing is wrong with your diagram
"The diagram render service could not be reached."No connection to the outside serviceCheck 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 sourceTry 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 secondsSplit 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 viewThe 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 drawnThe source uses D2 features outside the supported subsetRewrite 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 documentClose the brace or quote, then format
"This D2 file uses block strings — formatting is disabled so their contents stay exact."D2 block strings are presentFormat by hand
A Cytoscape figure refuses with "This Cytoscape document has no elements to draw. …"The JSON has no elements, nodes or edgesAdd elements in the shape the message shows
A Sequence figure says the interaction is uncheckedMessages use imported arrows that do not state call or replyRewrite them with ->, reply and async
A Cloud architecture subnet is reported as unstatedThe subnet has no public, private or isolatedAdd one, so the exposure check can run
An ArchiMate line is reported for using ->A bare arrow is ambiguous in ArchiMateUse a relationship word such as serves, triggers, flows-to or assigned-to
A Railroad rule is missingThe production was wrapped onto two linesPut the whole production on one line
An ASCII diagram loses connectorsLook-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.

Something unclear or out of date on this page? Tell us from the Support link in any studio — the flowss team reads every report.

© 2026 Voranox Inc. flowss — Flow Systems Studio. All rights reserved.

This documentation, its text and its examples are protected by copyright. Engine and format names are trademarks of their respective owners — see the terms and copyright and licences.