server is now the container the foundation raised — name mesh-broker, the same
pinned upstream lavinmq image, the same TLS args, ports and volumes — so the
applier adopts it in place. The second server, the lavinmq.ini bootstrap and the
module's own network are gone. The provisioner is host-networked to the broker's
loopback management (127.0.0.1:15672) and authenticates as lavinmq's default
guest, which the foundation broker runs with; the mesh bus stays on the / vhost,
a vhost-per-consumer beside it. One lavinmq now.
Issue 051 (WBS 3.2).
Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
The mesh refused to place it — two of its three containers named
mesh-runtime-lavinmq@sha256:000…0, "a placeholder digest, which is never a real
image". That refusal was right, and the belief behind the placeholder was that
lavinmq needs no building because its broker is an upstream image.
The broker is upstream. The module is not the broker. It carries a run-once
bootstrap that writes the broker's configuration before it first starts, a
provisioner that grants each consumer its own vhost and user, a set of tools and
an event consumer — all of it this module's own TypeScript, and none of it
producible by naming somebody else's image.
So it gets what every module with code of its own gets: a Dockerfile standing on
the shared toolchain and runtime bases, a build block naming them, and containers
that name the artifact rather than a digest nothing can produce. Same recipe as
postgres, which is the converted module closest in shape — it has a provisioner
too.
Noted and deliberately not changed: postgres runs its provisioner from its
container's args, and lavinmq's equivalent container names none, so on this
manifest the provisioner is never started. That may be why, or may be a second
fault; it is left alone so the next run says which.
Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
lavinmq becomes a provider of a user-facing amqp interface: a consumer
that requires a message queue is given its OWN broker — a scoped vhost
and user on a lavinmq provider — not an account on the mesh's own
control-plane broker (ADR 0048). Vhost-per-login is the isolation model,
the exact analog of postgres's database-per-login: the provider names a
vhost after the consumer's login and a user with full rights on that
vhost and none elsewhere, so a login is a broker the consumer alone can
reach.
The provider drives lavinmq through its HTTP management API (client.ts,
the module's one impure seam), with a run-once bootstrap that computes
the RabbitMQ-compatible password hash lavinmq's config wants from the
plain admin secret the mesh mints — the value no ${secret:...}
placeholder can produce and the reason the bootstrap exists (ADR 0052).
serves.amqp carries the port so consumers reference ${bound:amqp:port}.
amqp-ping is a demo consumer: it contributes nothing (the vhost is the
login), reads its grant from an env-file the mesh fills, and uses
${bound:amqp:as} for BOTH its username and its vhost — the db-name
lesson applied to AMQP. It speaks AMQP 0-9-1 over a raw socket with no
npm dependency (the way redis speaks RESP) and round-trips one message.
It carries a slug so its identity fits the 20-char backend bound
(ADR 0049).
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF