# Flow language reference Flow (https://flow.swofty.net) is a flowchart editor for software systems. Every chart is plain text in the Flow language below. Paste Flow source into the Code panel (or open a .flow file) and the canvas lays it out. Nodes can contain subflows, and a node's ports become the inputs and outputs of its subflow. ## File title "Checkout platform" # optional, the chart name # comments start with # or // Statements are separated by whitespace; newlines are not significant. Indent nested blocks by two spaces. ## Nodes : ["Label"] [at ] [{ ... }] - id: letters, digits, _ and -, starting with a letter or _. Unique within its scope. Used in edges. - kind: one of the kinds below. - "Label": display text. Defaults to the id. - at x y: optional canvas position. Omit it and Flow lays the chart out for you. Kinds: | kind | use for | aliases | |----------|----------------------------------------|---------------------------------| | client | web app, mobile app, CLI | app, ui, browser | | actor | a person or role | user, person | | gateway | load balancer, API gateway, edge | lb, proxy, edge | | service | long-running backend service | svc, server | | function | a step, handler or lambda | fn, lambda, step | | worker | background job or consumer | job, cron, consumer | | database | Postgres, MySQL, Mongo | db, sql | | cache | Redis, Memcached | | | queue | Kafka, SQS, pub/sub | stream, topic, bus | | storage | S3, blob or file storage | bucket, blob, s3 | | external | a third-party API you do not own | thirdparty, saas, vendor | | decision | a branch on a condition | branch, if | | note | free text annotation | comment | ## Node block A block holds properties, ports, and the node's subflow: orders: service "Order service" { tech "Go" # shown under the label desc "Owns the order lifecycle" # longer description in place "POST /orders" # input port: id, optional label out event "order.placed" # output port out charge # Any node or edge in the block makes it the node's subflow. validate: function "Validate cart" persist: function "Write order" db: database "orders schema" place -> validate -> persist # 'place' is the input port, seen from inside persist => db persist -> charge # 'charge' is an output port, seen from inside persist ~> event } Inside a subflow, the owner's ports are nodes: an `in` port can only start edges and an `out` port can only receive them. Port ids share a namespace with the subflow's nodes. Subflows nest to any depth. ## Edges a -> b # sync: a request/response call a ~> b # async: events, queues, webhooks a => b # data: reads, writes, streams a -> b : "HTTPS" # label (applies to the last hop of a chain) a -> b -> c # chain a, b -> c, d # fan in / fan out: every left node to every right node web -> api.request # into a port: . api.orders -> orders # out of a port Referencing a port that is not declared creates it: `a.x -> b` adds output port `x` to `a`. Edges can only connect nodes in the same scope. To cross a boundary, go through a port. ## Writing good Flow 1. Model the top level as deployable things (clients, services, stores, queues, third parties). 2. Give a node ports when traffic enters or leaves it in distinct ways (e.g. `in http`, `out events`). 3. Put internal steps in the node's block so the top level stays readable. 4. Use `->` for calls, `~>` for anything asynchronous, `=>` for data access. 5. Keep labels short: protocols, routes, topic names. 6. Leave out `at` positions unless you are preserving a layout. ## Complete example title "RAG assistant" user: actor "User" chat: client "Chat UI" { tech "React" } api: service "Assistant API" { tech "Python / FastAPI" in question out answer guard: function "Prompt guard" embed: function "Embed query" search: function "Vector search" llm: external "Claude" question -> guard -> embed -> search -> llm -> answer } vectors: database "Vector index" { tech "pgvector" } user -> chat -> api.question api.answer -> chat api => vectors : "kNN query"