# Importing from code and data

> Page: https://docs.plotwork.dev/importing-from-code-and-data. Index of every page: https://docs.plotwork.dev/llms.txt

`import_source` turns a file you already have into a finished diagram in one call: a database schema, an API specification, infrastructure code, a container setup, a CI workflow, a table of numbers, a folder listing, or a git log. It reads the text you send and nothing else, so it runs no code, opens no file, and fetches nothing. It is for anyone, human or AI agent, who would rather draw the system from its source than from memory.

## TL;DR

| Question | Answer |
|---|---|
| What can it read? | `sql`, `prisma`, `drizzle`, `openapi`, `terraform`, `terraform-plan`, `bicep`, `arm`, `pulumi`, `cloudformation`, `cdk`, `compose`, `kubernetes`, `helm`, `kustomize`, `backstage`, `structurizr`, `asyncapi`, `github-actions`, `gitlab-ci`, `azure-pipelines`, `csv`, `json`, `tree`, and `git-log`. An agent reads what each one takes with `get_diagram_syntax` and `topic` `import-formats`. |
| What does it draw? | A schema from SQL, Prisma, or Drizzle; an endpoint access matrix, a map of its endpoints by tag, or one operation as a sequence from OpenAPI; a flow from Terraform files, a Terraform plan or state, Bicep, an ARM template, Pulumi, CloudFormation, the AWS CDK, Compose, Kubernetes, Helm, Kustomize, a Backstage catalog, AsyncAPI documents, or a GitHub Actions, GitLab CI/CD, or Azure Pipelines pipeline; a C4 diagram from a Structurizr workspace; a chart from CSV or JSON; a tree from a listing; a git graph or a timeline from a log. |
| How do I call it? | With `format` and `text`, or `files` for a folder such as a Terraform stack with its modules and environments, and optionally `title` and `options`. Over HTTP it is `POST /v1/import`; on the command line, `npx @buun_group/plotwork import-source <format> <file or folder>`. |
| What comes back? | The Plotwork source, a diagnostic for everything left out with its line and column in your file, `coverage` (how much of the file the picture shows), the validation, an outline, and a `viewUrl` to open in a browser. A file too big for one picture also comes back in `parts`. |
| Does it look finished? | Yes. Boxes get types, product icons, and typed frames; arrows show their relationship with an icon and a line colour; every import has a title and a description; and every sample on this page draws with no warning. |
| What if the file is too big? | The hosted service reads up to 10 MiB in one call; the local server and the command line read far more. A file too big for one readable picture is split into several diagrams in `parts`, never cut; an answer holds 24 of them, and `partsPage` asks for the next. |
| What if it cannot read the file? | Nothing is imported: `ok` is false, and an `I1xx` diagnostic says where and why, with a hint. It never throws. |
| Is my file stored? | No. The text is worked on in memory and not kept. |

## Ask your agent

```text
Use Plotwork to draw our database from db/schema.sql. Import it with import_source, then fix anything the diagnostics say was left out.
```

```text
Use Plotwork to show which roles can call which endpoints in openapi.yaml, as a matrix. Do not guess a cell the spec does not state.
```

```text
Use Plotwork to draw our local stack from docker-compose.yml and our CI from .github/workflows/ci.yml, and put both pictures in docs/architecture.md.
```

```text
Use Plotwork to draw production from infra/terraform: send every .tf and .tfvars file under it with import_source, with options.root set to envs/prod.
```

## How an import works

The agent reads the file and sends its text with the format. The importer reads it, writes Plotwork source, and parses it; the tool then validates the source and outlines it, once each.

| Step | What happens |
|---|---|
| Read | One reader per format. It builds the boxes, tables, rows, entries, or commits, and reports what it skips or cannot resolve, with the line and column in your file. |
| Write | Labels are quoted and escaped, ids are made valid, and the caps are applied. Icons come from the bundled set only: a lookup table for each format, and the same rule lint uses to suggest an icon. |
| Check | The source is parsed, validated, and outlined. An import that would not parse returns nothing rather than broken source. |
| Count | `coverage` says how much of the file the picture shows, such as `Drawn 79 of 79 resources.` When something is left out, `complete` is false, the `next` sentence says so first, and `I206` is reported when much is missing, so a picture of part of a file is never taken for the whole. |

A folder is sent as `files`, a list of `path` and `text`. A format that spans files (Terraform, Bicep, ARM, Pulumi, CloudFormation, the CDK, SQL, Prisma, Drizzle, Kubernetes, Helm, Kustomize, Backstage, AsyncAPI) reads every file with its endings as one text and passes over the rest with `I201`; a diagnostic names its file and its line in that file. Other formats read the first file.

Then call `draw_diagram` on the source to draw it, edit it first, or place it in a document with `sync_markdown`.

## Arrows and frames

An imported arrow names its relationship twice: in its label, and with an icon in the label and a line colour. The same relationship looks the same whichever format it came from.

| Relationship | Label examples | Icon | Line |
|---|---|---|---|
| Reads or writes stored data | `data`, `storage`, `cache`, `mounts` | database, hard drive | Data (blue) |
| Reads its settings | `reads` (a ConfigMap) | eye | Plain |
| Sends or receives a message it does not wait for | `messages`, `dead letters`, an AsyncAPI message | send, inbox | Async (dotted) |
| Grants access, or uses a credential | a role, `identity`, `policy`, `secret`, `reads` (a Secret) | key | Identity (violet) |
| Reaches another over a private network | `private link`, `VNet integration` | lock | Private network (aqua) |
| Depends on, provides, or consumes | `depends on`, `provides`, `consumes`, a chart version | link, plug | Plain |
| Routes outside traffic | an Ingress host or path, `public IP` | globe | Plain |
| Starts another | `triggers` | lightning | Plain |
| Other relationships | `firewall`, `logs`, `telemetry`, `images`, `builds on`, `includes`, `patches` | shield, activity, download, layers, package, replace | Plain |

A line colour is given only where it tells lines apart: when every line of a picture would have the same one, as in an AsyncAPI document where every line is a message, the lines stay plain and the icons say the rest. A legend names each colour used. An arrow with no label, such as a Compose `depends_on`, has no icon. Structurizr relationships keep their own description and technology, since a C4 line takes no icon.

Every frame has a type, so it has its colour and icon: a Terraform resource drawn round its parts by what it is (a SQL server is data, a Service Bus namespace messaging, a vault security), modules, CDK constructs, and Pulumi components as resource groups, Kubernetes namespaces, Helm releases and charts, Kustomize overlays as environments, GitHub Actions jobs and pipeline stages as CI and CD, and Backstage domains, systems, and owner groups. A Structurizr boundary gets an icon and a tone.

## Database schemas

