Tier file format
A Tier is one position in your collection topology and the unit the renderer works in: one rendered artefact per Tier. It declares exactly one Environment and binds exactly one Blueprint version.
Tiers live at teams/<team>/tiers/<name>.yaml. Services and Rollouts load
from the same tree and are documented here, because a Service's Paths decide
how strictly a Tier is judged and a Rollout dual-binds a Tier. See
Estate layout for the rules every authored file follows.
Tier fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | no | the filename | Must equal the filename without its extension when present. |
owner |
string | yes | none | The accountable party. |
environment |
string | yes | none | The one Environment this Tier declares. |
blueprint |
string | yes | none | The Blueprint binding, <team>/<name>@<version>. |
selector |
map of string to string | no | empty | Equality selector over reported identifying attributes. |
min_expected |
integer | no | 0 |
The declared Population floor. Zero means no declared floor. |
serving |
mapping | no | absent | When present, marks the Tier as served over OpAMP. Holds one field, endpoint. |
hops |
list of mappings | no | empty | Directed edges arriving at this Tier. Each has from and trusted. |
live_check |
mapping | no | absent | When present, opts the Tier in to the Live-check tap. Holds one optional field, sample_percent. |
The Tier's team-qualified id is <team>/<name>, taken from the file's place
in the layout.
# teams/data-flow/tiers/gateway.yaml
owner: gateway-owners
environment: production
blueprint: data-flow/gateway-standard@4
selector:
telecraft.tier: gateway
deployment.environment: production
min_expected: 2
serving:
endpoint: wss://opamp.telecraft.internal/v1/opamp
hops:
- from: internet
- from: data-flow/edge
trusted: true
Environment
environment is a single value you define, aligned to
deployment.environment.name. It describes the infrastructure, so a Tier
declares one and only one. To bind per Environment, author sibling Tiers:
gateway.yaml and gateway-staging.yaml, each with its own Environment and
its own binding.
production is the value that policy defaults attach to: it's the default
Environment lens, it leads every report, and the shipped Stability floors are
defined for it.
A Tier with no environment is a load error.
Blueprint binding
blueprint is the string <team>/<name>@<version>. A binding always pins:
the version after @ is a positive integer, and there's no track-head mode,
so rebinding is an authored, reviewed change.
| Problem | Result |
|---|---|
No @ and version |
Load error. |
| A version that isn't a positive integer | Load error. |
Not of the form <team>/<name> |
Load error. |
| Pinned to a version other than the one at head | binding finding at render. |
The estate tree holds head content, so head is what renders. A pin off head is
visible drift, reported as a binding finding, never a block.
Selector
selector matches every authored pair, by equality, against the identifying
attributes a collector reports. A collector is never authored: it connects,
reports its attributes, and lands in the Tier whose selector its attributes
satisfy. The most specific satisfied selector wins.
The selector also states the Tier's expectation: what shape should match, not how many. A collector matching no Tier selector is served the Unmatched artefact.
| Problem | Result |
|---|---|
| A pair with an empty key or value | Load error: an empty side can never match. |
serving declared with no selector |
Load error: every collector of the Tier would land on the Unmatched artefact. |
min_expected above zero with no selector |
Load error: the floor counts collectors matched by selector. |
Population floors
min_expected is the Tier's declared Population floor: at least this many
collectors should match its selector. It's reviewable in git, which suits
substrates with no queryable inventory.
- It's a floor, never an equality. Surplus is never a finding.
- Zero, the default, means no declared floor.
- A negative value is a load error.
- A live count from the substrate always outranks the declaration: derived beats declared beats absent.
A floor gives two findings their teeth: never_seen, when the selector has
matched no collector in any reading, and under_populated, when collectors
matched but fewer than the floor. Both attach to the Tier and route to the
Tier's owner.
Serving
A serving block marks the Tier as served over OpAMP and makes the renderer
write rendered/<team>/<tier>.supervisor.yaml beside the collector artefact.
| Field | Type | Required | Description |
|---|---|---|---|
endpoint |
string | yes | The OpAMP server endpoint the Supervisor connects to. |
A serving block with no endpoint is a load error. A Tier with no serving
block is delivered from git, which is a fully supported delivery path.
Hops
Each entry of hops is a directed edge arriving at this Tier.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
from |
string | yes | none | The source of the edge: another Tier's id, or a name for the world outside the graph. |
trusted |
boolean | no | false |
Whether data arriving over this edge is trusted. |
Trust belongs to the Hop, not the Tier: one gateway can receive both trusted
and untrusted traffic. An undeclared trusted fails safe to untrusted.
One Tier renders one artefact for all its collectors, so the render can't split intake per Hop: any untrusted arrival makes the whole intake untrusted. The renderer then emits a processor that strips Telecraft's attribute namespace from arriving data, so identity comes from the receiving Tier's own config stamps rather than from inbound data.
A Hop with no from is a load error.
Live-check
A live_check block opts the Tier in to the Live-check tap: its presence
alone is the opt-in, and an empty mapping is a complete one.
| Field | Type | Required | Description |
|---|---|---|---|
sample_percent |
number | no | Overrides the sample rate for this Tier alone, above 0 and at most 100. Absent inherits the estate's rate. |
For each signal lane the Tier's Blueprint wires, the renderer adds a
pipeline named <signal>/telecraft.live-check beside it. The added
pipeline shares the lane's receivers, samples at the resolved rate through
a generated probabilistic_sampler/telecraft.live-check processor, and
exports only through a generated otlp/telecraft.live-check exporter to
the destination declared in live-check.yaml (see
Estate layout), resolved on the Tier's Environment.
The lanes themselves are unchanged: a Tier renders the same data pipelines
with and without the block. Both generated ids are reserved on every Tier,
so an authored Component landing on one is a render error.
The sample rate is the Tier's sample_percent when set, else the estate
file's, else 10. A Tier opting in while live-check.yaml is absent is a
render error naming both files. A sample_percent of zero or below, or
above 100, is a load error.
The live-check service the branch feeds is yours to deploy:
Deploy the live-check service
covers it. A Requirement reads the findings it emits by setting
placement: live.
Service fields
A Service is the governed unit. Its Paths decide which Tiers its Service Class judges, so it loads with the topology.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | no | the filename | Must equal the filename without its extension when present. |
owner |
string | yes | none | The accountable party. |
class |
string | yes | none | The Service Class, such as C1. |
paths |
list of mappings | no | empty | Each has a through list of team-qualified Tier ids. |
# teams/product/services/checkout.yaml
owner: checkout-team
class: C1
paths:
- through: [data-flow/edge, data-flow/gateway]
- through: [data-flow/gateway-staging]
A Path through a Tier nobody authored is a load error, not a finding. A silently dropped Path would relax the floor judgement on the Tiers it crosses. A Path through no Tiers at all is also a load error.
How Paths set a Tier's floor
Stability floors are judged at render, per component and per signal the Tier routes, at the Tier's declared Environment crossed with the strictest Service Class among the Services whose Paths cross that Tier. Adding a C1 Path tightens every Tier it crosses to the C1 floor. Strictness comes from the Paths, so there's nothing to maintain by hand.
The floors that ship: in production, C1 and C2 require beta or better, and
C3 requires alpha or better. Environments absent from the table carry no
floor at all, which is where alpha and development components belong. The
maturity ladder is development < alpha < beta < stable. deprecated
and unmaintained are lifecycle end-states rather than rungs, and are judged
apart from floors.
A breach is a floor finding routed to an owner, never a block.
Rollout fields
A Rollout stages a Blueprint change across a Tier's collectors. It's an
authored, owned object at teams/<team>/rollouts/<name>.yaml that targets
exactly one Tier. Rollouts are optional: the default is to rebind the Tier
directly.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | no | the filename | Must equal the filename without its extension when present. |
owner |
string | yes | none | Must be the target Tier's owner. |
tier |
string | yes | none | Team-qualified id of the one Tier this Rollout stages. |
from |
string | yes | none | The current binding, <team>/<name>@<version>. Must equal the Tier's own binding. |
to |
string | yes | none | The candidate binding. Must name a different Blueprint from from. |
stage |
integer | no | 0 |
Zero-based index of the active stage. |
hash_attributes |
list of strings | when a stage uses percent |
empty | The identifying attributes that fractional membership hashes over, in authored order. |
stages |
list of stages | yes | none | The ordered stage list. At least one. |
Each stage has:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
cohort |
mapping | yes | none | The Cohort spec. At least one form must be present. |
soak |
duration string | no | 0s |
Minimum time the stage must have been active before its advance can be proposed. Zero means no soak gate. |
A Cohort spec has three forms, which you can mix. Membership is their union, so "the three hosts I trust plus 5%" is one stage:
| Field | Type | Description |
|---|---|---|
hosts |
mapping | Listed identifying-attribute values. Holds attribute and values, both required when hosts is present. |
match |
map of string to string | An equality selector over reported identifying attributes, with the same semantics as the Tier selector. |
percent |
integer | A share of the population, 1 to 100, chosen by a stable hash over hash_attributes. The share is statistical, not exact. |
# teams/data-flow/rollouts/gateway-v5.yaml
owner: gateway-owners
tier: data-flow/gateway
from: data-flow/gateway-standard@4
to: data-flow/gateway-next@1
stage: 0
hash_attributes: [host.name]
stages:
- cohort:
hosts:
attribute: host.name
values: [gw-01, gw-02, gw-03]
soak: 24h
- cohort:
percent: 25
soak: 24h
- cohort:
percent: 100
Dual binding
While a Rollout is active, the target Tier is dual-bound. Both artefacts
render at head: the base artefact from from at
rendered/<team>/<tier>.yaml, and the candidate from to at
rendered/<team>/<tier>@next.yaml. The @next artefact exists exactly while
the Rollout does, and is removed with it.
The candidate is judged like any bound Blueprint: floors, the Allow-list block, and the stale-pin finding all apply, because that config is about to run in this Tier.
Every step is a commit on this one small file. To start a Rollout, add the
file. To advance, raise stage. To complete, rebind the Tier to to and
delete the file. To abort, delete the file.
Rollout load errors
Beyond the field rules above, the topology load refuses when:
- The Rollout targets a Tier nobody authored, or a Tier of another team.
- Its
ownerdiffers from the target Tier's owner. - Two Rollouts target the same Tier: one active Rollout per Tier.
fromdoesn't equal the Tier's authored binding. While a Rollout is active, the Rollout file is the only way to change the binding, so rebinding the Tier directly fails render validation.fromandtoname the same Blueprint. The estate tree holds one content per Blueprint id at head, so both artefacts would render identically and the rollout would stage nothing. Author the candidate as a sibling Blueprint.stageis negative or not an index intostages. To complete a Rollout, delete the file rather than counting past the end.- A stage's Cohort spec is empty, its
hostsform lacks an attribute or values, itsmatchform has an empty key or value, or itspercentis outside 1 to 100. - A stage uses
percentbut the Rollout sets nohash_attributes, orhash_attributesholds an empty or duplicated entry.
Topology load errors
The topology load fails closed on every problem, structural or cross-object, and returns nothing. Beyond the per-object rules above:
- An unknown or misspelled field anywhere in the document.
- A malformed document, an empty file, a top-level list, or more than one YAML document in the file.
- A duplicate object id across the source roots.
- A source root with no
teams/tree. - A set of roots holding no Tiers at all: the Tier is the rendering unit, so there'd be nothing to render.