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:
@@ -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.
|
||||
Reference in New Issue
Block a user