`sql` reads `CREATE TABLE` and `ALTER TABLE ... ADD` from Postgres, MySQL, and SQLite: primary keys, foreign keys inline or at the table level, `UNIQUE`, and `NOT NULL`. A column is `nullable` unless it is `NOT NULL` or part of the primary key. A migration history can be sent whole: renames, dropped tables and columns, and tables created again are applied in order, so the picture is the schema the last migration leaves. A schema of more than six tables comes back with `I204` and ready groups for `options.tables` (names, or prefixes such as `org_*`), to draw it in parts that stay readable. A schema of more than 20 tables is drawn whole in `parts`: an overview of the parts and the foreign keys between them, then the tables of each schema name, cut by the tables that share keys when one is too big. `prisma` reads `schema.prisma` models, with each relation labelled by its field. `drizzle` reads `pgTable`, `sqliteTable`, and `mysqlTable` calls as text; the code is never run.

**Diagram source**

```diagram
schema "Database schema" desc="5 tables and the 4 foreign keys between them, imported from SQL."

table customers {
  id uuid pk
  email "varchar(255)" unique
  full_name text
  created_at timestamptz
}

table products {
  id bigserial pk
  sku text unique
  name text
  price_cents integer
  tags "text[]" nullable
}

table orders {
  id bigserial pk
  customer_id uuid fk
  status text
  placed_at timestamptz nullable
}

table order_items {
  order_id bigint pk fk
  product_id bigint pk fk
  quantity integer
  unit_price_cents integer
}

table payments {
  id bigserial pk
  order_id bigint fk
  provider_ref text nullable
  amount_cents integer
}

orders.customer_id -> customers.id
order_items.order_id -> orders.id
order_items.product_id -> products.id
payments.order_id -> orders.id
```

*Picture: Database schema: 5 tables, 4 relations. 5 tables and the 4 foreign keys between them, imported from SQL.*

**Text description**

**Tables:**

- customers
  - id uuid (primary key)
  - email varchar(255) (unique)
  - full_name text
  - created_at timestamptz
- products
  - id bigserial (primary key)
  - sku text (unique)
  - name text
  - price_cents integer
  - tags text[] (nullable)
- orders
  - id bigserial (primary key)
  - customer_id uuid (foreign key)
  - status text
  - placed_at timestamptz (nullable)
- order_items
  - order_id bigint (primary key, foreign key)
  - product_id bigint (primary key, foreign key)
  - quantity integer
  - unit_price_cents integer
- payments
  - id bigserial (primary key)
  - order_id bigint (foreign key)
  - provider_ref text (nullable)
  - amount_cents integer

**Relations:**

- orders.customer_id references customers.id (many to one)
- order_items.order_id references orders.id (one to one)
- order_items.product_id references products.id (one to one)
- payments.order_id references orders.id (many to one)

## API specifications

`openapi` reads an OpenAPI 3 document in JSON or YAML. The default view, `endpoints`, is a matrix of each operation against the security schemes, OAuth scopes, and `x-roles` values the document names. A cell is `yes` when the operation's security grants it, `no` when its security is stated and does not, and `na` when the document does not say: the importer never guesses. A `No sign-in` column appears when an operation is public through `security: []`. With `options.view` set to `sequence` and `options.operationId` set to an operation, it draws that one call: the client, the identity provider when OAuth secures it, the API, and each response as a branch.

**Diagram source**

```diagram
matrix "Orders API access" desc="Who may call each of the 6 endpoints of the Orders API, by security scheme, scope, and role, imported from its OpenAPI document."

columns "No sign-in" "orders:read" "partnerKey" "orders:write" "customer" "support"

row "GET /health" sub="Check the service is up" : yes, yes, yes, yes, yes, yes
row "GET /orders" sub="List orders" : no, yes, yes, no, yes, yes
row "POST /orders" sub="Place an order" : no, no, no, yes, yes, no
row "GET /orders/{orderId}" sub="Read one order" : no, yes, no, no, yes, yes
row "DELETE /orders/{orderId}" sub="Cancel an order before it ships" : no, no, no, yes, yes, yes
row "POST /orders/{orderId}/refunds" sub="Refund an order" : no, no, no, yes, no, yes

legend yes="Allowed" no="Denied"
```

*Picture: Orders API access: 6 rows compared on 6 columns. Who may call each of the 6 endpoints of the Orders API, by security scheme, scope, and role, imported from its OpenAPI document.*

**Text description**

**Columns: No sign-in, orders:read, partnerKey, orders:write, customer, support.**

- GET /health (Check the service is up): No sign-in: Allowed; orders:read: Allowed; partnerKey: Allowed; orders:write: Allowed; customer: Allowed; support: Allowed.
- GET /orders (List orders): No sign-in: Denied; orders:read: Allowed; partnerKey: Allowed; orders:write: Denied; customer: Allowed; support: Allowed.
- POST /orders (Place an order): No sign-in: Denied; orders:read: Denied; partnerKey: Denied; orders:write: Allowed; customer: Allowed; support: Denied.
- GET /orders/{orderId} (Read one order): No sign-in: Denied; orders:read: Allowed; partnerKey: Denied; orders:write: Denied; customer: Allowed; support: Allowed.
- DELETE /orders/{orderId} (Cancel an order before it ships): No sign-in: Denied; orders:read: Denied; partnerKey: Denied; orders:write: Allowed; customer: Allowed; support: Allowed.
- POST /orders/{orderId}/refunds (Refund an order): No sign-in: Denied; orders:read: Denied; partnerKey: Denied; orders:write: Allowed; customer: Denied; support: Allowed.

With `options.view` set to `map`, the endpoints are drawn as a tree: a folder for each tag, in the order the document's `tags` list gives them, and a row for each endpoint with its summary. The path every endpoint of a tag shares is named once on its folder, so long paths stay readable. An endpoint with several tags is drawn once, under its first. A document whose operations state no security and no `x-roles` has nothing for the columns of a matrix, so the default view draws the map instead and says so with `I203`.

A big API is never cut short. A matrix of more than 20 endpoints, or a map of more than 60 rows, is split by tag into several diagrams, returned in `parts` with `I204`; whole tags stay together, and a tag too big for one diagram is cut into even pieces titled `(1 of 2)`. `options.tags` draws only the operations with one of the tags, and `options.paths` only those under one of the path prefixes, such as `/orders`; what they leave out is counted in `coverage` and named with `I201`.

Only what the pictures need is read: paths, operations, tags, security schemes and requirements, and `x-roles`. In YAML the bodies of `components.schemas` and `components.examples` are skipped before parsing, and a `$ref` is followed only for the one operation a sequence draws, so a document of several megabytes imports in well under a second on the local server.

## Infrastructure as code

`terraform` reads the `.tf`, `.tf.json`, and `.tfvars` files of a stack, best sent whole in `files`, with its modules and its environments. Every `resource` and `module` block is drawn, nothing is summed into a count, and `coverage` says `Drawn N of N resources`:

