Files

3.7 KiB


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) 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.