# Arrows and edge labels

> Page: https://docs.plotwork.dev/reference/language/edges. Index of every page: https://docs.plotwork.dev/llms.txt

The arrow tokens, the labels and icons an edge can carry, the level edge that keeps mirrored columns side by side, and the options that animate a line, number the steps, or color a line by what it carries.

## TL;DR

| Question | Answer |
|---|---|
| Which arrows exist? | `<-->`, `-->`, `<->`, `..>`, `->`, `--`. |
| How is an edge labelled? | `a -> b : "label"`, and with an icon `a -> b : "label" icon=status:yes`. |
| Which labels get an icon without being asked? | `yes`, `no`, `true`, `false`, and a flow label that names a relationship with a verb, such as `reads`, `writes orders` or `deploys to`. Write `icon=none` on the edge to remove it, or `icons=off` on the header to remove them all. |
| Can an edge join a group? | No. Edges connect nodes, never an overlay or a group. |
| How do I make data move along a line? | `a -> b animate=on`, or `animate=on` on the header for every line with an arrowhead. A viewer who asks for reduced motion sees still lines. |
| How do I number the steps? | `numbered=on` on the header. `start=5` begins at 5, and `step=off` or `step=12` on a line skips it or sets its number. |
| How do I show what a line carries? | `role=data`, `identity`, `async` or `private` on a flow edge colors the line by meaning, and a legend under the diagram names each role used. `legend=off` on the header hides it. |

## Arrows

| Token | Line | Arrowheads | Use it for |
|---|---|---|---|
| `<-->` | dashed | both | A two-way link that is optional or indirect. |
| `-->` | dashed | end | A reply, a read, or a link that is optional or indirect. |
| `<->` | solid | both | A two-way call or sync. |
| `..>` | dotted | end | An asynchronous call: a webhook, an event, a pull, or a push that is not waited for. |
| `->` | solid | end | A call or a flow of data. |
| `--` | solid | none | An association with no direction. |

## Level edges

An edge between two nodes that are already side by side is drawn straight across instead of as a step forward. Two nodes count as side by side when other edges reach both and, without this edge, they sit the same distance from the start, as a primary and a replica database do. Such an edge, often dotted and labelled "replication", never pushes one of the two a layer away from its twin, so mirrored columns such as two availability zones stay level. When something sits between them the edge goes round it.

## Labels and icons

An edge may carry a label and an icon: `a -> b : "label" icon=status:yes`. These labels get an icon without being asked, so the branches of a decision read at a glance. Write `icon=none` on the edge to remove it.

| Label | Icon |
|---|---|
| `yes` | `status:yes` |
| `no` | `status:no` |
| `true` | `status:yes` |
| `false` | `status:no` |

### Verbs

A flow edge label that names a relationship with a verb draws that relationship's icon, the same one an imported diagram draws for it. The whole label may be the verb (`reads`, `depends on`), or start with it and go on with what is acted on, in five words or fewer (`writes orders`, `publishes OrderPlaced to Kafka`). The longest phrase wins, so `sends logs` is reporting and not sending, and a label that starts with a thing named after a verb, such as `Read replica` or `Pull request`, draws nothing. `icon=` on the edge picks another icon, `icon=none` keeps the label plain, and `icons=off` on the header turns every automatic icon off.