| What the stack says | What is drawn |
|---|---|
| A resource names its resource group, network, subnet, plan, or environment (`resource_group_name`, `subnet_id`, `service_plan_id`) | It sits in that frame, the most specific one: a plan or an environment, then a subnet, a network, a resource group, and the account of the `provider` block. A frame named by a variable, such as `subnet_id = var.endpoint_subnet_id`, is drawn with the variable's name. |
| A resource names the one that holds it (`key_vault_id`, `server_id`, `storage_account_name`, an API in its API Management) | It sits in a frame around its holder, so a vault holds its secrets and a server its databases. |
| A role assignment or an IAM member | An arrow from the principal to the scope, labelled with the role. Several roles between the same two are one arrow, `3 roles`. A role given to principals the stack does not declare is a box named by the role, inside its scope. |
| A private endpoint | A box in its subnet, with a `private link` arrow to what it reaches. |
| A diagnostic setting, a DNS link, a listener, an association | An arrow labelled `diagnostics`, `DNS link`, the protocol and port, or drawn by nesting, such as a security group in the subnet it secures. |
| Any other reference | An arrow labelled by its attribute (`identity`, `storage`, `logs`) or by what it reaches (`secret`, `data`, `messages`). |

Types of AWS, Azure, and Google Cloud get their product icons from a table, and any other type of those providers is read from its name, so a new resource type still gets a fitting icon. A resource of another provider, such as `random_password`, is a plain box. Send the `.tfvars` files too, and names are drawn as they become: `name = "kv-${local.base}"` is drawn `kv-shop-dev`, and a subnet named by `var.endpoint_subnet_id` is drawn with the name at the end of its id. Variable defaults, `terraform.tfvars`, and every `*.auto.tfvars` apply as Terraform applies them; an environment's file, such as `env/dev.tfvars`, applies when it is the only one sent, or when `options.varFiles` names it (`--var-file` on the command line). A variable marked sensitive is never drawn. A variable that is named but never declared frames nothing: the resource sits in the account, with `I202` on it. With `options.groupBy` set to `file`, resources are framed by the file that declares them instead. When three or more boxes of one frame have the same arrow to one box, such as five jobs pulling images from one registry, they are drawn as one arrow from the frame's own box, `images (5)`. A stack of more than 30 boxes comes back in `parts`: first an overview with one box for each resource group and one counted arrow for everything between two groups, then one diagram for each group with everything in it. An arrow to a box of another part ends on a dashed box that names the part.

**Diagram source**

```diagram
flow "Azure infrastructure" direction=right provider=azure desc="12 resources on Azure, nested in the groups, networks, and subnets that hold them, imported from Terraform. An arrow means one resource refers to another, and its label says how: a role, a private link, logs, or a secret."

group azure_account "Azure subscription" type=account {

  group app "orders-prod" type=resource-group sub="westeurope" {
    node main_4 storage "ordersfuncstore" sub="Storage account Standard" icon=azure:storage-accounts
    node main_5 secret "orders-kv" sub="Key Vault standard" icon=azure:key-vault

    group main "orders-vnet" type=network sub="10.20.0.0/16" {

      group apps "apps" type=subnet sub="10.20.1.0/24" {
        node apps_subnet service "Subnet" icon=azure:subnet
      }
    }

    group main_2 "orders-plan" type=service-plan sub="P1v3 Linux" {
      node api service "orders-api" sub="App Service" icon=azure:app-service
      node worker function "orders-worker" sub="Function app" icon=azure:functions
    }

    group main_group "orders-sql" icon=azure:sql-server {
      node main_3 database "SQL server" icon=azure:sql-server
      node orders database "orders" sub="SQL database S1" icon=azure:sql-database
    }

    group main_group_2 "orders-bus" icon=azure:service-bus sub="Service Bus namespace, Standard" {
      node orders_2 queue "orders" sub="Service Bus queue" icon=azure:service-bus
    }
  }
}

api -> apps_subnet : "VNet integration"
api -> main_3 : "data"
api -> orders : "data"
api -> orders_2 : "messages"
api -> main_5 : "secret"
worker -> main_4 : "storage"
worker -> orders_2 : "messages"
```

*Picture: Azure infrastructure: 8 nodes, 7 connections. 12 resources on Azure, nested in the groups, networks, and subnets that hold them, imported from Terraform. An arrow means one resource refers to another, and its label says how: a role, a private link, logs, or a secret.*

**Text description**

**Nodes:**

- Account: Azure subscription
  - Resource group: orders-prod (westeurope)
    - ordersfuncstore (Storage account Standard), storage
    - orders-kv (Key Vault standard), secret
    - Virtual network: orders-vnet (10.20.0.0/16)
      - Subnet: apps (10.20.1.0/24)
        - Subnet, service
    - Service plan: orders-plan (P1v3 Linux)
      - orders-api (App Service), service
      - orders-worker (Function app), function
    - Group: orders-sql
      - SQL server, database
      - orders (SQL database S1), database
    - Group: orders-bus (Service Bus namespace, Standard)
      - orders (Service Bus queue), queue

**Connections:**

- orders-api to Subnet: VNet integration
- orders-api to SQL server: data
- orders-api to orders: data
- orders-api to orders: messages
- orders-api to orders-kv: secret
- orders-worker to ordersfuncstore: storage
- orders-worker to orders: messages

### Terraform in folders

A stack laid out as enterprises keep one, `modules/network`, `modules/data`, and `envs/dev` and `envs/prod` that call them, is read as Terraform plans it:

| What the folder holds | What is drawn |
|---|---|
| `module "network" { source = "../../modules/network" }`, with the module's files sent | A frame of the module around its resources, named as Terraform names them, `module.network.azurerm_subnet.this["app"]`. The module's variables take what the call passes, and `module.network.subnet_ids["app"]` is followed through the output to the subnet. A module from a registry or git, or one whose files were not sent, is one box. |
| Several root modules, such as `envs/dev` and `envs/prod` | One diagram for each, in `parts`, each with the `terraform.tfvars` of its own folder. `options.root` (`--root` on the command line) draws one: `envs/prod`, or `prod`. |
| `for_each` or `count` whose keys are in the text: a map or set literal, a variable's value, a local, or a number | One box for each copy, `aws_subnet.private["a"]`, named with `each.key` and `count.index`. A reference with an index points at that copy; one without points at every copy. Keys from outside the text, or more than 12, are one box marked `for each`. A block that makes no copy, such as `count = var.enabled ? 1 : 0` with `enabled` false, is left out with `I201`. |
| `format()`, `join()`, `lookup()`, `merge()`, `replace()`, and conditionals in names and tags | Resolved when their inputs are in the text: `format("kv-%s-%s", local.org, var.env)` is `kv-acme-prod`. A part that is not is kept by name, `kv-{prefix}-shared`. |
| `depends_on` | A dashed `depends on` arrow, only where nothing else joins the two. |
| Provider blocks with `alias` in different regions | A region frame for each inside the account. |

`options.groupBy` set to `network` nests by network alone, without module frames. A frame of the network module sits in the resource group its resources name, and a database of the data module placed in a subnet of the network module points at that subnet with a `subnet` arrow.

### Terraform plans and state

`terraform-plan` reads what Terraform itself writes, so values that come from outside the `.tf` files, such as a registry module's resources or a name a data source returns, are drawn as Terraform resolved them:

