Issue 049 — a module serves tools and nothing may call them

A module's broker account is scoped to what it declares it emits and consumes. A
tool call needs a reply queue, which that scope does not cover and should not. So
the account is right, the request is reasonable, and no account exists that can
make it — asking a module its own question, from its own container, with its own
credential, is refused.

It matters because a module's tools are its operator-facing surface: the
catalogue serves the five questions it exists to answer and nothing can reach
them. It is also why a running module keeps being mistaken for a working one — a
test that cannot ask anything checks a container is up, and that substitution has
hidden two faults this week.

Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
2026-09-14 22:08:40 +02:00
parent ee70d5b451
commit 0aa8f62e0c
@@ -0,0 +1,72 @@
---
status: open
opened: 2026-09-14
located-in: []
fixed-by:
amended-design:
---
# 049 — A module can serve tools, and nothing is allowed to call them
## Symptom
A module serves tools over the broker. Asking it one, from inside that same module's own container,
using its own credential, is refused:
```
Error: Channel closed by server: 403 (ACCESS-REFUSED) with message
"ACCESS_REFUSED - User '<node>-<module>' doesn't have permissions to queue 'amq.gen-...'"
```
A module's broker account is scoped to what it declares it emits and consumes. A tool call is a
request and a reply, and the reply arrives on a temporary queue the caller creates — which that
scope does not cover, and should not: a module that only publishes events has no business declaring
queues.
So the account is right and the request is reasonable, and there is no account that can make it.
## Why this matters
**A module's tools are its operator-facing surface, and nothing can reach it.** The catalogue serves
five: what this mesh holds, what a module is made of, what provides a given provision, what depends
on a module, and what is stale. Those are the questions the catalogue exists to answer, and today
the answer to "how does anyone ask one" is that they run a container joined to the broker's network
namespace and authenticate as the substrate's bootstrap admin.
**It is why a running module gets mistaken for a working one.** A test that cannot ask a module
anything checks that its container is up, and a container being up is not a claim about function.
That substitution has now hidden two separate faults in one week — a crash-looping runtime beside a
healthy container of a similar name, and a catalogue that was never installed at all.
**The mesh already has the shape for this.** An account scoped to a purpose, minted by the mesh and
sealed to a holder, is exactly what provisioning does. What is missing is the recognition that
*asking* is a use of a module, the way pulling is a use of the artifact store
([042](../042-nothing-gives-a-node-an-account-for-a-registry/00-report.md)) rather than something
that happens beneath it.
## Evidence
The refusal above, from a module invoking its own tool with the credential the mesh gave it. The
working alternative, which is the measure of the gap:
```
docker run --rm --network container:<the broker> \
-e MESH_BROKER_URL=amqp://<bootstrap admin>@127.0.0.1:<port>/ \
<the module's image> invoke <module> <tool> '{}'
```
That is the bootstrap credential, host-local, with no scope at all. It works, and nothing about it
should be how a mesh is asked a question.
## Open questions
- **Who is the caller?** An operator at a terminal, an agent acting for one, and another module are
three different holders with three different scopes, and only the last resembles anything the
mesh mints today.
- **Should calling be a grant like any other** — 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 queue?
- **Should the control plane be the way in?** It holds an admin connection already, and a
`mesh-control` subcommand for asking a module a question would need no new account — at the cost
of routing every question through one process and its privileges.
- **What does a module declare about being asked?** Serving a tool is already declared. Whether it
may be asked by anyone who can reach the broker, or only by named holders, is not.