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
This commit is contained in:
2026-09-06 15:50:19 +02:00
parent 090a839d42
commit 97707480d6
13 changed files with 793 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
{
"module": "lavinmq",
"version": "1",
"provides": [
{
"name": "amqp",
"scope": "mesh"
}
],
"capabilities": [
"container-runtime"
],
"emits": [
"module.lavinmq.amqp.provisioned",
"module.lavinmq.amqp.deprovisioned"
],
"consumes": [
"module.lavinmq.amqp.provisioned",
"module.lavinmq.amqp.deprovisioned"
],
"serves": {
"amqp": {
"port": 5672
}
},
"receives": {
"amqp": "/var/lib/lavinmq-module/grants/mesh.json"
},
"grants": {
"amqp": "/var/lib/lavinmq-module/grants"
},
"own-secrets": {
"default": "/var/lib/lavinmq-module/default.secret",
"broker": "/var/lib/mesh/lavinmq/broker"
},
"listens": [
{
"port": 5672,
"protocol": "tcp",
"from": "mesh",
"why": "modules on any machine that were granted a queue"
}
],
"resources": [
{
"id": "mesh-state",
"type": "directory",
"path": "/var/lib/mesh/lavinmq",
"mode": "0700"
},
{
"id": "state",
"type": "directory",
"path": "/var/lib/lavinmq-module",
"mode": "0700"
},
{
"id": "grants-dir",
"type": "directory",
"path": "/var/lib/lavinmq-module/grants",
"mode": "0700"
},
{
"id": "data",
"type": "directory",
"path": "/services/lavinmq/data",
"mode": "0700"
},
{
"id": "net",
"type": "network",
"name": "lavinmq"
},
{
"id": "bootstrap",
"type": "container",
"name": "lavinmq-bootstrap",
"image": "mesh-runtime-lavinmq@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"run-once": true,
"volumes": [
"/var/lib/lavinmq-module:/var/lib/lavinmq-module",
"/var/lib/lavinmq-module/default.secret:/run/secrets/default:ro"
],
"env": {
"MESH_PROVISION_ADMIN_USER": "mesh-admin",
"MESH_PROVISION_PASSWORD_FILE": "/run/secrets/default",
"MESH_LAVINMQ_CONFIG_OUT": "/var/lib/lavinmq-module/lavinmq.ini",
"MESH_LAVINMQ_DATA_DIR": "/var/lib/lavinmq"
},
"args": [
"run",
"/app/modules/lavinmq/dist/bootstrap/index.js"
]
},
{
"id": "server",
"type": "container",
"name": "lavinmq",
"image": "cloudamqp/lavinmq@sha256:3eb54c12916d700a978c2ea86e6362cd4974b0e3189508718006d4e6d341246b",
"network": "lavinmq",
"ports": [
"5672"
],
"volumes": [
"/services/lavinmq/data:/var/lib/lavinmq",
"/var/lib/lavinmq-module/lavinmq.ini:/etc/lavinmq/lavinmq.ini:ro"
],
"args": [
"--config",
"/etc/lavinmq/lavinmq.ini"
]
},
{
"id": "runtime",
"type": "container",
"name": "mesh-lavinmq",
"image": "mesh-runtime-lavinmq@sha256:0000000000000000000000000000000000000000000000000000000000000000",
"network": "lavinmq",
"volumes": [
"/var/lib/mesh/lavinmq/broker:/run/secrets/broker:ro",
"/var/lib/lavinmq-module/grants:/var/lib/lavinmq-module/grants:ro",
"/var/lib/lavinmq-module/default.secret:/run/secrets/default:ro"
],
"env": {
"MESH_BROKER_FILE": "/run/secrets/broker",
"MESH_RECEIVES": "/var/lib/lavinmq-module/grants/mesh.json",
"MESH_PROVISION_LAVINMQ": "http://lavinmq:15672",
"MESH_PROVISION_ADMIN_USER": "mesh-admin",
"MESH_PROVISION_PASSWORD_FILE": "/run/secrets/default"
}
}
]
}