| What you send | What is drawn |
|---|---|
| The output of `terraform show -json plan.out`, after `terraform plan -out=plan.out` | The stack as the plan leaves it. Each box the plan changes ends its second line with a word, in a tone: `create` green, `update` amber, `replace` orange, `destroy` red, `import` and `move` aqua, `forget` violet. A box that is created, updated, replaced, or destroyed also carries the `change=` mark, a badge with `+`, `~`, `±`, or `−` and an outline in its tone, so the change shows on a box with a vendor icon too. The title counts the changes as Terraform does, `AWS infrastructure: 3 to add, 4 to change, 3 to destroy`. A resource the plan destroys is drawn from the plan's prior state. A resource changed outside Terraform since the last apply says `drifted`. |
| The output of `terraform show -json`, or a `terraform.tfstate` file | The stack as it was last applied, with no tone. |

Every resource keeps the address the `terraform` reader gives it, such as `module.network.aws_subnet.private["eu-west-1a"]`, so the overrides of a diagram made from the `.tf` files carry over to one made from a plan. A plan's configuration gives the arrows, followed through module outputs and the variables a call passes; when a reference names every copy of a block, the known value chooses the copy, so each machine sits in its own subnet. A state has no configuration: a value that holds another resource's id is the arrow, and a dependency no value shows is a dashed `depends on` arrow, left out when another arrow already leads there. The plan's JSON does not expand local values, so an arrow made through a `local` is left out with `I202`. A sensitive value or variable is never drawn.

### Bicep and ARM templates

`bicep` reads a deployment: `main.bicep`, the module files it uses (sent in `files`, at their paths), and its `.bicepparam` values. Every resource is drawn as the Terraform resource that makes the same thing, so `Microsoft.KeyVault/vaults` is the same Key Vault box, with the same frames and arrow labels, as `azurerm_key_vault`. A module whose file is sent is a frame in the resource group its `scope:` names; resources nested in a parent, or with `parent:`, sit in it; subnets written inline in a network are frames; `existing` resources say so. Names are drawn as they become, with `format('{0}-{1}-api', prefix, env)` and `'${prefix}-web'` resolved and `uniqueString()` kept by name. A loop over a known list is one box for each item, and a condition the values make false leaves the resource out with `I201`. A `.bicepparam` file applies when it is the only one sent, or when `options.varFiles` names it.

`arm` reads an ARM template, as `az bicep build` or the portal writes it, with its parameters file, and draws it as the Bicep reader does: `parameters()`, `variables()`, `copy` loops, `condition`, `dependsOn`, and child resources named `parent/child`.

**Diagram source**

```diagram
flow "Invoicing on Azure" direction=down provider=azure desc="8 resources on Azure, nested in the groups, networks, and subnets that hold them, imported from Bicep. An arrow means one resource refers to another, and its label says how: a role, a private link, logs, or a secret."

group azure_account "Azure subscription" type=account {

  group resource_group "Resource group" type=resource-group {
    node workspace service "log-ledger-prod" sub="Log Analytics workspace" icon=azure:log-analytics-workspaces
    node insights service "appi-ledger-prod" sub="Application Insights" icon=azure:application-insights
    node vault secret "kv-ledger-prod" sub="Key Vault" icon=azure:key-vault

    group storage_group "stledgerprod{uniqueString}" icon=azure:storage-accounts {
      node storage storage "Storage account" icon=azure:storage-accounts
      node invoices queue "invoices" sub="Storage queue" icon=azure:storage-queue
    }

    group plan "asp-ledger-prod" type=service-plan sub="Y1" {
      node job function "func-ledger-prod-invoices" sub="Function app" icon=azure:functions
    }
  }
}

insights -> workspace : "logs"
job -> storage : "storage"
job -> insights : "telemetry"
job -> vault : "Key Vault Secrets User, secret"
invoices -> storage : "part of"
```

*Picture: Invoicing on Azure: 6 nodes, 5 connections. 8 resources on Azure, nested in the groups, networks, and subnets that hold them, imported from Bicep. An arrow means one resource refers to another, and its label says how: a role, a private link, logs, or a secret.*

**Text description**

**Nodes:**

- Account: Azure subscription
  - Resource group: Resource group
    - log-ledger-prod (Log Analytics workspace), service
    - appi-ledger-prod (Application Insights), service
    - kv-ledger-prod (Key Vault), secret
    - Group: stledgerprod{uniqueString}
      - Storage account, storage
      - invoices (Storage queue), queue
    - Service plan: asp-ledger-prod (Y1)
      - func-ledger-prod-invoices (Function app), function

**Connections:**

- appi-ledger-prod to log-ledger-prod: logs
- func-ledger-prod-invoices to Storage account: storage
- func-ledger-prod-invoices to appi-ledger-prod: telemetry
- func-ledger-prod-invoices to kv-ledger-prod: Key Vault Secrets User, secret
- invoices to Storage account: part of

### Pulumi

`pulumi` reads three things, and draws each resource as the Terraform resource that makes the same thing, so AWS, Azure, and Google Cloud look the same whichever tool wrote them:

| What you send | What is read |
|---|---|
| `Pulumi.yaml` of a YAML program, with its `Pulumi.<stack>.yaml` | Resources by type token, `${resource.property}` references, variables, configuration, `fn::join`, `fn::select`, and `fn::split`, and `options.dependsOn` and `options.parent`. |
| The output of `pulumi stack export` | Every resource of a program in TypeScript, Python, Go, C#, or Java, without reading the code: its parent, its dependencies by property, and its components as frames. |
| The output of `pulumi preview --json` | The stack as the update will leave it; a step that deletes is counted with `I201`, not drawn. |

A resource is named by its logical name, or by its URN when two share one. A secret is never drawn. With no component to frame it, the network is a layer of its own: an app or a database placed in a subnet sits beside the network with a `subnet` arrow, as a network module draws it in Terraform and Bicep.

### CloudFormation and SAM

`cloudformation` reads a template in YAML, with its short tags, or JSON. `!Ref`, `!GetAtt`, and `!Sub` are arrows named by the property that holds them, names are drawn as `!Sub`, `!Join`, and parameter defaults make them, a `Condition` the defaults make false leaves the resource out with `I201`, and `DependsOn` is a dashed arrow. A SAM function's API event is an arrow from the API labelled with its route, `POST /orders`, and a queue or stream event a `triggers` arrow from its source. A resource is named by its logical id.

### AWS CDK

`cdk` reads the cloud assembly `cdk synth` writes: send the `cdk.out` folder (`manifest.json`, `tree.json`, each stack's template, and its nested stacks' templates), or one template `cdk synth` prints. Each resource is read as `cloudformation` reads it, so a bucket made with the CDK is the same box as one written in CloudFormation or Terraform.

| What | How it is drawn |
|---|---|
| Stacks | Frames, when the app has several, with arrows across them through `Fn::ImportValue`. `options.root` draws one stack alone, titled by its name. |
| Nested stacks | Frames inside their parent, with the parameters it passes and the outputs it reads followed both ways. One whose template was not sent is a box, with `I202`. |
| Your own constructs | Frames, by construct path. A construct that wraps a resource of its own, such as a `Function` or a `Vpc`, is that resource's box, and what it makes besides (its role and policy, its subnets) sits beside it in the same frame. |
| Names | Construct paths: `OrdersApi/Ordering/Handler` for a box, `OrdersApi/Ordering` for a frame. |