| Icon | Phrases |
|---|---|
| `lucide:eye` | `reads`, `read`, `reading`, `reads from`, `read from`, `queries`, `query`, `querying`, `looks up`, `look up`, `selects`, `select from`, `loads from`, `fetches from`, `read by`, `queried by`, `looked up by`, `loaded from`, `fetched from` |
| `lucide:square-pen` | `writes`, `write`, `writing`, `writes to`, `write to`, `inserts`, `insert`, `inserts into`, `updates`, `update`, `saves`, `save`, `saves to`, `persists`, `persist`, `persists to`, `deletes`, `delete`, `deletes from`, `uploads`, `upload`, `uploads to`, `upload to`, `written to`, `written by`, `written into`, `inserted into`, `updated by`, `saved to`, `saved in`, `persisted to`, `persisted in`, `deleted from`, `uploaded to`, `uploaded by` |
| `lucide:database` | `stores`, `store`, `stores in`, `store in`, `caches`, `caches in`, `reads and writes`, `reads writes`, `read write`, `read and write`, `stored in`, `stored by`, `cached in`, `cached by` |
| `lucide:hard-drive` | `mounts`, `mount`, `mounted by`, `mounted at`, `mounted in`, `mounted on` |
| `lucide:send` | `sends`, `send`, `sends to`, `publishes`, `publish`, `publishes to`, `publish to`, `emits`, `emit`, `produces`, `produce`, `produces to`, `enqueues`, `enqueue`, `pushes to`, `notifies`, `notify`, `fires`, `sent to`, `sent by`, `published to`, `published by`, `emitted to`, `emitted by`, `produced to`, `enqueued to`, `enqueued in`, `enqueued on`, `pushed to`, `notified by` |
| `lucide:inbox` | `receives`, `receive`, `consumes`, `consume`, `consumes from`, `subscribes`, `subscribes to`, `subscribe to`, `listens to`, `listens on`, `polls`, `dequeues`, `received by`, `received from`, `consumed by`, `consumed from`, `subscribed to`, `polled by`, `polled from`, `dequeued by` |
| `lucide:arrow-right-left` | `calls`, `call`, `invokes`, `invoke`, `requests`, `request`, `fetches`, `fetch`, `queries api`, `http`, `https`, `grpc`, `rest`, `graphql`, `called by`, `invoked by`, `requested by`, `requested from`, `fetched by` |
| `lucide:globe` | `routes`, `routes to`, `route to`, `forwards`, `forwards to`, `forward to`, `proxies`, `proxies to`, `redirects to`, `load balances`, `routed to`, `routed by`, `routed through`, `forwarded to`, `forwarded by`, `proxied to`, `proxied by`, `proxied through`, `redirected to`, `load balanced by` |
| `lucide:link` | `depends on`, `depend on`, `requires`, `needs`, `required by`, `needed by`, `depended on by` |
| `lucide:plug` | `provides`, `exposes`, `serves`, `provided by`, `exposed by`, `served by`, `served from` |
| `lucide:zap` | `triggers`, `trigger`, `starts`, `kicks off`, `schedules`, `wakes`, `triggered by`, `started by`, `kicked off by`, `scheduled by`, `woken by` |
| `lucide:rocket` | `deploys`, `deploy`, `deploys to`, `deploy to`, `releases to`, `ships to`, `promotes to`, `rolls out`, `rolls out to`, `deployed to`, `deployed by`, `deployed on`, `released to`, `shipped to`, `promoted to`, `rolled out to`, `rolled out by` |
| `lucide:key-round` | `authenticates`, `authenticate`, `authenticates with`, `authenticates against`, `authorizes`, `authorize`, `authorises`, `authorise`, `signs in`, `sign in`, `signs in with`, `logs in`, `log in`, `logs in with`, `grants`, `grant`, `assumes role`, `issues token`, `issues tokens`, `verifies token`, `verify token`, `validates token`, `validate token`, `logs out`, `log out`, `signs out`, `sign out`, `authenticated by`, `authenticated with`, `authenticated against`, `authorized by`, `authorised by`, `granted by`, `granted to`, `assumed by`, `issued by` |
| `lucide:key-round` | `reads secret`, `reads secrets`, `read secret`, `read secrets`, `read credentials`, `fetches secret`, `fetches secrets`, `reads credentials`, `gets secret`, `gets secrets` |
| `lucide:activity` | `reports`, `reports to`, `logs`, `logs to`, `metrics`, `telemetry`, `traces`, `logs and metrics`, `sends logs`, `sends logs to`, `sends metrics`, `sends metrics to`, `sends telemetry`, `sends traces`, `ships logs`, `ships logs to`, `exports metrics`, `exports traces`, `emits metrics`, `emits logs`, `reported to`, `reported by`, `logged to` |
| `lucide:download` | `pulls image`, `pulls images`, `pulls images from`, `pulls image from`, `pulled from` |

