Skip to content

Language reference

GitHub →

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 conceptloko blockParent attributeExample
Person (user, actor)personnonea customer, an operator
Software system (yours)systemnone“Payments”
External systemexternalnonea card processor, an email provider
Container (deployable/runnable unit, data store)containersystem = system.<name> (required)API, SPA, database, queue
Component (grouping inside a container)componentcontainer = container.<name> (required)handler, repository
Deploymentdeployment with nested node and instanceof = <element> on an instanceprod 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?