`AWS::CDK::Metadata` and the asset manifests are passed over, and a stack the manifest names whose template was not sent is `I201`.

## Containers

`compose` reads a Docker Compose file. Each service is a box whose type and brand mark come from its image, so `postgres:16` is a database with the Postgres mark. An arrow goes from a service to each service it depends on, through `depends_on`, `links`, or an address in its environment such as `postgres://app@db:5432`, and a service that shares another's network through `network_mode: "service:db"` sits in its frame with a `shares network` arrow. Every network the file declares becomes a frame, and named volumes become storage boxes. Variables are read as Compose reads them with nothing set: `${PORT:-8080}` and `${PORT-8080}` read as their default, `${FLAG:+on}` as nothing, and a variable with no default (`${TAG}`, `$TAG`) stays as `$TAG`, so the box shows the value is set elsewhere. `kubernetes` reads multi-document manifests the same way: workloads, Ingresses, ConfigMaps, Secrets, and claims become boxes, namespaces become frames, and routes, selectors, addresses, and mounts become arrows.

`helm` reads what `helm template` writes and draws it as `kubernetes` would, framed by the labels Helm adds: the release (`app.kubernetes.io/instance`), and, in a release of several charts such as an umbrella chart and its subcharts, the chart (`helm.sh/chart`); `app.kubernetes.io/part-of` frames the releases of one application. Sent a chart folder instead, it draws the charts (`Chart.yaml`, and those in `charts/`) and what each depends on, each arrow labelled with the version `Chart.lock` resolved, and a dependency `values.yaml` switches off dashed. The templates themselves are not rendered, and `I201` says so.

`kustomize` reads what `kustomize build` writes and draws it as `kubernetes` would, with the hash a generator adds to a ConfigMap's or Secret's name taken off, so the box keeps its name when its data changes. Sent a folder of `kustomization.yaml` files instead, it draws each overlay, base, and component as a frame of the files, patches, and generators it lists, with arrows for what an overlay `builds on` and `includes`, and what each patch `patches`.

**Diagram source**

```diagram
flow "shop services" direction=down desc="The 6 services of a Docker Compose file in 2 networks, with 1 named volume. Each arrow points from a service to one it depends on."

node db_data_volume storage "db-data" sub="Named volume"

group edge_net "edge" type=network {
  node proxy gateway "proxy" sub="nginx:1.27-alpine, ports 80, 443" icon=simple:nginx
  node web service "web" sub="build ./web"
  node api service "api" sub="build ./api"
}

group data_net "data" type=network {
  node worker service "worker" sub="build ./api, 2 replicas" icon=lucide:server
  node db_1 database "db" sub="postgres:16-alpine" icon=simple:postgresql
  node cache cache "cache" sub="redis:7" icon=simple:redis
}

proxy -> web
proxy -> api
web -> api
api -> db_1
api -> cache
worker -> db_1
worker -> cache
db_1 -> db_data_volume : "mounts" role=data
```

*Picture: shop services: 7 nodes, 8 connections. The 6 services of a Docker Compose file in 2 networks, with 1 named volume. Each arrow points from a service to one it depends on.*

**Text description**

**Nodes:**

- db-data (Named volume), storage
- Virtual network: edge
  - proxy (nginx:1.27-alpine, ports 80, 443), gateway
  - web (build ./web), service
  - api (build ./api), service
- Virtual network: data
  - worker (build ./api, 2 replicas), service
  - db (postgres:16-alpine), database
  - cache (redis:7), cache

**Connections:**

- proxy to web
- proxy to api
- web to api
- api to db
- api to cache
- worker to db
- worker to cache
- db to db-data (stored data line): mounts

## Software catalogs

`backstage` reads Backstage catalog descriptors: one `catalog-info.yaml` of many documents, or every descriptor of a folder. Components, APIs, and resources are boxes typed by what they are (a `website` is a client, an `openapi` API carries the OpenAPI mark, a `database` resource is a database), systems are frames, and domains are frames around their systems. An arrow says a component `provides` or `consumes` an API, or `depends on` another entity, and a subcomponent is a dashed `part of`. Each box names its owner on its second line; with `options.groupBy` set to `owner`, the groups that own the entities are the frames instead, nested by their parent group, with their members named on the tab. Everything is named by its entity reference, such as `component:default/orders-api`. A `Location` is not followed, a kind such as `Template` is left out with `I201`, and a relation to an entity the files do not describe is `I202`.

## Architecture models

`structurizr` reads a Structurizr workspace, written in its DSL (`workspace.dsl`) or exported as JSON, and draws one view of it as a C4 diagram. The view is the first `systemContext`, `container`, or `component` view the workspace defines, or the containers of its first system that has some; `options.view` (`context`, `container`, or `component`) and `options.root` (an identifier or a name, such as `banking.api`) choose another. A view holds what Structurizr's `include *` holds: the elements it is about and every person, system, or container a relationship joins to them, with a relationship of a component drawn as one of its container at the container level.

| What | How it is drawn |
|---|---|
| People, software systems, containers, components | C4 elements, with their description and technology. A container or component tagged `Database`, styled as a cylinder, or built on a database technology is a database. |
| The system or container the view is about | A boundary round its children. What a card holds is counted on it: `Holds 5 containers and 3 components.` |
| Groups and the enterprise | `group` and `enterprise` boundaries. A system inside the enterprise is the company's own; one outside it, or tagged `External`, is external. |
| Relationships | Lines with their description and technology, dashed when tagged `Asynchronous`, each drawn once. |
| Identifiers | Flat or `!identifiers hierarchical`, with `this`, `-> x` inside an element's block, `!element`, and `!const` read. They are the stable names: `banking.api`. |

`autolayout tb` or `lr` of the view sets the direction. An element the view does not reach is `I201`, with how to draw the view that holds it; a deployment environment is `I201`, and an `!include` or `workspace extends` is named, not fetched.

**Diagram source**

```diagram
c4 "Online banking: containers" view=container direction=down desc="The containers of Online banking and what they talk to, imported from the Structurizr workspace Harbour Bank."

person customer "Personal customer" desc="A customer with a current or savings account."
person agent "Support agent" desc="Answers customers in the contact centre."

boundary banking "Online banking" kind=system {
  container web "Web app" tech="React" desc="The banking pages in the browser."
  container api "Banking API" tech="Kotlin, Spring Boot" desc="Serves accounts, payments, and statements as JSON. Holds 3 components."
  db store "Banking database" tech="PostgreSQL" desc="Customers, sessions, and audit events."

  boundary payments "Payments" kind=group {
    container worker "Payment worker" tech="Go" desc="Sends payment orders to the card scheme in batches."
    container orders "Payment orders" tech="RabbitMQ" desc="Holds payment orders until they are sent."
  }
}

system ledger "Core ledger" desc="The bank's system of record for every account." external=on
system cards "Card scheme" desc="Clears card and account-to-account payments." external=on

customer -> web : "Signs in and pays" tech="HTTPS"
agent -> web : "Looks up a customer" tech="HTTPS"
web -> api : "Calls" tech="JSON/HTTPS"
api -> store : "Reads sessions" tech="JDBC"
api -> orders : "Queues payment orders" tech="AMQP"
api -> ledger : "Reads balances and posts entries" tech="MQ"
worker -> orders : "Takes payment orders" tech="AMQP"
worker -> cards : "Sends payments" tech="ISO 20022"
```

