Glossary, the mesh-controller/foundation vocabulary, ADR 0076, Phase 3 closed #43
@@ -0,0 +1,117 @@
|
||||
---
|
||||
topic: the tiers
|
||||
status: proposed
|
||||
date: 2026-09-15
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
extends: 0039-what-the-sdk-holds-and-refuses.md
|
||||
---
|
||||
|
||||
# 74. The wire is specified; an SDK is whatever passes the conformance suite
|
||||
|
||||
## Context
|
||||
|
||||
[ADR 0039](0039-what-the-sdk-holds-and-refuses.md) says what the SDK holds: the tool-serving
|
||||
harness, the messaging and event framework, the contracts, and core primitives. It settles what
|
||||
belongs in *an* SDK. It does not say what happens when there is more than one.
|
||||
|
||||
There is already more than one. **The contracts are expressed twice** — as Go types in the control
|
||||
plane and the host, and as TypeScript types in the SDK — and nobody has felt it because both live
|
||||
in one repository and one head.
|
||||
|
||||
**They already disagree.** Not in some future where a second language is added; today:
|
||||
|
||||
| | TypeScript | Go |
|
||||
|---|---|---|
|
||||
| the provision's field | `resource` | `Provision` |
|
||||
| what `consumer` means | **the module** | **the node**; the module is `From` |
|
||||
| event headers | six, including `x-causation-id` and `x-schema` | four — the other two are never written |
|
||||
|
||||
So one word means two things in the two halves of one mesh, and the header that exists so a body's
|
||||
shape can change without silent misreads is declared on one side and emitted by neither.
|
||||
|
||||
A failure of this kind does not announce itself. Two implementations that disagree about an
|
||||
envelope do not fail to compile — they ignore each other's messages, and a mesh where a module
|
||||
stops reacting looks exactly like a mesh where nothing happened.
|
||||
|
||||
## The question this settles
|
||||
|
||||
A module may be written in any language the mesh can build
|
||||
([`18-building-a-module`](../03-DESIGN/01-to-be/18-building-a-module.md)). Every language needs an
|
||||
SDK. What is an SDK *of*?
|
||||
|
||||
Two answers were available, and the obvious one is wrong.
|
||||
|
||||
**Shared types, generated.** Write the shapes once — a schema, an IDL — and generate Go, TypeScript,
|
||||
Rust. It is the familiar answer and it solves the smaller half of the problem. The shapes are not
|
||||
where the difficulty is.
|
||||
|
||||
**A specified wire, with a conformance suite.** The shapes are a consequence; what an SDK must get
|
||||
right is *behaviour*.
|
||||
|
||||
## Decision
|
||||
|
||||
**The wire is specified, and an SDK is any implementation that passes the conformance suite.**
|
||||
|
||||
What the specification covers is what two implementations can disagree about:
|
||||
|
||||
- **the exchanges and queues** — which exchanges exist, that a consumer's queue is durable and
|
||||
named `<node>.<module>.events`, that a tool is served from a shared durable `serve.<key>`
|
||||
- **the envelope** — every header, which are required, what an unknown `x-` header means, and that
|
||||
ignoring one is correct rather than lax
|
||||
- **identity** — that a module's node and module name come from its sealed credential and not from
|
||||
its environment, so what it emits matches what the mesh authorised
|
||||
- **delivery** — at-least-once, and that dedup is on `x-event-id`, which only the emitter can make
|
||||
- **the credential** — the sealed document's fields, and that a connection pins a certificate
|
||||
fingerprint rather than trusting an authority
|
||||
- **provisioning** — a grant in, a credential out, and what each carries
|
||||
- **the vocabulary** — that `consumer` is one thing, named once
|
||||
|
||||
**And the suite is executable, not prose.** A specification nobody can run is a document two
|
||||
implementations drift from while both believe they conform. Conformance is a set of fixtures — an
|
||||
emitted event, a served tool call, a grant and its answer — that every SDK must produce and consume
|
||||
byte-for-byte.
|
||||
|
||||
**The existing two implementations are the first two to be made to pass it.** Not a future language:
|
||||
the drift above is present, and a suite that only new SDKs must satisfy would leave the disagreement
|
||||
that already exists in place while certifying everything added afterwards against it.
|
||||
|
||||
## Why not generated types
|
||||
|
||||
Generation makes the shapes agree and leaves everything that matters unspecified. Two SDKs
|
||||
generated from one schema can still name their queues differently, take identity from the
|
||||
environment, dedup on the wrong field, or omit a header the other requires — and every one of those
|
||||
is a mesh that runs and quietly does not work.
|
||||
|
||||
It also makes the contract into whatever the generator supports, which is a decision nobody made
|
||||
about a boundary everything else depends on.
|
||||
|
||||
**The shapes are worth generating once the wire is specified.** That is a convenience, and it comes
|
||||
second.
|
||||
|
||||
## Consequences
|
||||
|
||||
**A language is a commitment, and now a measurable one.** Adding one means implementing the wire
|
||||
and passing the suite. That is more work than transliterating types, and it is the work that was
|
||||
always there — the difference is that it can now be finished rather than believed.
|
||||
|
||||
**Versioning becomes possible.** `x-schema` exists for it and is never written. A specified envelope
|
||||
with a version on the body is what lets a mesh hold a module built against an older SDK, which is
|
||||
the ordinary state of any mesh that has been running for a while.
|
||||
|
||||
**The two current implementations will be found wrong.** They disagree, so at least one is. Fixing
|
||||
that is the point rather than a cost, but it is not free: something is emitting or expecting
|
||||
something it should not.
|
||||
|
||||
**This does not make the mesh polyglot by itself**, and should not be reported as though it does. It
|
||||
makes polyglot possible to do correctly. A Rust SDK is still a Rust SDK.
|
||||
|
||||
## How this is checked
|
||||
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| One vocabulary | A word means one thing across implementations, checked by the fixtures using it. |
|
||||
| The wire is what is specified | Both existing SDKs run the conformance suite in their own test suites, and a change to one that breaks a fixture fails there rather than in a mesh. |
|
||||
| A new SDK is a passing SDK | A language is not listed as buildable until its SDK passes; the toolchain list and the conformance results name the same set. |
|
||||
| An unknown header is ignored | A fixture carries one, and every implementation accepts it. |
|
||||
| Identity comes from the credential | A fixture sets an environment that disagrees with the credential, and the emitted event carries the credential's. |
|
||||
@@ -106,6 +106,7 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0071** — [Genesis clones from a mesh, and checks what it got](0071-where-genesis-gets-its-source.md)
|
||||
- **0072** — [Two graphs, and a build chain that orders itself](0072-two-graphs-and-the-build-chain.md)
|
||||
- **0073** — [The installer carries a builder, and the registry stays where it is](0073-the-installer-carries-a-builder.md)
|
||||
- **0074** — [The wire is specified; an SDK is whatever passes the conformance suite](0074-the-wire-is-specified-not-the-types.md) *(proposed)*
|
||||
|
||||
### What runs on them, and how it gets there
|
||||
|
||||
|
||||
Reference in New Issue
Block a user