Language reference
An architecture is written in *.loko.hcl files with exactly one project block. There is no
loko.toml, no markdown frontmatter model, and no hand-written D2. Run loko validate after every
change; loko fmt restores canonical formatting.
Element kinds
| C4 concept | loko block | Parent attribute | Example |
|---|---|---|---|
| Person (user, actor) | person | none | a customer, an operator |
| Software system (yours) | system | none | “Payments” |
| External system | external | none | a card processor, an email provider |
| Container (deployable/runnable unit, data store) | container | system = system.<name> (required) | API, SPA, database, queue |
| Component (grouping inside a container) | component | container = container.<name> (required) | handler, repository |
| Deployment | deployment with nested node and instance | of = <element> on an instance | prod environment |
Level 4 (code) is out of scope: leave it to the IDE.
Addresses are kind.name (container.api). Names are unique per kind across the whole project, not
per parent, so container.api in two systems is a duplicate_declaration error: name them
orders_api and billing_api, and use title for the display name.
Complete example
A full, compilable configuration demonstrating every block type — it validates with no errors:
project "acme-payments" {
description = "Payment processing platform"
loko_version = "~> 1.0"
}
locals {
team = "platform"
}
person "customer" {
description = "Buys things"
docs = "./docs/customer.md"
uses "checkout" {
target = container.api
description = "Places an order"
}
}
system "payments" {
description = "Authorization, capture, settlement"
owner = local.team
docs = "./docs/payments.md"
tags = ["pci"]
}
container "api" {
system = system.payments
technology = "AWS Lambda (Go)"
docs = "./docs/api.md"
tags = ["public", "pci"]
uses "orders" {
target = container.orders_db
description = "Reads and writes orders"
technology = "PostgreSQL wire protocol"
}
uses "cards" {
target = external.stripe
description = "Authorizes and captures card payments"
}
}
container "orders_db" {
system = system.payments
technology = "Aurora PostgreSQL"
docs = "./docs/db.md"
tags = ["pci"]
}
component "handler" {
container = container.api
description = "HTTP entry point"
docs = "./docs/handler.md"
uses "store" {
target = container.orders_db
description = "Persists the order"
}
}
external "stripe" {
description = "Card processing"
docs = "./docs/stripe.md"
}
deployment "prod" {
provider = "aws"
account = "123456789012"
region = "us-east-1"
node "vpc-main" {
node "subnet-a" {
instance "api" {
of = container.api
attributes = { memory = 1024, timeout = 30 }
binding "terraform" {
address = "module.api.aws_lambda_function.this"
}
}
}
}
}
view "payment-path" {
include = [system.payments, container.api]
exclude = [container.orders_db]
}
reconcile {
ignore = ["aws_iam_role_policy_attachment.*"]
}Blocks and attributes
project
Defines the project and the compiler version constraint. loko_version accepts constraints =, !=,
>, >=, <, <=, ~>, and comma-separated conjunctions; an unsatisfied constraint is a
version_unsatisfied error.
project "acme-payments" {
description = "Payment processing platform"
loko_version = "~> 1.0"
}locals
Names for reuse in expressions, referenced as local.<name> (see owner = local.team above).
person, system, external
Top-level logical elements with description, docs, tags, and (for system) owner.
container
Belongs to a system via the required system attribute. Common attributes: title, technology,
shape (e.g. function, database, queue), docs, tags.
component
Groups code inside a container via the required container attribute.
uses (relationships)
Relationships nest inside their source element. A uses "orders" inside container "api" has
the stable address container.api.uses.orders. Attributes: target (a typed reference such as
container.db, never a quoted string), description, technology, and kind (sync, async,
trigger). kind changes how a relationship is drawn, never what depends on what.
deployment, node, instance
The deployment plane instantiates the logical plane per environment. deployment carries
provider, account, region; node blocks nest arbitrarily (e.g. VPC → subnet) but do not
contribute to instance addresses — instance names are unique per deployment. instance has of
(the element it instantiates), free-form attributes, and binding blocks binding it to real
infrastructure such as Terraform addresses.
view
Selects elements by reference or tag for a focused diagram, with optional exclude:
view "payment-path" {
include = [system.payments, container.api]
exclude = [container.orders_db]
}Every system and container with children gets a diagram automatically; declared view blocks add
focused pictures.
docs and prose
docs = "./docs/api.md" attaches markdown to an element; it appears on the element’s site page,
so prose lives beside the model.
reconcile
Declares ignore patterns for future reconciliation against infrastructure sources.
References
References are bare traversals (container.api, system.payments), resolved and type-checked at
compile time. A typo is an error with a file, line and column, usually with a suggestion:
Error: Unresolvable reference
on arch.loko.hcl:28:19:
28 | target = container.ordrs_db
^^^^^^^^^^^^^^^^^^
No element "container.ordrs_db" is declared. Did you mean
container.orders_db?