*Picture: Online banking: containers: container view, 9 elements, 2 boundaries, 8 relationships. The containers of Online banking and what they talk to, imported from the Structurizr workspace Harbour Bank.*

**Text description**

**Elements:**

- Personal customer, person (A customer with a current or savings account.)
- Support agent, person (Answers customers in the contact centre.)
- Core ledger, external system (The bank's system of record for every account.)
- Card scheme, external system (Clears card and account-to-account payments.)
- Boundary: Online banking, software system
  - Web app, container, React (The banking pages in the browser.)
  - Banking API, container, Kotlin, Spring Boot (Serves accounts, payments, and statements as JSON. Holds 3 components.)
  - Banking database, database, PostgreSQL (Customers, sessions, and audit events.)
  - Boundary: Payments, group
    - Payment worker, container, Go (Sends payment orders to the card scheme in batches.)
    - Payment orders, container, RabbitMQ (Holds payment orders until they are sent.)

**Relationships:**

- Personal customer to Web app: Signs in and pays, HTTPS
- Support agent to Web app: Looks up a customer, HTTPS
- Web app to Banking API: Calls, JSON/HTTPS
- Banking API to Banking database: Reads sessions, JDBC
- Banking API to Payment orders: Queues payment orders, AMQP
- Banking API to Core ledger: Reads balances and posts entries, MQ
- Payment worker to Payment orders: Takes payment orders, AMQP
- Payment worker to Card scheme: Sends payments, ISO 20022

## Event-driven systems

`asyncapi` reads AsyncAPI 2.x and 3.x documents in YAML or JSON, one application each. Send one, or every document of a folder in `files`: channels of one address are one box, so the picture shows where the producers and consumers of an event-driven system meet. Each application is a service, each channel a queue in the frame of its broker, and each operation an arrow labelled with its messages: into the channel for what the application sends, out of it for what it receives.

| What | How it is read |
|---|---|
| 3.x operations | `action: send` and `action: receive`, on the channel they reference, with their `messages`, or every message of the channel. |
| 2.x operations | Inverted, as the specification words them from the other side: `subscribe` is what the application sends, and `publish` is what it receives. |
| Servers | A frame for each kind of broker (`kafka` and `kafka-secure` are Kafka), with its host; a channel sits in it when its servers are of that kind. The picture runs down from what is sent to what reads it, so a broker is drawn once for each step that holds its channels, and a channel on a loop (an application that reads what its own sends lead to) stands outside the frame with the broker's mark. No frame holds both a channel an application sends to and one it reads, so no arrow runs round a frame. The Kafka, Redis, Pulsar, Amazon SNS and SQS, and Google Pub/Sub marks are drawn from the protocol; another mark only when the server's name names the product. |
| Names | `app:` and the title, `channel:` and the address, `broker:` and the protocol. |

An operation whose channel is not in its document is `I202`, a server that holds no drawn channel is `I201`, and a `$ref` into another file is read for its name only.

**Diagram source**

```diagram
flow "Event flows" direction=down desc="4 applications and the 5 channels where they meet: an arrow into a channel sends to it, an arrow out of one receives from it, labelled with the messages. Imported from AsyncAPI."

node checkout_service service "Checkout service" sub="Version 2.4.0"
node orders_placed_v1 queue "orders.placed.v1" sub="One event per order that was paid for." icon=simple:apachekafka
node orders_cancelled_v1 queue "orders.cancelled.v1" sub="An order the customer or support cancelled." icon=simple:apachekafka
node payments_settled_v1 queue "payments.settled.v1" icon=simple:apachekafka
node stock_reserved_v1 queue "stock.reserved.v1" icon=simple:apachekafka
node payments_service service "Payments service" sub="Version 1.12.3"
node warehouse_service service "Warehouse service" sub="Version 3.1.0"
node notification_service service "Notification service" sub="Version 0.20.1"

group kafka "Kafka" type=messaging icon=simple:apachekafka sub="2 hosts" {
  node payments_refunds_v1 queue "payments.refunds.v1" sub="Refunds asked for by support."
}

checkout_service -> orders_placed_v1 : "OrderPlaced"
checkout_service -> orders_cancelled_v1 : "OrderCancelled"
payments_settled_v1 -> checkout_service : "PaymentSettled"
stock_reserved_v1 -> checkout_service : "StockReserved"
orders_placed_v1 -> notification_service : "OrderPlaced"
payments_settled_v1 -> notification_service : "PaymentSettled, PaymentFailed"
orders_placed_v1 -> payments_service : "OrderPlaced"
payments_service -> payments_settled_v1 : "PaymentSettled, PaymentFailed"
payments_refunds_v1 -> payments_service : "RefundRequested"
orders_placed_v1 -> warehouse_service : "OrderPlaced"
orders_cancelled_v1 -> warehouse_service : "OrderCancelled"
warehouse_service -> stock_reserved_v1 : "StockReserved"
```

*Picture: Event flows: 9 nodes, 12 connections. 4 applications and the 5 channels where they meet: an arrow into a channel sends to it, an arrow out of one receives from it, labelled with the messages. Imported from AsyncAPI.*

**Text description**

**Nodes:**

- Checkout service (Version 2.4.0), service
- orders.placed.v1 (One event per order that was paid for.), queue
- orders.cancelled.v1 (An order the customer or support cancelled.), queue
- payments.settled.v1, queue
- stock.reserved.v1, queue
- Payments service (Version 1.12.3), service
- Warehouse service (Version 3.1.0), service
- Notification service (Version 0.20.1), service
- Messaging: Kafka (2 hosts)
  - payments.refunds.v1 (Refunds asked for by support.), queue

**Connections:**

- Checkout service to orders.placed.v1: OrderPlaced
- Checkout service to orders.cancelled.v1: OrderCancelled
- payments.settled.v1 to Checkout service: PaymentSettled
- stock.reserved.v1 to Checkout service: StockReserved
- orders.placed.v1 to Notification service: OrderPlaced
- payments.settled.v1 to Notification service: PaymentSettled, PaymentFailed
- orders.placed.v1 to Payments service: OrderPlaced
- Payments service to payments.settled.v1: PaymentSettled, PaymentFailed
- payments.refunds.v1 to Payments service: RefundRequested
- orders.placed.v1 to Warehouse service: OrderPlaced
- orders.cancelled.v1 to Warehouse service: OrderCancelled
- Warehouse service to stock.reserved.v1: StockReserved

## Pipelines

`github-actions` reads a workflow file. What starts it (`push`, `pull_request`, a schedule, or a manual run) is the first box, and each job follows in the order its `needs` set. When the steps fit, each job is a frame of its steps; otherwise a job is one box that says how many steps it runs.

**Diagram source**

```diagram
flow "CI" direction=right desc="4 jobs of the CI workflow, started by a push to main or a pull request to main, in the order their needs set, imported from GitHub Actions."

node on terminal "Push or pull request"
node lint step "Lint and type check" sub="ubuntu-latest, 6 steps" icon=simple:pnpm
node test step "Unit tests" sub="4 matrix runs, 6 steps" icon=simple:pnpm
node e2e step "Browser tests" sub="ubuntu-latest, 5 steps" icon=simple:pnpm
node build step "Build image" sub="ubuntu-latest, 3 steps" icon=simple:docker

on -> lint
on -> test
lint -> e2e
test -> build
e2e -> build
```

*Picture: CI: 5 nodes, 5 connections. 4 jobs of the CI workflow, started by a push to main or a pull request to main, in the order their needs set, imported from GitHub Actions.*

**Text description**

**Nodes:**

- Push or pull request, terminal
- Lint and type check (ubuntu-latest, 6 steps), step
- Unit tests (4 matrix runs, 6 steps), step
- Browser tests (ubuntu-latest, 5 steps), step
- Build image (ubuntu-latest, 3 steps), step

**Connections:**

- Push or pull request to Lint and type check
- Push or pull request to Unit tests
- Lint and type check to Browser tests
- Unit tests to Build image
- Browser tests to Build image

`gitlab-ci` reads a `.gitlab-ci.yml`. Its stages are frames down the page in the order of `stages` (GitLab's `.pre`, `build`, `test`, `deploy`, `.post` when it lists none), and its jobs sit inside them. A job with `needs` is reached from each job it needs, and `needs: []` from the start; a job without is reached from every job of the stage before, which is when GitLab starts it. A job that runs by hand, by its `rules`, or `only` on some refs is reached by dashed arrows and says so. `extends`, hidden `.jobs`, and anchors are resolved and not drawn; `parallel` and `parallel: matrix` are counted; a `trigger:` job points at the child or downstream pipeline it starts. Each `include:` is named in `I201`, not fetched.

`azure-pipelines` reads an `azure-pipelines.yml`: stages as frames, jobs and deployment jobs inside, a deployment naming its environment, and arrows for `dependsOn`, a name or a list, at both levels. A stage with no `dependsOn` waits for the one before it, and `dependsOn: []` starts with the pipeline; a `condition:` makes the arrows into a job dashed. A file of only jobs draws without frames, and a file of only steps as one chain. Templates, in a list or as `extends: template:`, are named in `I201`, not fetched.

Neither pipeline is told apart from other YAML by its text: name the file (`.gitlab-ci.yml`, `azure-pipelines.yml`) or set `format`.

## Tables of data

`csv` reads comma, tab, or semicolon separated text with quoted fields, and `json` reads an array of records, rows with a header, an object of names and numbers, or nested objects of numbers. With `options.chart` left at `auto`, the shape of the table chooses the chart. A table too big for one chart is drawn as several in `parts`, never cut: the places of an XY chart in pages of up to 60 and its series in groups of six, a matrix in pages of 20 rows and 8 columns, a pie of more than 30 slices in pages that each keep one slice for all the others, so every pie is of the whole, flows on several sankeys (a flow back to an earlier node on a sankey of its own, since a sankey only goes forward), and tiles cut along their groups.

| Shape of the table | Chart |
|---|---|
| A source, a target, and a value column | `sankey` |
| A path column (`a/b/c`), or two or more text columns and one number; nested JSON objects of numbers | `treemap` |
| Names, then columns of yes and no | `matrix` |
| One category and one number that are shares of a whole | `pie` |
| A category and one or more number columns | `xychart`, with lines when the categories are times |

Currency signs, thousands separators, and percent signs are read, and the unit goes on the chart.

**Diagram source**

```diagram
xychart "P50 ms and p95 ms by endpoint" direction=horizontal desc="p50 ms and p95 ms for each endpoint, 5 in all, imported from a table."

x "Endpoint" ["GET /products" "GET /products/{id}" "POST /cart/items" "POST /checkout" "GET /orders"]

bar "p50 ms" [42 31 58 145 67]
bar "p95 ms" [118 96 171 388 204]
```

*Picture: P50 ms and p95 ms by endpoint: 2 series (bar) over 5 places on the Endpoint axis, on a scale of 0 to 400. p50 ms and p95 ms for each endpoint, 5 in all, imported from a table.*

**Text description**

**X axis, Endpoint: GET /products, GET /products/{id}, POST /cart/items, POST /checkout, GET /orders.**

**Y axis: 0 to 400.**

**Drawn with the places down the side.**

**Series:**

- p50 ms (bar): GET /products 42, GET /products/{id} 31, POST /cart/items 58, POST /checkout 145, GET /orders 67
- p95 ms (bar): GET /products 118, GET /products/{id} 96, POST /cart/items 171, POST /checkout 388, GET /orders 204

## Folder listings

`tree` reads the output of the `tree` command, a list of paths such as the output of `git ls-files`, or an indented outline. Files and folders get their icons; an outline of plain words becomes an outline tree. A listing of more than 60 rows, or deeper than six levels, is drawn as several trees in `parts`, cut along its folders: each part names the path of the folder it is in (`src/lib`), and no entry is left out.

**Diagram source**

```diagram
tree "docs-site" desc="The folders and files of docs-site, 18 entries, imported from a tree listing."

dir "content" {
  dir "guides" {
    file "install.md"
    file "quick-start.md"
  }
  dir "reference" {
    file "api.md"
  }
}
dir "public" {
  file "favicon.svg"
  file "logo.png"
}
dir "src" {
  dir "components" {
    file "Nav.tsx"
    file "Page.tsx"
  }
  file "main.tsx"
  file "styles.css"
}
file "README.md"
file "tsconfig.json"
file "vite.config.ts"
```

*Picture: docs-site: file tree of 18 entries (6 folders, 12 files), 3 levels. The folders and files of docs-site, 18 entries, imported from a tree listing.*

**Text description**
- content/ (folder, 2 entries)
  - content/guides/ (folder, 2 entries)
    - content/guides/install.md
    - content/guides/quick-start.md
  - content/reference/ (folder, 1 entry)
    - content/reference/api.md
- public/ (folder, 2 entries)
  - public/favicon.svg
  - public/logo.png
- src/ (folder, 3 entries)
  - src/components/ (folder, 2 entries)
    - src/components/Nav.tsx
    - src/components/Page.tsx
  - src/main.tsx
  - src/styles.css
- README.md
- tsconfig.json
- vite.config.ts

## Git history

`git-log` reads `git log --oneline --graph --decorate`, plain `git log`, or `--oneline`. A log with a graph or decorations becomes a git graph: lanes from the graph, branch names from decorations and merge messages, and tags. A plain log with dates becomes a timeline of commits by day, or by month when it spans more than about twelve days. `options.view` chooses either. A longer history is drawn in pages of up to 40 consecutive commits, newest first, each a git graph of its own; a timeline draws a busy day or month as several periods of five commits, and a long one in pages. No commit is left out.

**Diagram source**

```diagram
git "Repository history" desc="8 commits on 3 branches, with 2 merges and 2 tags, imported from git log."
commit "Initial commit"
commit "Add sign in with email" id=c9d0e1f2
branch fix-login from=c9d0e1f2
commit "Keep the session after a refresh"
checkout main
merge fix-login "Pull request #12" id=c5a6b7c8 tag="v1.0.0"
commit "Fix a typo in the README"
branch "feature/search" from=c5a6b7c8
commit "Add a search box to the header"
commit "Index the docs pages"
checkout main
merge "feature/search" tag="v1.1.0"
```

*Picture: Repository history: 8 commits on 3 branches, 2 merges, 2 tags. 8 commits on 3 branches, with 2 merges and 2 tags, imported from git log.*

**Text description**

**Branches, in lane order:**

- main is the default branch
- fix-login was made from main at "Add sign in with email"
- feature/search was made from main at "Pull request #12"

**History, oldest first:**

**1. On main: commit "Initial commit"**

**2. On main: commit "Add sign in with email", id c9d0e1f2**

- fix-login was made from main at "Add sign in with email"

**3. On fix-login: commit "Keep the session after a refresh"**

**4. On main: merge of fix-login into main ("Pull request #12"), id c5a6b7c8, tagged v1.0.0**

**5. On main: commit "Fix a typo in the README"**

- feature/search was made from main at "Pull request #12"

**6. On feature/search: commit "Add a search box to the header"**

**7. On feature/search: commit "Index the docs pages"**

**8. On main: merge of feature/search into main, tagged v1.1.0**

## Limits

| Limit | Value | Past it |
|---|---|---|
| Text on the hosted service | 10 MiB in one call (10,485,760 characters), 200 files | Refused with `I102` and a hint, as data; a request body over 10 MiB is refused with 413 before it is read. |
| Text on the local server and the command line | 100 million characters, 20 thousand files | A guard on memory, not a cap a real project meets. |
| Diagrams in one answer | 24 | Each grows a little first; past that the answer pages them, and `partsPage` asks for the next page. Nothing is left out. |
| Boxes in a Terraform flow | none | A stack of more than 30 boxes also comes back in `parts`. |
| Elements of a C4 view from Structurizr | 80, and 160 relationships | Left out past them with `I200`; draw a smaller view with `options.view` and `options.root`. |
| Boxes in another flow (Compose, Kubernetes, a workflow) | 40 a picture | Split into `parts` along its frames, with an overview of its groups when it has some, `I204`. |
| Tables in a schema | 20 a picture | Split into `parts` by schema name, then by tables that share keys, after an overview of the parts and the foreign keys between them, `I204`. A key to a table of another part ends on a copy of that table. |
| Endpoints of an OpenAPI matrix or map | 20 matrix rows, 8 columns, 60 map rows a picture | Split by tag, and columns into pages, all in `parts`, `I204`. |
| Rows and columns of another matrix | 20 rows, 8 columns a picture | Split into pages in `parts`, `I204`. |
| Places, slices, flows, or tiles of a chart | 60 a picture | Split into pages in `parts`, `I204`; a pie keeps the rest as one slice on each page. |
| Series of an XY chart | 6 a picture | Split into groups of series in `parts`, `I204`. |
| Rows of a tree | 60 a picture | Split by folder into `parts`, `I204`. |
| Commits of a git graph, events of a timeline | 40 a picture | Split into consecutive pages in `parts`, newest first, `I204`. |
| Messages of a sequence | 30 a picture | Split into sequences in `parts`, each with the request, `I204`. |

Nothing in a file is left out by a picture's size: past it, the import comes back in `parts`, each made a little bigger before there are more. An answer holds 24 parts; `partsTotal` says how many there are and `partsPage` asks for the next 24. A part can still be dense: validation then warns with `W200`.

## Import codes

Every import diagnostic points into the file you sent.

| Code | Means | What to do |
|---|---|---|
| `I100` | The text is empty. | Send the content of the file. |
| `I101` | The text cannot be read as that format, such as YAML indented with a tab or a quote that never closes. | Fix the line it names, or check that `format` matches the file. |
| `I102` | The call is over the limits: characters that are not white space or a comment line, all characters, or files. | Send fewer files or one part, or use the local server, which reads far more. |
| `I103` | The importer wrote source that does not parse. | A mistake in the importer, not in your file: report it, and draw by hand meanwhile. |
| `I104` | An option names something the file does not have, such as an `operationId`. | Use one the hint lists. |
| `I105` | Nothing in the file can be drawn, such as SQL with no `CREATE TABLE`. | Check the format, or that the file holds what it should. |
| `I200` | A cap left items out. | Import a smaller part, or accept the summary. |
| `I201` | A construct or a file was skipped, such as a file of another format in a folder. | Read the hint; add it by hand if it matters. |
| `I202` | A reference points at something the file does not define, such as a foreign key to a missing table. | Include the target in the text, or ignore it if it lives elsewhere. |
| `I203` | Something was changed to fit, such as a short CSV row padded with empty cells or a matrix cell that is not yes or no turned into `na`. | Check the row it names, and fix the file if the change is wrong. |
| `I204` | A schema has more than six tables, so its text will be small; or an API was split into `parts` by tag. | Draw a schema in parts with `options.tables`, using the groups the hint names, such as `["users", "org_*"]`; show every part of a split API. |
| `I206` | Much of the file is not in the picture; `coverage` says how much. | Say so when you show it, read the other diagnostics, and import a smaller part or choose with the options. |

## See also

| Doc | Why |
|---|---|
| [import_source](https://docs.plotwork.dev/mcp/tools/import-source.md) | The tool's arguments, result, and limits |
| [Import source over HTTP](https://docs.plotwork.dev/api/post-v1-import.md) | `POST /v1/import` |
| [Migrating from Mermaid](https://docs.plotwork.dev/migrating-from-mermaid.md) | The other importer: Mermaid diagrams to Plotwork |
| [Writing with an agent](https://docs.plotwork.dev/mcp/agent-loop.md) | The fix-and-check loop after an import |
| [Diagnostic codes](https://docs.plotwork.dev/reference/language/diagnostics.md) | Every code, including the import codes |
| [Diagrams as code](https://docs.plotwork.dev/diagrams-as-code.md) | Keep an imported diagram in step with its files in CI |
| [Matrices](https://docs.plotwork.dev/matrices.md), [XY charts](https://docs.plotwork.dev/xy-charts.md), [Tree diagrams](https://docs.plotwork.dev/tree-diagrams.md), [Git graphs](https://docs.plotwork.dev/git-graphs.md), [C4 diagrams](https://docs.plotwork.dev/c4-diagrams.md) | The kinds an import writes, to edit the result |