## Animated lines, step numbers and line roles

Two opt-in options draw more than the shape of a diagram. `animate=on` makes dashes travel along a line toward its arrowhead, so a reader sees data or events move. `numbered=on` draws a numbered badge on each edge or message in the order written, so a reader can follow a story by number. Both are off unless written, and a diagram that uses neither is drawn exactly as before. They work on flow and sequence diagrams, and a schema takes neither.

| Option | Where | Takes | What it does |
|---|---|---|---|
| `animate` | The header line | on, off | Dashes travel along every line that has an arrowhead, in the direction of the arrowhead, so a reader sees data or events move. A line opts out with animate=off. The default is off, and a viewer who asks for reduced motion sees still lines. Example: `flow "Data flow" animate=on` |
| `animate` | An edge or a message | on, off | Animates this one line, or with off keeps it still when the header turns animation on. A line with no arrowhead (--) has no direction, so animate=on on it is an error. A line with two arrowheads animates from its first end to its second. Example: `api -> db : "write" animate=on` |
| `numbered` | The header line | on, off | Draws a numbered badge on each edge or message in the order written. One counter runs through loops, alternatives, and parallel blocks. The text description carries the numbers. Example: `sequence "Login" numbered=on` |
| `start` | The header line | 0 to 999 | The number of the first badge. The default is 1. It needs numbered=on. Example: `sequence "Login" numbered=on start=5` |
| `step` | An edge or a message | off, or 0 to 999 | step=off leaves this line without a badge and does not use up a number. step=12 gives it the badge 12, and the next line continues from 13. It needs numbered=on in the header. Example: `api -> cache : "warm" step=off` |
| `role` | An edge or a message | data, identity, async, private | Flow edges only. What the line carries: data (stored data), identity (sign-in, tokens), async (events, queues), private (private network). It colours the line, a solid async line is dotted, and a legend names each role used. Example: `api -> db : "read" role=data` |
| `legend` | The header line | on, off | Flow only. off hides the legend of line roles. Example: `flow "Platform" legend=off` |

### Animated lines

- The dashes move in the direction of the arrowhead. A line with two arrowheads (`<->`) moves from its first end to its second, and a line with none (`--`) never moves, because nothing flows along it; `animate=on` written on one is an error.
- A solid line is drawn as a dash and a gap only while it moves. With motion off it is the plain solid line, so the difference between a call (`->`), a reply (`-->`), and an asynchronous call (`..>`) stays in the picture.
- The arrowheads, the labels, and the numbered badges never move.
- A viewer whose system asks for reduced motion sees still lines, and a picture shown where scripts and style rules do not run is still too. The motion is a CSS animation inside the SVG, so it runs in an image tag, on GitHub, and in the docs.
- Animate the lines that carry the story. When every line moves, nothing stands out; write `animate=on` on the header, then `animate=off` on the lines that should rest.

**Diagram source**

```diagram
flow "Event pipeline" direction=right animate=on

node app    client   "Web app"
node api    service  "Ingest API"
node queue  queue    "Event queue"
node worker function "Worker"
node db     database "Warehouse"

app    -> api    : "POST /events"
api    -> queue  : "enqueue" role=async
queue  ..> worker : "pull"
worker -> db     : "write batch" role=data
worker --> api   : "ack" animate=off
```

*Picture: Event pipeline: 5 nodes, 5 connections.*

**Text description**

**Nodes:**

- Web app, client
- Ingest API, service
- Event queue, queue
- Worker, function
- Warehouse, database

**Connections:**

