9. Fields and Labels
A .arch file is mostly structural: declare a module, declare an interface, declare a process. The non-structural part — owners, versions, URLs, classification axes — lives in two related but distinct constructs: fields and labels.
This chapter is about both. The short version up front:
- Fields describe what a thing is: its team, its repo URL, its CMDB ID, its base path.
- Labels classify a thing along an axis: its domain, its network segment, its security zone, its criticality.
Fields are first-class values. Labels are tags used for projections (views, checks, groupings). Many things could plausibly be either; the rule of thumb is: if you’d ever want to group by it or focus a view on it, make it a label.
Fields
Fields live directly in a body, no surrounding block:
module Orders { team: Commerce version: v2 repo.url: "https://github.com/acme/orders" ext.cmdb.ci: "CI28304858" base: "/orders" "Order management service"}Each field is key: value where:
- Key is an identifier or a dotted path (
repo.url,ext.cmdb.ci). Dotted keys are equivalent to a nested object structure; tooling treats them uniformly. - Value is one of four scalar types:
- identifier —
Commerce,v2 - quoted string —
"any text", multi-line allowed - number —
42,3.14 - boolean —
true,false
- identifier —
That’s the complete value language. There are no nested objects, no per-field type system, no generics, no user-defined value shapes. Tooling may interpret values by convention (URLs become clickable, dotted IDs become copy-able) but the parser treats them all as scalars.
Mindset shift. If you’re coming from configuration languages like HCL, JSON, or YAML, the absence of nested objects can feel limiting. It’s deliberate. Architecture descriptions don’t need configuration’s expressivity; what they need is a flat key-value layer that’s trivial to query and group. The dotted-key form (
repo.url) covers what nested objects would.
Bare-string descriptions
A bare string is a field with a reserved meaning — it’s the description:
module Payments { "Core payment processing service for the platform."}A body may have multiple bare-string descriptions; they concatenate with \n in declaration order. This lets you write multi-paragraph descriptions without one huge multi-line string. Descriptions render as markdown plus two extensions — see Chapter 10.
Labels
Labels go inside a labels { } block:
module Payments { team: Payments
labels { domain: Payments security.zone: PCI network.segment: Internal criticality: High }}Each key: value inside labels { } is one label. Keys can be dotted (security.zone, network.segment); values use the same scalar types as fields.
Labels:
- Cascade. A label declared on a parent module is visible on every nested module, facet, and interface — unless they declare the same label themselves. This is how a
domain: Paymentson the top-level module reaches every operation inside without repetition. See Chapter 18. - Drive views.
focus domain: Paymentsworks becausedomainis a label.group by teamworks too — views accept field paths in addition to label paths, andteamis a cascade field. - Drive checks. Validators read labels: every PCI service must have a recorded
security.contact, no service innetwork.segment: DMZmay call a service innetwork.segment: Internal.
Field or label?
The rule of thumb again, with examples:
| Use a label when | Use a field when |
|---|---|
You’d ever group by it | The value is a single fact unique to this thing |
You’d focus a view on it | The value is a URL, ID, version, or count |
| It’s a classification axis | It’s an identifier or pointer |
Examples: domain, security.zone, criticality | Examples: team, repo.url, version, ext.cmdb.ci, base |
team sits squarely on the field side — the stdlib defines it as required cascade team, a plain cascade field. The reason it gets used in group by team is that views accept field paths as well as label paths; the choice between “field for team” and “label for team” was made once in the stdlib so every project shares the same convention.
Note one practical detail: labels are always cascade-override in propagation behavior. You can’t have an “append” label. If you need accumulation (a list of tags that grows from parent to child), use a field with the append modifier — see Chapter 18.
Multi-line strings
Quoted string values can span multiple lines. The lexer auto-dedents:
module Payments { " Core payment processing. Owns authorization, capture, and refund. Publishes events to the order pipeline. "}A leading whitespace-only line is dropped; the minimum indent shared by remaining lines is stripped. The above produces the value "Core payment processing.\nOwns authorization, capture, and refund.\nPublishes events to the order pipeline.".
The only recognized escape is \".
Triple-quoted strings
For values that contain double quotes (HTML, JSON snippets, regex), use triple-quoted strings. Backslashes are not escape sequences inside them:
module Cart { widget: """ <div class="card" data-name="{{name}}"> <span class="label">{{team}}</span> </div> """}Triple-quoted strings are raw. They don’t support ${expr}-style interpolation (so future template-literal features can be added later without conflicting).
No props { } block
Earlier Archlang had a props { } block to hold “extra metadata.” That’s gone. The role is fully covered by fields with dotted keys: ext.cmdb.ci: "...", repo.url: "...". If you’re reading old files with props { }, run the formatter — it’ll lift them out.
Summary
- Fields describe what a thing is. They live directly in the body, no surrounding block.
- Labels classify a thing along an axis. They live inside
labels { }. - Values are scalars: identifier, quoted string, number, boolean. No nested objects.
- Bare-string descriptions are reserved; multiple bare strings concatenate.
- Labels drive views and cascade through nesting; if you need accumulation, use a field with
append. - The choice “field or label?” is usually: would you
group byorfocuson it?
What’s next
Chapter 10: Descriptions → — the markdown subset and cross-reference syntax inside description strings.