ADR 0095: the control plane is the way to ask a module; issue 049 resolved; design 19 amended

This commit is contained in:
2026-09-21 20:38:23 +02:00
parent acc9824949
commit 39a01b7f7e
5 changed files with 79 additions and 14 deletions
@@ -0,0 +1,60 @@
---
topic: the tiers
status: accepted
date: 2026-09-21
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md
---
# 95. The control plane is the way to ask a module
## Context
A module serves tools over the broker under an account scoped to what it emits, consumes and
serves ([ADR 0047](0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md)). A
tool call is a request and a reply: the caller creates a reply queue and publishes to the serving
module's request key, and no module's scope grants either — nor should it, since a module that
only publishes events has no business declaring queues. So a module could serve tools and nothing
in the mesh could call them
([issue 049](../04-ISSUES/049-a-module-can-serve-tools-and-nothing-can-call-them/00-report.md)):
not an operator at a terminal, not an agent acting for one.
## Considered Options
1. **Calling is a grant**: a module declares it may be asked, and a consumer is issued an
account that may create a reply queue and publish to that module's request key. Deferred: a
module-to-module call is the only caller that resembles what the mesh mints today, and none
asks for one yet.
2. **The control plane is the way in.** Adopted. It holds a connection that may already, so a
person or an agent asks through it, and every question passes one process — which is where
an audit of who asked what belongs.
## Decision
`mesh-controller ask <module> <tool> [json]` publishes the request on the tool exchange under
`<module>.<tool>`, with a private reply queue bound under its own name, waits for the answer
whose correlation matches, and prints it as the module gave it. A tool that answered with an
error has answered: the answer is printed and the exit status says so. A module that never
answers is said to have not answered, with where to look.
A module declares nothing about being asked: serving a tool is being askable through the control
plane. A module-to-module call, if one is wanted, is a grant like any other and a later decision.
## Consequences
Anything with the control plane in reach can ask any module anything it serves. What got
harder: nothing outside the control plane can, and the control plane's connection is one more
thing on the path of every question — a cost accepted for the audit it buys.
## How it is checked
A tools-only bed asks a served tool through the control plane and asserts an answer arrived —
an error, since the lab has no upstream and no token, which is an answer where a timeout would
not be.
## References
- [issue 049](../04-ISSUES/049-a-module-can-serve-tools-and-nothing-can-call-them/00-report.md)
- [ADR 0047](0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md)
- [`03-DESIGN/01-to-be/19-the-module-protocol.md`](../03-DESIGN/01-to-be/19-the-module-protocol.md)
+1
View File
@@ -114,6 +114,7 @@ python3 00-META/checks/index.py fail if stale
- **0079** — [The foundation seats are named after their servers](0079-the-foundation-seats-are-named-after-their-servers.md) - **0079** — [The foundation seats are named after their servers](0079-the-foundation-seats-are-named-after-their-servers.md)
- **0092** — [An operator delivers a pair credential, and the mesh never replaces it](0092-an-operator-delivers-a-pair-credential.md) - **0092** — [An operator delivers a pair credential, and the mesh never replaces it](0092-an-operator-delivers-a-pair-credential.md)
- **0094** — [A module may hold several secrets from one provider, each a pair of its own](0094-a-module-may-hold-several-secrets-from-one-provider.md) - **0094** — [A module may hold several secrets from one provider, each a pair of its own](0094-a-module-may-hold-several-secrets-from-one-provider.md)
- **0095** — [The control plane is the way to ask a module](0095-the-control-plane-is-the-way-to-ask-a-module.md)
### What runs on them, and how it gets there ### What runs on them, and how it gets there
+11 -8
View File
@@ -5,8 +5,9 @@ code:
- mesh-sdk src - mesh-sdk src
- mesh-tools src/broker-amqp.ts - mesh-tools src/broker-amqp.ts
- mesh-controller internal/link - mesh-controller internal/link
updated: 2026-09-15 updated: 2026-09-21
decisions: decisions:
- 02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md
- 02-DECISIONS/0074-the-wire-is-specified-not-the-types.md - 02-DECISIONS/0074-the-wire-is-specified-not-the-types.md
- 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md - 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md
- 02-DECISIONS/0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md - 02-DECISIONS/0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md
@@ -137,13 +138,15 @@ A module's tools are its operator-facing surface.
into any queue on the broker. into any queue on the broker.
- A caller needs a **reply queue**, and that is what a module's scoped account may not declare - A caller needs a **reply queue**, and that is what a module's scoped account may not declare
([issue 049](../../04-ISSUES/049-a-module-can-serve-tools-and-nothing-can-call-them/00-report.md)). ([issue 049](../../04-ISSUES/049-a-module-can-serve-tools-and-nothing-can-call-them/00-report.md)).
So a module may serve tools and may not call them, and nothing today issues an account to anything So a module may serve tools and may not call them.
that wants to ask. - **The control plane is the way to ask**
([ADR 0095](../../02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md)):
### Not yet true `ask <module> <tool> [json]` publishes on `mesh.rpc` under `<module>.<tool>` with a private reply
queue bound under its own name, and prints the answer as the module gave it. A module declares
The caller's half has no account. Until that is settled, the only thing that can ask a module a nothing about being asked — serving a tool is being askable through the control plane. A
question is the foundation's bootstrap admin, which is not a protocol so much as a way in. module-to-module call, if one is wanted, is a grant like any other and a later decision.
*How it is checked:* a tools-only bed asks a served tool through the control plane and asserts
an answer arrived, where a timeout would read differently.
--- ---
@@ -1,9 +1,9 @@
--- ---
status: located status: resolved
opened: 2026-09-14 opened: 2026-09-14
located-in: [mesh-controller cmd/mesh-controller, mesh-tools (the request contract)] located-in: [mesh-controller cmd/mesh-controller (ask), mesh-controller internal/link]
fixed-by: fixed-by: ADR 0095; mesh-controller multiple-fixes (ask: the control plane publishes the request with a private reply queue and prints the answer); proven by the confluence bed
amended-design: amended-design: 03-DESIGN/01-to-be/19-the-module-protocol.md
--- ---
# 049 — A module can serve tools, and nothing is allowed to call them # 049 — A module can serve tools, and nothing is allowed to call them
@@ -14,5 +14,6 @@
like any other, and a later decision. like any other, and a later decision.
**Located in:** the controller (a command speaking the tool request/reply over its own connection) **Located in:** the controller (a command speaking the tool request/reply over its own connection)
and the tool runtime's request contract. Not fixed here: it is a new command against a protocol and the tool runtime's request contract. Fixed as
the runtime owns, and needs a lab run against a tools-only bed to be proven. [ADR 0095](../../02-DECISIONS/0095-the-control-plane-is-the-way-to-ask-a-module.md): `ask`,
proven by the confluence bed asking a served tool and getting its answer.