The kimtu.yaml file
One file at the repository root declares every component the repository contains and how it connects to the rest of the ecosystem.
A complete file
One file describes one repository. A monorepo declares several components in the same file; a repository per service declares one.
apiVersion: kimtu/v1
repository:
id: invoice-service
name: Invoice Service
url: https://github.com/kimtu/invoice-service
docs: ./docs
components:
- id: invoice
name: Invoice Service
type: service
description: >-
Manages the lifecycle of an invoice from draft through finalisation and
settlement. The system of record for what a customer owes.
owner: Billing Team
language: TypeScript
runtime: Node 20.x
surface:
endpoints: 14
events: 4
relationships:
- to: pg-invoices
interaction: postgres
description: Persists invoices and their line items
data:
- Invoice
- LineItem
- to: event-bus
interaction: amqp
description: Publishes invoice lifecycle events
data:
- invoice.created
- invoice.finalized
- to: event-bus
interaction: amqp
direction: inbound
description: >-
Consumes settlement outcomes to move an invoice from finalised to
paid. Finalisation is synchronous; settlement is not.
data:
- payment.settled
- payment.failed
- to: tax-rates
interaction: https
endpoint: POST /rates
description: Resolves the regional rate at finalisation
data:
- TaxRate
- id: pg-invoices
name: Postgres · invoices
type: database
description: Primary Postgres instance for invoices and their line items.
owner: Billing Team
runtime: PostgreSQL 16
- id: invoicing-job
name: Invoicing Job
type: job
description: Nightly job that renders a PDF for every finalised invoice.
owner: Billing Team
docs: ./docs/invoicing-job
relationships:
- to: pg-invoices
interaction: postgres
description: Reads finalised invoices from a read replica
data:
- Invoice
- LineItem
external:
- id: event-bus
name: Event Bus
type: queue
description: >-
Fan-out broker carrying every billing event. Owned by the platform
repository, not this one.
- id: tax-rates
name: Tax Rates
type: external
description: Third-party service returning the tax rate for a jurisdiction.Top-level keys
| Field | Type | Description |
|---|---|---|
| apiVersion (required) | string | The manifest format. kimtu/v1 is the only accepted value. |
| repository (required) | map | The repository this file describes: id and name, plus an optional url linked from every page. |
| docs | string | The repository's documentation directory, e.g. ./docs. |
| components (required) | list | What this repository runs or owns. At least one. |
| external | list | Systems outside this repository that its components talk to. Declared here so they are drawn with a name rather than a bare slug. |
Fields marked * are required. Unknown keys are ignored, so you can annotate the file for your own tooling.
Component fields
Every entry under components becomes one page and one node on the architecture graph.
| Field | Type | Description |
|---|---|---|
| id (required) | string | Stable identifier, referenced by other components and used in URLs. Lowercase kebab-case. Renaming it creates a new component. |
| name (required) | string | Display name, shown in the rail and on the graph. |
| type (required) | enum | What kind of thing it is. Picks the icon. |
| description | string | One or two sentences, present tense. Shown under the title and in the system map's detail panel. |
| owner | string | The team or person responsible, as the repository names them. |
| language | string | The main programming language, e.g. TypeScript. |
| runtime | string | What it runs on, with the version, e.g. Node 20.x or PostgreSQL 16. |
| surface | map | How much it exposes, as counts: endpoints and events. |
| docs | string | Overrides the repository documentation path for this component, and claims the markdown underneath it. |
| relationships | list | Everything this component calls, reads, publishes or consumes. Each entry is one edge on the graph. |
surface is two counts rather than a list of routes. Numbers can be compared and summed across a system; a list of paths cannot.
| Field | Type | Description |
|---|---|---|
| endpoints | number | HTTP or RPC operations it serves, e.g. 14 for fourteen routes. |
| events | number | Distinct event types it publishes. |
surface:
endpoints: 14 # HTTP or RPC operations it serves
events: 4 # distinct event types it publishesRelationships
There is no separate depends_on, publishes or consumes. A call, a read and an event are the same thing — an edge between two components — and interaction and direction are what tell them apart.
| Field | Type | Description |
|---|---|---|
| to (required) | string | The id of the component on the other end. It may live in another repository, or be declared under external. |
| interaction | string | Protocol or mechanism: http, postgres, amqp, grpc, sqs. Free-form on purpose — an enum breaks on the first team using something we did not think of. |
| direction | enum | Which way the data moves. One of outbound, inbound. Defaults to outbound. |
| endpoint | string | The specific operation, e.g. POST /v1/invoices. Shown as the contract when present. |
| description | string | What moves across this edge, and why, in one line. |
| data | list | The types or events that travel over this edge. This is what Data flow follows, so an edge without it is drawn but cannot be traced. |
direction, publishing to a queue and consuming from one are the same edge between the same two components. It is the one field worth being deliberate about.relationships:
# A call this component makes. The endpoint is the contract.
- to: customer
interaction: http
endpoint: GET /v1/customers/{id}
description: Reads billing details when composing an invoice
data: [Customer]
# Something it publishes. direction defaults to outbound.
- to: event-bus
interaction: amqp
description: Publishes invoice lifecycle events
data: [invoice.created, invoice.finalized]
# Something it consumes. The same two components, opposite direction --
# which is the only thing telling the two edges apart.
- to: event-bus
interaction: amqp
direction: inbound
description: Moves an invoice to paid once payment settles
data: [payment.settled]External systems
Anything your components talk to that this repository does not own: a payment provider, a queue run by the platform team, another team's service. Entries under external get a name and a description but no page of their own, and they declare no relationships — whoever talks to them does that, with direction: inbound for a call that comes the other way.
| Field | Type | Description |
|---|---|---|
| id (required) | string | The id your components point at in their relationships. |
| name (required) | string | Display name, e.g. Stripe. |
| type | enum | Set it when the kind is worth drawing — a queue owned by another repository, say. Defaults to external. |
| description | string | What it is, and whose it is. |
Pointing at documentation
docs is one path, not a list. A component can override the repository's with a path of its own, which claims the markdown underneath it for that component.
# the repository's documentation directory
docs: ./docs
components:
# a component can override it with a path of its own,
# which claims the markdown underneath it
- id: invoicing-job
name: Invoicing Job
type: job
docs: ./docs/invoicing-jobValidation
An error stops the import and nothing is stored; a warning is reported beside the architecture it produced.
- Unknown targetswarning
- A relationship pointing at an id nobody declares never fails the import. The target is drawn as an undeclared placeholder carrying only the slug the file gave it — which is usually how you find out something was missed.
- Duplicate idserror
- Ids are unique across every repository in the workspace, and the second declaration is dropped rather than merged.
- Unknown typeserror
typeis a closed list, so a value outside it is rejected with the offending line named, rather than quietly turned into aservice.- Missing targeterror
- A relationship with no
todescribes an edge with one end. There is nothing to draw and nothing to guess. - Relationships to selfwarning
- The edge is counted, but no diagram draws a component calling itself.
- Empty docs patherror
- Raised when the file declares
docs:and no markdown was found under it. A repository that never mentions documentation is allowed not to have any. - Cycles
- Allowed and expected. Two components that call each other are two edges, and nothing tries to untangle them.
- Unknown keys
- Ignored, so the file can carry annotations for your own tooling.