Files
mesh-catalog/modules/lavinmq/index.ts
T
jschoubben 97707480d6 Add lavinmq amqp provider and amqp-ping consumer
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
2026-09-06 15:50:19 +02:00

26 lines
1.1 KiB
TypeScript

// lavinmq's events entrypoint, loaded by the per-node tool host (the provisioner container runs
// ./provisioner separately). The broker lifecycle events are EMITTED from the provisioner, where the
// lifecycle actually happens (novox/hq ADR 0041/0042):
// module.lavinmq.amqp.provisioned — a consumer's vhost + user was created
// module.lavinmq.amqp.deprovisioned — that vhost + user was removed
// Here in the tool host we react to them, keeping a lightweight audit trail of who was granted a
// broker and who lost one — observability the provider itself is best placed to log.
import { on } from "@novox/mesh-sdk/events";
interface AmqpEvent {
consumer?: string;
user: string;
vhost?: string;
}
await on<AmqpEvent>("module.lavinmq.amqp.provisioned", async (e) => {
console.log(`[lavinmq] broker provisioned for ${e.body.consumer ?? "?"} (user ${e.body.user}, vhost ${e.body.vhost})`);
});
await on<AmqpEvent>("module.lavinmq.amqp.deprovisioned", async (e) => {
console.log(`[lavinmq] broker deprovisioned (user ${e.body.user})`);
});
console.log("[lavinmq] auditing broker lifecycle events");