broker: a generic module account + module issue (ADR 0048) #3

Closed
jschoubben wants to merge 4 commits from events/module-broker-account into claude/manifest-events
Owner

Implements ADR 0048's control-plane half — a module's broker account, scoped by its manifest.

  • CreateModuleAccount — permissions are the manifest: read mesh.events + its own <node>.<module>.events queue if it consumes; write mesh.events only if it emits; nothing else.
  • EnsureEventExchanges — the substrate's exchanges (mesh.events/mesh.rpc/mesh.events.dead + retention queue), idempotent.
  • EnsureModuleQueue — the substrate pre-declares a consumer's dead-lettered queue, because LavinMQ refuses a non-administrator declaring one; the runtime then passively checks it.
  • module issue <module> --node <m> — looks up emits/consumes, ensures the bus, creates the account, and seals an amqps {url,fingerprint,node,module} as the module's broker own-secret.

Scope tested as patterns; every management call verified against a real LavinMQ; the whole flow proven in the lab (mesh-lab: the assigned audit-logger passes 1/1).

Honest limit, recorded in the code: LavinMQ has no topic permissions, so ADR 0047's emit-origin reservation (module.<self>.*) is stamped by the sdk, not broker-enforced.

Stacked on claude/manifest-events (the emits/consumes fields, PR #2).

https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF

Implements ADR 0048's control-plane half — a module's broker account, scoped by its manifest. - **`CreateModuleAccount`** — permissions **are** the manifest: read `mesh.events` + its own `<node>.<module>.events` queue if it consumes; write `mesh.events` only if it emits; nothing else. - **`EnsureEventExchanges`** — the substrate's exchanges (`mesh.events`/`mesh.rpc`/`mesh.events.dead` + retention queue), idempotent. - **`EnsureModuleQueue`** — the substrate pre-declares a consumer's dead-lettered queue, because LavinMQ refuses a non-administrator declaring one; the runtime then passively checks it. - **`module issue <module> --node <m>`** — looks up emits/consumes, ensures the bus, creates the account, and seals an amqps `{url,fingerprint,node,module}` as the module's broker own-secret. Scope tested as patterns; every management call verified against a real LavinMQ; the whole flow proven in the lab (mesh-lab: the assigned audit-logger passes 1/1). **Honest limit, recorded in the code:** LavinMQ has no topic permissions, so ADR 0047's emit-origin reservation (`module.<self>.*`) is stamped by the sdk, not broker-enforced. Stacked on `claude/manifest-events` (the emits/consumes fields, PR #2). https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
jschoubben added 4 commits 2026-09-04 00:13:29 +00:00
CreateModuleAccount gives an assigned module its own broker account whose
permissions ARE its manifest: declare and read its own <node>.<module>.events
queue, read the events exchange to bind onto if it consumes, write the events
exchange only if it emits. The account name carries the node (sealed per
machine), the permissions carry the module (one cannot read another's queue).
The builder becomes one instance of this rule rather than a separate kind.

EnsureEventExchanges declares the bus the substrate owns — mesh.events,
mesh.rpc, mesh.events.dead + a retention queue — idempotently, since a module
account may not declare an exchange.

Scope tested as patterns (no broker needed), and every management call verified
against a real LavinMQ. Honest limit recorded in the code: LavinMQ has no topic
permissions, so ADR 0047's emit-origin reservation (module.<self>.*) is stamped
by the sdk, not enforced by the broker; a pure consumer like the audit logger
is unaffected.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
'module issue <module> --node <m>' looks up the module's emits/consumes from
the catalogue, ensures the bus exchanges exist, creates its scoped account
(CreateModuleAccount), and seals an amqps {url,fingerprint} to the node as the
module's broker own-secret — the same delivery as 'builder issue', now generic.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
LavinMQ refuses a non-administrator declaring a queue with a dead-letter
exchange, so a scoped module cannot make its own. EnsureModuleQueue declares
<node>.<module>.events with its DLX as the mesh, and 'module issue' does so
for a consuming module — the runtime then passively checks it rather than
declaring. Verified against a real broker: the scoped account binds and
consumes the pre-declared queue.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
So the runtime knows the identity the account was scoped to, without a
manifest naming the node.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
jschoubben closed this pull request 2026-09-05 01:19:51 +00:00

Pull request closed

This pull request cannot be reopened because the branch was deleted.
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-controller#3