- Web app to Ingest API: POST /events
- Ingest API to Event queue (asynchronous line): enqueue
- Event queue to Worker (dotted): pull
- Worker to Warehouse (stored data line): write batch
- Worker to Ingest API (dashed): ack

### Step numbers

- The count runs in the order written, through loops, alternatives, and parallel blocks, and starts at 1. `start=5` begins at 5.
- `step=off` on a line leaves it without a badge and does not use up a number. `step=12` sets the badge to 12, and the next line continues from 13.
- A sequence message has its badge directly above the arrow, before its label. A flow edge has its badge at the left end of its label, and an edge with no label has the badge on its line. Layout makes room for the badge, so it never covers a node or a label.
- The text description carries the numbers, as in `1. User to API: POST /login`, so a reader who cannot see the badges has them.
- More than 25 numbered steps in one diagram is the density warning `W200`: split the diagram into phases that each restart at 1.

**Diagram source**

```diagram
sequence "Sign in" numbered=on

actor user person   "User"
actor web  client   "Browser"
actor api  service  "Auth API"

user -> web : "Open the sign in page"
web  -> api : "POST /login"
loop "up to 3 attempts" {
  api -> api : "Check the password"
}
api --> web : "200 with a session cookie"
web --> user : "Show the account page" step=off
```

*Picture: Sign in: 3 participants.*

**Text description**

**Participants:**

- User, person
- Browser, client
- Auth API, service

**Steps:**

- 1. User to Browser: Open the sign in page
- 2. Browser to Auth API: POST /login
- loop: up to 3 attempts
  - 3. Auth API to Auth API: Check the password
- 4. Auth API to Browser (dashed): 200 with a session cookie
- Browser to User (dashed): Show the account page

### Line roles

A flow edge can say what it carries with `role=`: `data` for reads and writes of stored data, `identity` for sign-in and tokens, `async` for events and messages, and `private` for private network links. Each role has its own color, an `async` line drawn with a solid arrow is dotted, and the outline names the role in words, so the meaning never rests on color alone. A legend under the diagram lists the roles used. A brand theme colors the roles with the brand. Red, green and amber are never used, because they mean a status.

A role is never given by itself, because a colour means something. When an arrow's label names a relationship that has one, such as `reads` (data), `publishes` (async), or `authenticates` (identity), and the arrow has no `role=` and no `icon=`, warning `W500` names the arrows, one warning per role, with the `role=` to add in its hint. `W500` is a style suggestion, like `W204` and `W205`: `validate --strict` and the Action's `fail-on: warning` pass it.

## Make it look finished

- Label each arrow with a verb, such as "reads", "writes orders", "publishes OrderPlaced", "calls", "deploys to", or "authenticates": the label then draws the icon of that relationship (verbIcons). icon= picks another icon; icon=none keeps the label plain.
- Add role= to the arrows whose line means something: role=data for reads and writes of stored data, role=async for messages nobody waits for, role=identity for sign-in and credentials, role=private for private network links. The line takes the role's colour and the legend names it; warning W500 lists the arrows to mark.
- Give every group a type= (network, subnet, environment, data-tier, messaging, cluster, ci-cd, observability, and the rest), so its frame, tone, and icon follow. A group named plainly, such as "VPC", "Production", "Kafka", or "Monitoring", gets its type by itself; type=none keeps it plain.
- Name each node after the product it is ("Postgres", "Redis", "Amazon S3") so it draws the mark, and choose the node type that says what it is.
- Keep labels to two to four words; the picture carries the rest.

## See also

| Doc | Why |
|---|---|
| [Language reference](https://docs.plotwork.dev/reference/language.md) | Every page of the reference and what each covers |
| [Flow node types](https://docs.plotwork.dev/reference/language/flow-nodes.md) | The nodes an edge joins |
| [Icon sets](https://docs.plotwork.dev/reference/language/icon-sets.md) | The status icons an edge label can use |
| [Writing diagrams](https://docs.plotwork.dev/writing-diagrams.md) | Edges in context |
