Blueprint file format
A Blueprint is a named, integer-versioned composition of Components. It isn't
annotated collector config. It lists per-signal lanes under the upstream
signal names, each an explicitly ordered list of Component references, plus
one collector-wide extensions block.
Blueprints live at teams/<team>/blueprints/<name>.yaml and shared Components
at teams/<team>/components/<name>.yaml. See
Estate layout for the rules every authored file follows.
To load a tree, run blueprint-check. It prints what loaded and every
finding. telecraft render performs the same load.
Blueprint fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | yes | none | Must equal the filename without its extension. |
version |
integer | yes | none | An integer, 1 or higher. The owner raises it in the same change that alters the Blueprint. |
owner |
string | yes | none | The accountable party. |
satisfies |
list of strings | no | empty | Version-stamped Requirement claims. See Satisfies claims. |
components |
list of Components | no | empty | The Blueprint's local Components, declared inline. |
pipelines |
mapping | no | empty | The four per-signal lanes. See Lanes. |
extensions |
list of entries | no | empty | The collector-wide extensions block. |
A Blueprint with no lane entries and no extensions is a load error, because it would render an empty collector.
The Blueprint's team-qualified id is <team>/<name>, taken from the file's
place in the layout.
# teams/data-flow/blueprints/gateway-standard.yaml
name: gateway-standard
version: 4
owner: gateway-owners
components:
- name: otlp-in
class: receiver
type: otlp
version: 1
config:
protocols:
grpc: {}
- name: guard
class: processor
type: memory_limiter
version: 2
config:
check_interval: 1s
limit_mib: 512
- name: batcher
class: processor
type: batch
version: 1
- name: health
class: extension
type: health_check
version: 1
pipelines:
traces:
- component: otlp-in
- component: guard
- component: infosec/pii-redaction@3
- component: batcher
- component: data-flow/gateway-exporter@2
logs:
- component: otlp-in
- component: guard
- component: batcher
- component: data-flow/gateway-exporter@2
extensions:
- component: health
Component fields
One schema serves both kinds of Component. A shared Component is a standalone
file with a required owner and the team-qualified id <team>/<name>. A
local Component sits inline in a Blueprint's components: list, belongs to
the Blueprint's owner, and can't be referenced from outside that Blueprint.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | yes | none | On a shared Component, must equal the filename without its extension. On a local Component, the name that lane entries reference. |
class |
string | yes | none | One of receiver, processor, exporter, connector, extension. |
type |
string | yes | none | The Catalogue type this Component configures. |
version |
integer | yes | none | An integer, 1 or higher. |
owner |
string | shared only | none | Required on a shared Component, forbidden on a local one. |
config |
mapping | no | empty | The otelcol configuration body, written as is under the rendered id. |
A Component says what it is. The Tier that uses it decides which Catalogue version judges it.
# teams/data-flow/components/gateway-exporter.yaml
name: gateway-exporter
class: exporter
type: otlphttp
version: 2
owner: gateway-owners
config:
endpoint: https://gateway.internal:4318
Rendered ids
Each placed Component renders under a type/name id: a shared Component as
<type>/<team>.<name>, a local one as <type>/<name>. The id carries the
provenance. If two rendered ids collide, the render refuses.
Lanes
pipelines accepts exactly four keys, named after the upstream signal names.
Each is optional and defaults to empty. Any other lane name is an unknown
field and fails the load.
| Key | Contains |
|---|---|
traces |
Ordered entries for the traces pipeline. |
metrics |
Ordered entries for the metrics pipeline. |
logs |
Ordered entries for the logs pipeline. |
profiles |
Ordered entries for the profiles pipeline. |
extensions sits beside pipelines, not inside it, and holds the
collector-wide extensions block. Findings report the extensions block under
the lane name extensions.
The renderer never re-sorts a lane. What you write is what renders. If an order contradicts a rule, the renderer reports a finding instead. See Ordering findings.
Lane entries
Every lane entry has exactly two fields. An entry can only point at a Component, so you can't write a raw inline otelcol block.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
component |
string | yes | none | The Component reference. |
track |
string | no | empty | The only accepted value is head. Empty means pinned. |
Reference syntax
A reference is an id, never a path:
| Form | Means |
|---|---|
<name> |
A local Component of this Blueprint. |
<team>/<name>@<version> |
A shared Component pinned to that version. |
<team>/<name> with track: head |
A shared Component tracking the owning team's head. |
The pin after @ is a positive integer. Anything else is a load error.
The loader enforces these rules on each reference:
- A shared reference either pins a version or sets
track: head. Doing neither is a load error: shared references pin by default, so write the pin. - Doing both is a load error.
- A local reference must not carry a pin. A local Component travels with its Blueprint, so there's nothing for a pin to point at.
- A local reference must not set
track: head. A local Component has no head apart from the Blueprint it lives in. - A local reference must name a Component this Blueprint declares.
- The same id appearing twice in one lane is a load error.
- A declared local Component that no lane references is a load error.
Satisfies claims
Each satisfies entry is the string <requirement-id>@<version>: the
Requirement id the Blueprint claims to satisfy, stamped with the Requirement
version the claim was made against. An unversioned claim is a load error,
because drift couldn't be detected. Claiming the same Requirement twice is a
load error.
A claim states intent. The evaluator always judges against the Requirement's
current version, so a claim can't freeze the rule at an older version. A
claim stamped behind the current version is what library_drift detects. See
Requirements.
satisfies:
- logs-delivered@1
- service-name-on-logs@1
Load errors
The load fails closed and returns nothing. Every message names the file, and the loader reports every problem it found rather than stopping at the first. The load refuses on:
- 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 missing
owner, or aversionbelow 1 - a
namethat contradicts the filename - a
classoutside the five pipeline classes, or an emptytype - an
owneron a local Component - a duplicate local Component name, or a duplicate object id across the source roots
- any of the reference rules above
- a source root with no
teams/tree, or a tree with nothing authored at all
Findings
Problems that cross object boundaries don't block the load. They surface as findings, route to the owner of the Blueprint they're about, and name the lane, so the fix is an edit to one list. One team's retraction never stops another team's render.
Each finding carries a kind, the Blueprint id, the lane, and a message.
Reference findings
Kind reference. Raised when a structurally valid reference can't deliver
what it promises:
- The referenced shared Component doesn't exist: it was never authored, or it has since been retracted.
- The pin is ahead of the owning team's current version, so the pinned version doesn't exist at head.
- An entry in the
extensionsblock references something that isn't an extension. - An entry in a signal lane references an extension. Extensions are collector-wide and never live in a signal lane.
A pin behind head is not a reference finding. That's library_drift, which
has its own detection.
Ordering findings
Kind ordering. Raised when a lane's explicit order contradicts an ordering
rule keyed on a Catalogue type. Position is judged among the same-class
entries of that lane, because that's how the rendered pipeline executes. The
extensions block is never judged: extensions have no pipeline order.
The rules that ship:
| Class | Type | Slot | Why |
|---|---|---|---|
processor |
memory_limiter |
first |
Back-pressure must engage before any other processor buffers or fans out. |
processor |
batch |
last |
Batching belongs after every shaping processor, so the exporter sees the final shape. |
A lane entry that doesn't resolve is skipped: its problem is already a reference finding.