From 7b06a7a408cc6f590582152c197d72915c51a374 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 27 Sep 2026 14:42:28 +0200 Subject: [PATCH] Event names are local now, in the manifests and in the code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every module named its events the way the old bus spelled a routing key — `module..`. Design 29 says a module names an event locally and the mesh works out where it lands, so all 37 were stale against a rule already decided. On the new bus that derives into a namespace belonging to a module called "module", so no cross-module subscription in the mesh matched anything: nothing failed, nothing reacted (novox/hq 04-ISSUES/127). 36 manifests converted, and 43 files of module code with them. The code mattered as much as the manifests: the runtime builds the subject from what `emit()` is handed, so a converted manifest with unconverted code would have had the permission and the subject disagree. Three things the new check found on the way: - `photos` emitted an event its manifest never declared, which the new bus refuses outright. Declared. - `showcase` waited for an event nothing emits, so its demo could never be triggered — only `showcase` may publish under its own name. It emits both halves now. - `distribution` declared an event named after a different module. It emits `image.pushed` under its own name. An event about a *role* belongs on the seat, where the name outlives whoever holds it, but the sdk has no way to publish on a seat yet, so that stays recorded rather than declared. The audit logger's "everything" pattern is `**` rather than the old bus's `#`. --- modules/anthropic-consumer/module.json | 2 +- modules/anthropic-consumer/usage/index.ts | 2 +- modules/anthropic-manager/module.json | 2 +- modules/anthropic-manager/refresh/index.ts | 2 +- modules/audit-logger/module.json | 2 +- modules/audit-logger/test/audit.test.ts | 6 +++--- modules/bazarr/index.ts | 2 +- modules/bazarr/module.json | 2 +- modules/bookshelf/index.ts | 4 ++-- modules/bookshelf/module.json | 4 ++-- modules/builder/module.json | 2 +- modules/cloudflare-dns/module.json | 4 ++-- modules/cloudflare-dns/provisioner/index.ts | 4 ++-- modules/distribution/index.ts | 2 +- modules/distribution/module.json | 2 +- modules/dnsmasq/index.ts | 4 ++-- modules/dnsmasq/module.json | 4 ++-- modules/gitea/index.ts | 2 +- modules/gitea/module.json | 6 +++--- modules/gitea/tools/index.ts | 4 ++-- modules/grafana/index.ts | 2 +- modules/grafana/module.json | 2 +- modules/home-assistant/index.ts | 2 +- modules/home-assistant/module.json | 2 +- modules/icecast/index.ts | 4 ++-- modules/icecast/module.json | 4 ++-- modules/keycloak/index.ts | 12 ++++++------ modules/keycloak/module.json | 12 ++++++------ modules/lavinmq/index.ts | 4 ++-- modules/lavinmq/module.json | 8 ++++---- modules/lavinmq/provisioner/index.ts | 4 ++-- modules/lidarr/index.ts | 4 ++-- modules/lidarr/module.json | 4 ++-- modules/mailu/index.ts | 4 ++-- modules/mailu/module.json | 8 ++++---- modules/mesh-catalog/index.ts | 10 +++++----- modules/mesh-catalog/module.json | 8 ++++---- modules/mesh-vault/index.ts | 6 +++--- modules/mesh-vault/module.json | 12 ++++++------ modules/mesh-vault/provisioner/index.ts | 2 +- modules/minio/module.json | 4 ++-- modules/minio/provisioner/index.ts | 4 ++-- modules/model-usage/index.ts | 2 +- modules/model-usage/module.json | 2 +- modules/mongodb/index.ts | 4 ++-- modules/mongodb/module.json | 8 ++++---- modules/mongodb/provisioner/index.ts | 4 ++-- modules/mosquitto/index.ts | 4 ++-- modules/mosquitto/module.json | 8 ++++---- modules/mosquitto/provisioner/index.ts | 4 ++-- modules/mssql/index.ts | 4 ++-- modules/mssql/module.json | 8 ++++---- modules/mssql/provisioner/index.ts | 4 ++-- modules/nextcloud/index.ts | 4 ++-- modules/nextcloud/module.json | 4 ++-- modules/nodered/module.json | 2 +- modules/nodered/tools/index.ts | 2 +- modules/nzbget/index.ts | 4 ++-- modules/nzbget/module.json | 4 ++-- modules/ombi/index.ts | 4 ++-- modules/ombi/module.json | 4 ++-- modules/photos/index.ts | 2 +- modules/photos/module.json | 3 +++ modules/plex/index.ts | 8 ++++---- modules/plex/module.json | 8 ++++---- modules/postgres/index.ts | 4 ++-- modules/postgres/module.json | 8 ++++---- modules/postgres/provisioner/index.ts | 4 ++-- modules/qbittorrent/index.ts | 4 ++-- modules/qbittorrent/module.json | 4 ++-- modules/radarr/index.ts | 4 ++-- modules/radarr/module.json | 4 ++-- modules/redis/index.ts | 4 ++-- modules/redis/module.json | 8 ++++---- modules/redis/provisioner/index.ts | 4 ++-- modules/showcase/index.ts | 4 ++-- modules/showcase/module.json | 5 +++-- modules/sonarr/index.ts | 4 ++-- modules/sonarr/module.json | 4 ++-- modules/tautulli/index.ts | 2 +- modules/tautulli/module.json | 2 +- modules/verdaccio/index.ts | 2 +- modules/verdaccio/module.json | 2 +- 83 files changed, 181 insertions(+), 177 deletions(-) diff --git a/modules/anthropic-consumer/module.json b/modules/anthropic-consumer/module.json index 981199d..55754a7 100644 --- a/modules/anthropic-consumer/module.json +++ b/modules/anthropic-consumer/module.json @@ -18,7 +18,7 @@ "broker": "/var/lib/mesh/anthropic-consumer/broker" }, "emits": [ - "module.anthropic-consumer.usage.session" + "usage.session" ], "resources": [ { diff --git a/modules/anthropic-consumer/usage/index.ts b/modules/anthropic-consumer/usage/index.ts index f874547..9dae62d 100644 --- a/modules/anthropic-consumer/usage/index.ts +++ b/modules/anthropic-consumer/usage/index.ts @@ -123,7 +123,7 @@ async function emitUsage(body: Record): Promise { await new Promise((resolve) => { const child = spawn( process.execPath, - [main, "emit", "module.anthropic-consumer.usage.session", JSON.stringify(body)], + [main, "emit", "usage.session", JSON.stringify(body)], { stdio: "inherit" }, ); child.on("exit", () => resolve()); diff --git a/modules/anthropic-manager/module.json b/modules/anthropic-manager/module.json index b0f9d55..a82b24d 100644 --- a/modules/anthropic-manager/module.json +++ b/modules/anthropic-manager/module.json @@ -18,7 +18,7 @@ "broker": "/var/lib/mesh/anthropic-manager/broker" }, "emits": [ - "module.anthropic-manager.usage.read" + "usage.read" ], "resources": [ { diff --git a/modules/anthropic-manager/refresh/index.ts b/modules/anthropic-manager/refresh/index.ts index 6db3a94..3749233 100644 --- a/modules/anthropic-manager/refresh/index.ts +++ b/modules/anthropic-manager/refresh/index.ts @@ -143,7 +143,7 @@ async function emitUsage(body: Record): Promise { const main = process.env.MESH_TOOLS_MAIN ?? "/app/dist/main.js"; const { spawn } = await import("node:child_process"); await new Promise((resolve) => { - const child = spawn(process.execPath, [main, "emit", "module.anthropic-manager.usage.read", JSON.stringify(body)], { + const child = spawn(process.execPath, [main, "emit", "usage.read", JSON.stringify(body)], { stdio: "inherit", }); child.on("exit", () => resolve()); diff --git a/modules/audit-logger/module.json b/modules/audit-logger/module.json index 671d9fb..0fbae45 100644 --- a/modules/audit-logger/module.json +++ b/modules/audit-logger/module.json @@ -3,7 +3,7 @@ "version": "1", "slug": "audit", "consumes": [ - "#" + "**" ], "own-secrets": { "broker": "/var/lib/audit-logger/broker" diff --git a/modules/audit-logger/test/audit.test.ts b/modules/audit-logger/test/audit.test.ts index 2762934..db09111 100644 --- a/modules/audit-logger/test/audit.test.ts +++ b/modules/audit-logger/test/audit.test.ts @@ -15,16 +15,16 @@ test("audit-logger records every event to the trail as one line each", async () const path = join(dir, "audit.log"); // The audit-logger's whole behaviour: consume everything, record it. - await on("#", async (event) => record(event, path)); + await on("**", async (event) => record(event, path)); process.env.MESH_MODULE = "umami"; process.env.MESH_NODE = "anchor"; - await emit("module.umami.site.created", { domain: "my-app" }); + await emit("site.created", { domain: "my-app" }); await emit("node.anchor.joined", { role: "worker" }); // a node event, not a module one const lines = (await readFile(path, "utf8")).trim().split("\n").map((l) => JSON.parse(l)); assert.equal(lines.length, 2); - assert.deepEqual(lines.map((l) => l.type), ["module.umami.site.created", "node.anchor.joined"]); + assert.deepEqual(lines.map((l) => l.type), ["umami.site.created", "node.anchor.joined"]); assert.equal(lines[0].source, "umami"); assert.equal(lines[0].node, "anchor"); assert.equal(lines[0].body.domain, "my-app"); diff --git a/modules/bazarr/index.ts b/modules/bazarr/index.ts index 42bbe4f..6c07d0c 100644 --- a/modules/bazarr/index.ts +++ b/modules/bazarr/index.ts @@ -24,7 +24,7 @@ async function pollHistory(): Promise { for (const entry of entries) { if (seen.has(entry.id)) continue; if (primed) { - await emit("module.bazarr.subtitle.downloaded", { + await emit("subtitle.downloaded", { kind: entry.kind, title: entry.title, language: entry.language, diff --git a/modules/bazarr/module.json b/modules/bazarr/module.json index fb95070..00bd0dc 100644 --- a/modules/bazarr/module.json +++ b/modules/bazarr/module.json @@ -5,7 +5,7 @@ "container-runtime" ], "emits": [ - "module.bazarr.subtitle.downloaded" + "subtitle.downloaded" ], "own-secrets": { "broker": "/var/lib/mesh/bazarr/broker", diff --git a/modules/bookshelf/index.ts b/modules/bookshelf/index.ts index 6b870d6..a7543fa 100644 --- a/modules/bookshelf/index.ts +++ b/modules/bookshelf/index.ts @@ -45,12 +45,12 @@ async function pollQueue(bookshelf: BookshelfClient): Promise { if (primed) { // Entered the queue since last look — Bookshelf grabbed a release. for (const [id, item] of now) { - if (!inQueue.has(id)) await emit("module.bookshelf.book.grabbed", { title: item.title, status: item.status }); + if (!inQueue.has(id)) await emit("book.grabbed", { title: item.title, status: item.status }); } // Left the queue — imported and done, unless it was last seen failing. for (const [id, item] of inQueue) { if (!now.has(id) && !FAILED_STATUSES.has(item.status)) { - await emit("module.bookshelf.download.completed", { title: item.title }); + await emit("download.completed", { title: item.title }); } } } diff --git a/modules/bookshelf/module.json b/modules/bookshelf/module.json index 9361d77..17a96b0 100644 --- a/modules/bookshelf/module.json +++ b/modules/bookshelf/module.json @@ -6,8 +6,8 @@ "container-runtime" ], "emits": [ - "module.bookshelf.book.grabbed", - "module.bookshelf.download.completed" + "book.grabbed", + "download.completed" ], "consumes": [], "own-secrets": { diff --git a/modules/builder/module.json b/modules/builder/module.json index 68b3e9e..de6ea36 100644 --- a/modules/builder/module.json +++ b/modules/builder/module.json @@ -21,7 +21,7 @@ "npm-package-registry": "/var/lib/mesh/builder/package-registry.secret" }, "emits": [ - "module.builder.built" + "built" ], "own-secrets": { "broker": "/var/lib/mesh/builder/broker" diff --git a/modules/cloudflare-dns/module.json b/modules/cloudflare-dns/module.json index 5a52670..9ce387f 100644 --- a/modules/cloudflare-dns/module.json +++ b/modules/cloudflare-dns/module.json @@ -22,8 +22,8 @@ "broker": "/var/lib/mesh/cloudflare-dns/broker" }, "emits": [ - "module.cloudflare-dns.record.created", - "module.cloudflare-dns.record.removed" + "record.created", + "record.removed" ], "resources": [ { diff --git a/modules/cloudflare-dns/provisioner/index.ts b/modules/cloudflare-dns/provisioner/index.ts index e192843..80bfcaf 100644 --- a/modules/cloudflare-dns/provisioner/index.ts +++ b/modules/cloudflare-dns/provisioner/index.ts @@ -20,7 +20,7 @@ runProvisioner("public-dns", { async create(p: Provision): Promise { const fqdn = cloudflare.nameFor(p.as); await cloudflare.upsert(fqdn); - await announce("module.cloudflare-dns.record.created", { + await announce("record.created", { name: fqdn, target: cloudflare.ingress, consumer: p.consumer ?? "", @@ -30,7 +30,7 @@ runProvisioner("public-dns", { async remove(p: { as: string }): Promise { const fqdn = cloudflare.nameFor(p.as); await cloudflare.remove(fqdn); - await announce("module.cloudflare-dns.record.removed", { name: fqdn, consumer: p.as }); + await announce("record.removed", { name: fqdn, consumer: p.as }); }, }); diff --git a/modules/distribution/index.ts b/modules/distribution/index.ts index 86748b7..949f1f9 100644 --- a/modules/distribution/index.ts +++ b/modules/distribution/index.ts @@ -33,7 +33,7 @@ async function pollCatalog(): Promise { for (const tag of tags) { const id = `${repo}:${tag}`; if (!seen.has(id)) { - if (primed) await emit("module.registry.image.pushed", { repo, tag }); + if (primed) await emit("image.pushed", { repo, tag }); seen.add(id); } } diff --git a/modules/distribution/module.json b/modules/distribution/module.json index dfa7c9c..ea28f11 100644 --- a/modules/distribution/module.json +++ b/modules/distribution/module.json @@ -17,7 +17,7 @@ "container-runtime" ], "emits": [ - "module.registry.image.pushed" + "image.pushed" ], "own-secrets": { "broker": "/var/lib/mesh/registry/broker" diff --git a/modules/dnsmasq/index.ts b/modules/dnsmasq/index.ts index 2c428c8..d8327bd 100644 --- a/modules/dnsmasq/index.ts +++ b/modules/dnsmasq/index.ts @@ -20,10 +20,10 @@ async function poll(): Promise { const now = new Map((await dnsmasq.answeredNames()).map((a) => [a.name, a.address])); if (primed) { for (const [name, address] of now) { - if (!known.has(name)) await emit("module.dnsmasq.name.added", { name, address }); + if (!known.has(name)) await emit("name.added", { name, address }); } for (const [name] of known) { - if (!now.has(name)) await emit("module.dnsmasq.name.removed", { name }); + if (!now.has(name)) await emit("name.removed", { name }); } } known.clear(); diff --git a/modules/dnsmasq/module.json b/modules/dnsmasq/module.json index 3e3ae78..8f8af5e 100644 --- a/modules/dnsmasq/module.json +++ b/modules/dnsmasq/module.json @@ -8,8 +8,8 @@ "mesh-addressing" ], "emits": [ - "module.dnsmasq.name.added", - "module.dnsmasq.name.removed" + "name.added", + "name.removed" ], "own-secrets": { "broker": "/var/lib/mesh/dnsmasq/broker" diff --git a/modules/gitea/index.ts b/modules/gitea/index.ts index f7847bb..eb1d565 100644 --- a/modules/gitea/index.ts +++ b/modules/gitea/index.ts @@ -35,7 +35,7 @@ async function pollRepos(client: GiteaClient): Promise { for (const repo of repos) { if (!seen.has(repo.full_name)) { if (primed) { - await emit("module.gitea.repo.created", { + await emit("repo.created", { full_name: repo.full_name, owner: repo.owner, name: repo.name, diff --git a/modules/gitea/module.json b/modules/gitea/module.json index a5b9843..c44cabe 100644 --- a/modules/gitea/module.json +++ b/modules/gitea/module.json @@ -38,9 +38,9 @@ "container-runtime" ], "emits": [ - "module.gitea.repo.created", - "module.gitea.issue.opened", - "module.gitea.pull.merged" + "repo.created", + "issue.opened", + "pull.merged" ], "listens": [ { diff --git a/modules/gitea/tools/index.ts b/modules/gitea/tools/index.ts index 7e77484..e910f8b 100644 --- a/modules/gitea/tools/index.ts +++ b/modules/gitea/tools/index.ts @@ -124,7 +124,7 @@ export function getGiteaTools(gitea: GiteaClient): ToolDefinition[] { labels: labelIds, }); // The mesh just opened an issue — announce it the moment it exists. - await emit("module.gitea.issue.opened", { + await emit("issue.opened", { owner, repo, number: issue.number, @@ -231,7 +231,7 @@ export function getGiteaTools(gitea: GiteaClient): ToolDefinition[] { // Read the PR first, so the merged event carries a title and branches, not just a number. const pull = await gitea.getPullRequest(owner, repo, number); await gitea.mergePullRequest(owner, repo, number, method, deleteBranch); - await emit("module.gitea.pull.merged", { + await emit("pull.merged", { owner, repo, number, diff --git a/modules/grafana/index.ts b/modules/grafana/index.ts index 03e8814..6e174b2 100644 --- a/modules/grafana/index.ts +++ b/modules/grafana/index.ts @@ -40,7 +40,7 @@ async function pollAlerts(client: GrafanaClient): Promise { for (const key of now) { if (!firing.has(key)) { const a = byKey.get(key)!; - await emit("module.grafana.alert.firing", { name: a.name, labels: a.labels, activeAt: a.activeAt }); + await emit("alert.firing", { name: a.name, labels: a.labels, activeAt: a.activeAt }); } } } diff --git a/modules/grafana/module.json b/modules/grafana/module.json index 8ae941f..fe877ff 100644 --- a/modules/grafana/module.json +++ b/modules/grafana/module.json @@ -2,7 +2,7 @@ "module": "grafana", "version": "1", "emits": [ - "module.grafana.alert.firing" + "alert.firing" ], "own-secrets": { "admin": "/var/lib/grafana-module/admin.secret", diff --git a/modules/home-assistant/index.ts b/modules/home-assistant/index.ts index aba7949..d59a5c2 100644 --- a/modules/home-assistant/index.ts +++ b/modules/home-assistant/index.ts @@ -44,7 +44,7 @@ async function pollStates(): Promise { for (const s of states) { const prev = lastState.get(s.entity_id); if (primed && prev !== undefined && prev !== s.state) { - await emit("module.home-assistant.state.changed", { + await emit("state.changed", { entity: s.entity_id, name: nameOf(s), from: prev, diff --git a/modules/home-assistant/module.json b/modules/home-assistant/module.json index 4b8bcb9..e860c1f 100644 --- a/modules/home-assistant/module.json +++ b/modules/home-assistant/module.json @@ -6,7 +6,7 @@ "container-runtime" ], "emits": [ - "module.home-assistant.state.changed" + "state.changed" ], "own-secrets": { "broker": "/var/lib/mesh/home-assistant/broker", diff --git a/modules/icecast/index.ts b/modules/icecast/index.ts index 62523c3..2e121f0 100644 --- a/modules/icecast/index.ts +++ b/modules/icecast/index.ts @@ -23,7 +23,7 @@ async function pollMounts(): Promise { if (primed) { for (const [mount, m] of now) { if (!live.has(mount)) { - await emit("module.icecast.stream.started", { + await emit("stream.started", { mount, name: m.name, description: m.description, @@ -33,7 +33,7 @@ async function pollMounts(): Promise { } for (const [mount, m] of live) { if (!now.has(mount)) { - await emit("module.icecast.stream.stopped", { mount, name: m.name }); + await emit("stream.stopped", { mount, name: m.name }); } } } diff --git a/modules/icecast/module.json b/modules/icecast/module.json index cdbef64..51c6296 100644 --- a/modules/icecast/module.json +++ b/modules/icecast/module.json @@ -5,8 +5,8 @@ "container-runtime" ], "emits": [ - "module.icecast.stream.started", - "module.icecast.stream.stopped" + "stream.started", + "stream.stopped" ], "own-secrets": { "broker": "/var/lib/mesh/icecast/broker" diff --git a/modules/keycloak/index.ts b/modules/keycloak/index.ts index 451464c..dc25c8a 100644 --- a/modules/keycloak/index.ts +++ b/modules/keycloak/index.ts @@ -28,17 +28,17 @@ async function announce(type: string, body: Record): Promise - announce("module.keycloak.user.created", { realm, username, ...(email ? { email } : {}) }), + announce("user.created", { realm, username, ...(email ? { email } : {}) }), userDeleted: (realm: string, userId: string) => - announce("module.keycloak.user.deleted", { realm, userId }), + announce("user.deleted", { realm, userId }), passwordReset: (realm: string, userId: string) => - announce("module.keycloak.password.reset", { realm, userId }), + announce("password.reset", { realm, userId }), clientCreated: (realm: string, clientId: string, name?: string) => - announce("module.keycloak.client.created", { realm, clientId, ...(name ? { name } : {}) }), + announce("client.created", { realm, clientId, ...(name ? { name } : {}) }), groupCreated: (realm: string, name: string) => - announce("module.keycloak.group.created", { realm, name }), + announce("group.created", { realm, name }), roleCreated: (realm: string, name: string) => - announce("module.keycloak.role.created", { realm, name }), + announce("role.created", { realm, name }), }; console.log("[keycloak] event surface ready — identity, client, group and role changes are announced"); diff --git a/modules/keycloak/module.json b/modules/keycloak/module.json index 44e0d44..ae6f921 100644 --- a/modules/keycloak/module.json +++ b/modules/keycloak/module.json @@ -25,12 +25,12 @@ "container-runtime" ], "emits": [ - "module.keycloak.user.created", - "module.keycloak.user.deleted", - "module.keycloak.password.reset", - "module.keycloak.client.created", - "module.keycloak.group.created", - "module.keycloak.role.created" + "user.created", + "user.deleted", + "password.reset", + "client.created", + "group.created", + "role.created" ], "listens": [ { diff --git a/modules/lavinmq/index.ts b/modules/lavinmq/index.ts index d440014..76f5128 100644 --- a/modules/lavinmq/index.ts +++ b/modules/lavinmq/index.ts @@ -14,11 +14,11 @@ interface AmqpEvent { vhost?: string; } -await on("module.lavinmq.amqp.provisioned", async (e) => { +await on("amqp.provisioned", async (e) => { console.log(`[lavinmq] broker provisioned for ${e.body.consumer ?? "?"} (user ${e.body.user}, vhost ${e.body.vhost})`); }); -await on("module.lavinmq.amqp.deprovisioned", async (e) => { +await on("amqp.deprovisioned", async (e) => { console.log(`[lavinmq] broker deprovisioned (user ${e.body.user})`); }); diff --git a/modules/lavinmq/module.json b/modules/lavinmq/module.json index 2f40b65..31f76e4 100644 --- a/modules/lavinmq/module.json +++ b/modules/lavinmq/module.json @@ -11,12 +11,12 @@ "container-runtime" ], "emits": [ - "module.lavinmq.amqp.provisioned", - "module.lavinmq.amqp.deprovisioned" + "amqp.provisioned", + "amqp.deprovisioned" ], "consumes": [ - "module.lavinmq.amqp.provisioned", - "module.lavinmq.amqp.deprovisioned" + "lavinmq.amqp.provisioned", + "lavinmq.amqp.deprovisioned" ], "serves": { "amqp": { diff --git a/modules/lavinmq/provisioner/index.ts b/modules/lavinmq/provisioner/index.ts index 7cea27f..acd1d97 100644 --- a/modules/lavinmq/provisioner/index.ts +++ b/modules/lavinmq/provisioner/index.ts @@ -37,7 +37,7 @@ runProvisioner("amqp", { // The vhost and the user share the consumer's login, so one cannot reach another's broker. await lavinmq.waitReady(); await lavinmq.createConsumer(p.as, p.password); - await announce("module.lavinmq.amqp.provisioned", { + await announce("amqp.provisioned", { consumer: p.consumer ?? "", user: p.as, vhost: p.as, @@ -46,7 +46,7 @@ runProvisioner("amqp", { async remove(p: { as: string }): Promise { await lavinmq.removeConsumer(p.as); - await announce("module.lavinmq.amqp.deprovisioned", { user: p.as, vhost: p.as }); + await announce("amqp.deprovisioned", { user: p.as, vhost: p.as }); }, // Asked every minute by the harness: whether the backend still holds this consumer exactly as // the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120). diff --git a/modules/lidarr/index.ts b/modules/lidarr/index.ts index f286e2c..a19c88b 100644 --- a/modules/lidarr/index.ts +++ b/modules/lidarr/index.ts @@ -40,12 +40,12 @@ async function pollQueue(lidarr: LidarrClient): Promise { if (primed) { // Entered the queue since last look — Lidarr grabbed a release. for (const [id, item] of now) { - if (!inQueue.has(id)) await emit("module.lidarr.album.grabbed", { title: item.title, status: item.status }); + if (!inQueue.has(id)) await emit("album.grabbed", { title: item.title, status: item.status }); } // Left the queue — imported and done, unless it was last seen failing. for (const [id, item] of inQueue) { if (!now.has(id) && !FAILED_STATUSES.has(item.status)) { - await emit("module.lidarr.download.completed", { title: item.title }); + await emit("download.completed", { title: item.title }); } } } diff --git a/modules/lidarr/module.json b/modules/lidarr/module.json index ebb2b0b..6d424b6 100644 --- a/modules/lidarr/module.json +++ b/modules/lidarr/module.json @@ -5,8 +5,8 @@ "container-runtime" ], "emits": [ - "module.lidarr.album.grabbed", - "module.lidarr.download.completed" + "album.grabbed", + "download.completed" ], "consumes": [], "own-secrets": { diff --git a/modules/mailu/index.ts b/modules/mailu/index.ts index 6c891b0..b756e5f 100644 --- a/modules/mailu/index.ts +++ b/modules/mailu/index.ts @@ -36,8 +36,8 @@ function watcher(created: string, deleted: string): (keys: string[]) => Promise< }; } -const watchUsers = watcher("module.mailu.user.created", "module.mailu.user.deleted"); -const watchAliases = watcher("module.mailu.alias.created", "module.mailu.alias.deleted"); +const watchUsers = watcher("user.created", "user.deleted"); +const watchAliases = watcher("alias.created", "alias.deleted"); async function pollUsers(): Promise { await watchUsers((await mailu.listUsers()).map((u) => u.email)); diff --git a/modules/mailu/module.json b/modules/mailu/module.json index 7ee64b3..d4808fb 100644 --- a/modules/mailu/module.json +++ b/modules/mailu/module.json @@ -53,10 +53,10 @@ } }, "emits": [ - "module.mailu.user.created", - "module.mailu.user.deleted", - "module.mailu.alias.created", - "module.mailu.alias.deleted" + "user.created", + "user.deleted", + "alias.created", + "alias.deleted" ], "listens": [ { diff --git a/modules/mesh-catalog/index.ts b/modules/mesh-catalog/index.ts index 4181fcb..bb68529 100644 --- a/modules/mesh-catalog/index.ts +++ b/modules/mesh-catalog/index.ts @@ -47,7 +47,7 @@ interface Built { replay?: boolean; } -await on("module.builder.built", async (event) => { +await on("builder.built", async (event) => { const body = event.body as Built; if (!body.module || !body.commit) { // Said rather than dropped: a build that announced itself without saying what it built is a @@ -69,7 +69,7 @@ await on("module.builder.built", async (event) => { // it was missing, and the mesh is told nothing happened, because nothing did. if (body.replay) return; - await emit("module.mesh-catalog.registered", { + await emit("registered", { module: body.module, commit: body.commit, upgraded, }); @@ -77,13 +77,13 @@ await on("module.builder.built", async (event) => { // through modules that did not change, forever (ADR 0072). if (!upgraded) return; - await emit("module.mesh-catalog.upgraded", { + await emit("upgraded", { module: body.module, commit: body.commit, previous, }); // What can be built now — stale, and waiting on nothing that is itself stale. for (const next of await graph.buildable()) { - await emit("module.mesh-catalog.rebuild-needed", { + await emit("rebuild-needed", { module: next.module, builtAt: next.commit, because: next.because, @@ -101,4 +101,4 @@ await on("module.builder.built", async (event) => { // Asked on every start, not only the first. A catalogue cannot tell whether it has a gap, and the // answer is idempotent: registering a build already held changes nothing and announces nothing. // Asked AFTER subscribing, so a build arriving during the replay is not lost between the two. -await emit("module.mesh-catalog.catching-up", {}); +await emit("catching-up", {}); diff --git a/modules/mesh-catalog/module.json b/modules/mesh-catalog/module.json index 4c52d90..e940df4 100644 --- a/modules/mesh-catalog/module.json +++ b/modules/mesh-catalog/module.json @@ -29,12 +29,12 @@ "broker": "/var/lib/mesh/mesh-catalog/broker" }, "consumes": [ - "module.builder.built" + "builder.built" ], "emits": [ - "module.mesh-catalog.registered", - "module.mesh-catalog.upgraded", - "module.mesh-catalog.rebuild-needed" + "registered", + "upgraded", + "rebuild-needed" ], "resources": [ { diff --git a/modules/mesh-vault/index.ts b/modules/mesh-vault/index.ts index 6ef05e3..540a780 100644 --- a/modules/mesh-vault/index.ts +++ b/modules/mesh-vault/index.ts @@ -16,15 +16,15 @@ interface SecretEvent { rotations?: number; } -await on("module.mesh-vault.secret.provisioned", async (e) => { +await on("secret.provisioned", async (e) => { console.log(`[mesh-vault] secret provisioned for ${e.body.as} on ${e.body.consumer} (${e.body.fingerprint})`); }); -await on("module.mesh-vault.secret.rotated", async (e) => { +await on("secret.rotated", async (e) => { console.log(`[mesh-vault] secret rotated for ${e.body.as} — rotation ${e.body.rotations} (${e.body.fingerprint})`); }); -await on("module.mesh-vault.secret.deprovisioned", async (e) => { +await on("secret.deprovisioned", async (e) => { console.log(`[mesh-vault] secret withdrawn from ${e.body.as}`); }); diff --git a/modules/mesh-vault/module.json b/modules/mesh-vault/module.json index a875fbe..70577e5 100644 --- a/modules/mesh-vault/module.json +++ b/modules/mesh-vault/module.json @@ -11,14 +11,14 @@ "container-runtime" ], "emits": [ - "module.mesh-vault.secret.provisioned", - "module.mesh-vault.secret.rotated", - "module.mesh-vault.secret.deprovisioned" + "secret.provisioned", + "secret.rotated", + "secret.deprovisioned" ], "consumes": [ - "module.mesh-vault.secret.provisioned", - "module.mesh-vault.secret.rotated", - "module.mesh-vault.secret.deprovisioned" + "mesh-vault.secret.provisioned", + "mesh-vault.secret.rotated", + "mesh-vault.secret.deprovisioned" ], "receives": { "secret": "/var/lib/mesh-vault/grants/mesh.json" diff --git a/modules/mesh-vault/provisioner/index.ts b/modules/mesh-vault/provisioner/index.ts index 010d78e..ad0872c 100644 --- a/modules/mesh-vault/provisioner/index.ts +++ b/modules/mesh-vault/provisioner/index.ts @@ -43,6 +43,6 @@ runProvisioner("secret", { async remove(p: { as: string }): Promise { if (!ledger.withdraw(p.as)) return; console.log(`[mesh-vault] withdrawn: ${p.as}`); - await announce("module.mesh-vault.secret.deprovisioned", { as: p.as }); + await announce("secret.deprovisioned", { as: p.as }); }, }); diff --git a/modules/minio/module.json b/modules/minio/module.json index 6e005b9..56c933d 100644 --- a/modules/minio/module.json +++ b/modules/minio/module.json @@ -26,8 +26,8 @@ "container-runtime" ], "emits": [ - "module.minio.bucket.created", - "module.minio.bucket.removed" + "bucket.created", + "bucket.removed" ], "listens": [ { diff --git a/modules/minio/provisioner/index.ts b/modules/minio/provisioner/index.ts index 5e15c01..4c34201 100644 --- a/modules/minio/provisioner/index.ts +++ b/modules/minio/provisioner/index.ts @@ -30,7 +30,7 @@ runProvisioner("s3-bucket", { try { await minio.removeAccessKey(accessKeyId); } catch { /* none yet — first provision */ } await minio.createAccessKey(bucket, accessKeyId, p.password); - await announce("module.minio.bucket.created", { + await announce("bucket.created", { bucket, consumer: p.consumer ?? "", accessKey: accessKeyId, @@ -51,7 +51,7 @@ runProvisioner("s3-bucket", { console.error(`[minio] bucket ${bucket} not removed (likely non-empty), access revoked: ${err}`); } - await announce("module.minio.bucket.removed", { bucket, accessKey: p.as }); + await announce("bucket.removed", { bucket, accessKey: p.as }); }, // Asked every minute by the harness: whether the backend still holds this consumer exactly as diff --git a/modules/model-usage/index.ts b/modules/model-usage/index.ts index 2bcf812..bdde20e 100644 --- a/modules/model-usage/index.ts +++ b/modules/model-usage/index.ts @@ -22,7 +22,7 @@ const store = UsageStore.fromEnv(); // to reach the provider over the overlay would block the very apply that brings the overlay up. await store.migrate(); -await on("module.*.usage.*", async (event) => { +await on("*.usage.*", async (event) => { const body = event.body as { rows?: UsageRow[]; raw?: unknown }; for (const row of body.rows ?? []) { try { diff --git a/modules/model-usage/module.json b/modules/model-usage/module.json index 96fc842..60ec754 100644 --- a/modules/model-usage/module.json +++ b/modules/model-usage/module.json @@ -20,7 +20,7 @@ "postgres-database": "/var/lib/model-usage/database.secret" }, "consumes": [ - "module.*.usage.*" + "*.usage.*" ], "own-secrets": { "broker": "/var/lib/mesh/model-usage/broker" diff --git a/modules/mongodb/index.ts b/modules/mongodb/index.ts index c3bd40b..ebca8d5 100644 --- a/modules/mongodb/index.ts +++ b/modules/mongodb/index.ts @@ -14,11 +14,11 @@ interface DatabaseEvent { user?: string; } -await on("module.mongodb.database.provisioned", async (e) => { +await on("database.provisioned", async (e) => { console.log(`[mongodb] database provisioned for ${e.body.consumer} (db ${e.body.database})`); }); -await on("module.mongodb.database.deprovisioned", async (e) => { +await on("database.deprovisioned", async (e) => { console.log(`[mongodb] database deprovisioned for ${e.body.consumer} (db ${e.body.database})`); }); diff --git a/modules/mongodb/module.json b/modules/mongodb/module.json index f831eb4..f93fc34 100644 --- a/modules/mongodb/module.json +++ b/modules/mongodb/module.json @@ -11,12 +11,12 @@ "container-runtime" ], "emits": [ - "module.mongodb.database.provisioned", - "module.mongodb.database.deprovisioned" + "database.provisioned", + "database.deprovisioned" ], "consumes": [ - "module.mongodb.database.provisioned", - "module.mongodb.database.deprovisioned" + "mongodb.database.provisioned", + "mongodb.database.deprovisioned" ], "listens": [ { diff --git a/modules/mongodb/provisioner/index.ts b/modules/mongodb/provisioner/index.ts index e532b62..aaa279d 100644 --- a/modules/mongodb/provisioner/index.ts +++ b/modules/mongodb/provisioner/index.ts @@ -34,7 +34,7 @@ runProvisioner("mongodb-database", { // Database and owning user share the consumer's login, so the consumer owns exactly its own. const database = p.as; await mongo.createDatabaseAndUser(database, p.as, p.password); - await announce("module.mongodb.database.provisioned", { + await announce("database.provisioned", { consumer: p.consumer ?? "", database, user: p.as, @@ -43,7 +43,7 @@ runProvisioner("mongodb-database", { async remove(p: { as: string }): Promise { await mongo.dropDatabaseAndUser(p.as, p.as); - await announce("module.mongodb.database.deprovisioned", { database: p.as }); + await announce("database.deprovisioned", { database: p.as }); }, // Asked every minute by the harness: whether the backend still holds this consumer exactly as // the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120). diff --git a/modules/mosquitto/index.ts b/modules/mosquitto/index.ts index d91356c..af26f4b 100644 --- a/modules/mosquitto/index.ts +++ b/modules/mosquitto/index.ts @@ -14,11 +14,11 @@ interface TopicEvent { topicPrefix?: string; } -await on("module.mosquitto.topic.provisioned", async (e) => { +await on("topic.provisioned", async (e) => { console.log(`[mosquitto] topic provisioned for ${e.body.consumer} (client ${e.body.username})`); }); -await on("module.mosquitto.topic.deprovisioned", async (e) => { +await on("topic.deprovisioned", async (e) => { console.log(`[mosquitto] topic deprovisioned for ${e.body.consumer} (client ${e.body.username})`); }); diff --git a/modules/mosquitto/module.json b/modules/mosquitto/module.json index 774231f..267c24f 100644 --- a/modules/mosquitto/module.json +++ b/modules/mosquitto/module.json @@ -12,12 +12,12 @@ "container-runtime" ], "emits": [ - "module.mosquitto.topic.provisioned", - "module.mosquitto.topic.deprovisioned" + "topic.provisioned", + "topic.deprovisioned" ], "consumes": [ - "module.mosquitto.topic.provisioned", - "module.mosquitto.topic.deprovisioned" + "mosquitto.topic.provisioned", + "mosquitto.topic.deprovisioned" ], "serves": { "mqtt-topic": {} diff --git a/modules/mosquitto/provisioner/index.ts b/modules/mosquitto/provisioner/index.ts index 60f9ed6..8a5fa08 100644 --- a/modules/mosquitto/provisioner/index.ts +++ b/modules/mosquitto/provisioner/index.ts @@ -32,7 +32,7 @@ runProvisioner("mqtt-topic", { // The topic subtree is scoped to the consumer's own login, so one cannot read another's topics. const topicPrefix = p.as; await mosquitto.createScopedClient(p.as, p.password, topicPrefix); - await announce("module.mosquitto.topic.provisioned", { + await announce("topic.provisioned", { consumer: p.consumer ?? "", username: p.as, topicPrefix, @@ -41,7 +41,7 @@ runProvisioner("mqtt-topic", { async remove(p: { as: string }): Promise { await mosquitto.deleteScopedClient(p.as); - await announce("module.mosquitto.topic.deprovisioned", { username: p.as }); + await announce("topic.deprovisioned", { username: p.as }); }, // Asked every minute by the harness: whether the backend still holds this consumer exactly as // the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120). diff --git a/modules/mssql/index.ts b/modules/mssql/index.ts index 873b569..0232587 100644 --- a/modules/mssql/index.ts +++ b/modules/mssql/index.ts @@ -14,11 +14,11 @@ interface DatabaseEvent { user?: string; } -await on("module.mssql.database.provisioned", async (e) => { +await on("database.provisioned", async (e) => { console.log(`[mssql] database provisioned for ${e.body.consumer} (db ${e.body.database})`); }); -await on("module.mssql.database.deprovisioned", async (e) => { +await on("database.deprovisioned", async (e) => { console.log(`[mssql] database deprovisioned for ${e.body.consumer} (db ${e.body.database})`); }); diff --git a/modules/mssql/module.json b/modules/mssql/module.json index 5fa2b01..2de6678 100644 --- a/modules/mssql/module.json +++ b/modules/mssql/module.json @@ -11,12 +11,12 @@ "container-runtime" ], "emits": [ - "module.mssql.database.provisioned", - "module.mssql.database.deprovisioned" + "database.provisioned", + "database.deprovisioned" ], "consumes": [ - "module.mssql.database.provisioned", - "module.mssql.database.deprovisioned" + "mssql.database.provisioned", + "mssql.database.deprovisioned" ], "listens": [ { diff --git a/modules/mssql/provisioner/index.ts b/modules/mssql/provisioner/index.ts index 16d0907..9ef31aa 100644 --- a/modules/mssql/provisioner/index.ts +++ b/modules/mssql/provisioner/index.ts @@ -33,7 +33,7 @@ runProvisioner("mssql-database", { // Database, login and user share the consumer's name, so the consumer owns exactly its own. const database = p.as; await mssql.createDatabaseAndLogin(database, p.as, p.password); - await announce("module.mssql.database.provisioned", { + await announce("database.provisioned", { consumer: p.consumer ?? "", database, user: p.as, @@ -42,7 +42,7 @@ runProvisioner("mssql-database", { async remove(p: { as: string }): Promise { await mssql.dropDatabaseAndLogin(p.as, p.as); - await announce("module.mssql.database.deprovisioned", { database: p.as }); + await announce("database.deprovisioned", { database: p.as }); }, // Asked every minute by the harness: whether the backend still holds this consumer exactly as // the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120). diff --git a/modules/nextcloud/index.ts b/modules/nextcloud/index.ts index aeaf61d..9fcf6d5 100644 --- a/modules/nextcloud/index.ts +++ b/modules/nextcloud/index.ts @@ -26,7 +26,7 @@ async function pollUsers(client: NextcloudClient): Promise { const users = client.listUsers(); for (const u of users) { if (knownUsers.has(u.uid)) continue; - if (usersPrimed) await emit("module.nextcloud.user.created", { uid: u.uid, displayName: u.displayName }); + if (usersPrimed) await emit("user.created", { uid: u.uid, displayName: u.displayName }); knownUsers.add(u.uid); } usersPrimed = true; @@ -38,7 +38,7 @@ async function pollShares(client: NextcloudClient): Promise { const shares = await client.listShares(); for (const s of shares) { if (knownShares.has(s.id)) continue; - if (sharesPrimed) await emit("module.nextcloud.share.created", { id: s.id, path: s.path, shareType: s.shareType, shareWith: s.shareWith, owner: s.owner }); + if (sharesPrimed) await emit("share.created", { id: s.id, path: s.path, shareType: s.shareType, shareWith: s.shareWith, owner: s.owner }); knownShares.add(s.id); } sharesPrimed = true; diff --git a/modules/nextcloud/module.json b/modules/nextcloud/module.json index 7df1e84..fc82b7b 100644 --- a/modules/nextcloud/module.json +++ b/modules/nextcloud/module.json @@ -26,8 +26,8 @@ "s3-bucket": "${dir:state}/store.secret" }, "emits": [ - "module.nextcloud.user.created", - "module.nextcloud.share.created" + "user.created", + "share.created" ], "own-secrets": { "admin": "${dir:state}/admin.secret", diff --git a/modules/nodered/module.json b/modules/nodered/module.json index 73217b4..cd8e801 100644 --- a/modules/nodered/module.json +++ b/modules/nodered/module.json @@ -2,7 +2,7 @@ "module": "nodered", "version": "1", "emits": [ - "module.nodered.flows.deployed" + "flows.deployed" ], "own-secrets": { "broker": "/var/lib/mesh/nodered/broker" diff --git a/modules/nodered/tools/index.ts b/modules/nodered/tools/index.ts index e19b6dc..4a7b45d 100644 --- a/modules/nodered/tools/index.ts +++ b/modules/nodered/tools/index.ts @@ -43,7 +43,7 @@ export function getNodeRedTools(nodered: NodeRedClient): ToolDefinition[] { const result = await nodered.deployFlows(flows, type); // Best-effort announcement — a deploy must not fail because the broker is unbound here. try { - await emit("module.nodered.flows.deployed", { rev: result.rev, nodeCount: result.nodeCount, type }); + await emit("flows.deployed", { rev: result.rev, nodeCount: result.nodeCount, type }); } catch (err) { console.error(`[nodered] deployed but could not emit: ${err}`); } diff --git a/modules/nzbget/index.ts b/modules/nzbget/index.ts index 16bfee3..fa14e0b 100644 --- a/modules/nzbget/index.ts +++ b/modules/nzbget/index.ts @@ -27,7 +27,7 @@ async function pollQueue(): Promise { if (queuePrimed) { for (const item of items) { if (!inQueue.has(item.id)) { - await emit("module.nzbget.download.added", { name: item.name, category: item.category, sizeMB: item.sizeMB }); + await emit("download.added", { name: item.name, category: item.category, sizeMB: item.sizeMB }); } } } @@ -45,7 +45,7 @@ async function pollHistory(): Promise { // A newly-appeared history entry is a completion only if it actually succeeded; a failure or // a manual delete lands in history too, and neither is a "download.completed". if (historyPrimed && item.success) { - await emit("module.nzbget.download.completed", { name: item.name, category: item.category, sizeMB: item.sizeMB }); + await emit("download.completed", { name: item.name, category: item.category, sizeMB: item.sizeMB }); } seenHistory.add(item.id); } diff --git a/modules/nzbget/module.json b/modules/nzbget/module.json index 94f0f87..87f2cf6 100644 --- a/modules/nzbget/module.json +++ b/modules/nzbget/module.json @@ -5,8 +5,8 @@ "container-runtime" ], "emits": [ - "module.nzbget.download.added", - "module.nzbget.download.completed" + "download.added", + "download.completed" ], "consumes": [], "own-secrets": { diff --git a/modules/ombi/index.ts b/modules/ombi/index.ts index 0c38b0f..479c08c 100644 --- a/modules/ombi/index.ts +++ b/modules/ombi/index.ts @@ -31,7 +31,7 @@ async function pollRequests(): Promise { const key = keyOf(r); const known = approvedState.has(key); if (primed && !known) { - await emit("module.ombi.request.created", { + await emit("request.created", { kind: r.kind, id: r.id, title: r.title, @@ -41,7 +41,7 @@ async function pollRequests(): Promise { } // Approval: the flag went from false to true for a request we already knew about. if (primed && known && r.approved && approvedState.get(key) === false) { - await emit("module.ombi.request.approved", { kind: r.kind, id: r.id, title: r.title, tmdbId: r.tmdbId }); + await emit("request.approved", { kind: r.kind, id: r.id, title: r.title, tmdbId: r.tmdbId }); } approvedState.set(key, r.approved); } diff --git a/modules/ombi/module.json b/modules/ombi/module.json index 27536d7..4f73efa 100644 --- a/modules/ombi/module.json +++ b/modules/ombi/module.json @@ -5,8 +5,8 @@ "container-runtime" ], "emits": [ - "module.ombi.request.created", - "module.ombi.request.approved" + "request.created", + "request.approved" ], "own-secrets": { "broker": "/var/lib/mesh/ombi/broker", diff --git a/modules/photos/index.ts b/modules/photos/index.ts index b2f457a..747b171 100644 --- a/modules/photos/index.ts +++ b/modules/photos/index.ts @@ -20,7 +20,7 @@ async function pollRecent(): Promise { for (const asset of items) { if (!seen.has(asset.id)) { if (primed) { - await emit("module.photos.item.added", { + await emit("item.added", { id: asset.id, fileName: asset.fileName, kind: asset.type, diff --git a/modules/photos/module.json b/modules/photos/module.json index b804f99..235ddbc 100644 --- a/modules/photos/module.json +++ b/modules/photos/module.json @@ -4,6 +4,9 @@ "capabilities": [ "container-runtime" ], + "emits": [ + "item.added" + ], "requires": [ "s3-bucket", "mongodb-database", diff --git a/modules/plex/index.ts b/modules/plex/index.ts index d4d48de..66aebdb 100644 --- a/modules/plex/index.ts +++ b/modules/plex/index.ts @@ -24,10 +24,10 @@ async function pollSessions(): Promise { const now = new Map(sessions.map((s) => [s.key, s])); if (playbackPrimed) { for (const [key, s] of now) { - if (!active.has(key)) await emit("module.plex.playback.started", { title: s.title, user: s.user, player: s.player, kind: s.type }); + if (!active.has(key)) await emit("playback.started", { title: s.title, user: s.user, player: s.player, kind: s.type }); } for (const [key, s] of active) { - if (!now.has(key)) await emit("module.plex.playback.stopped", { title: s.title, user: s.user, player: s.player }); + if (!now.has(key)) await emit("playback.stopped", { title: s.title, user: s.user, player: s.player }); } } active.clear(); @@ -44,7 +44,7 @@ async function pollRecent(): Promise { for (const item of items) { const id = `${item.title}@${item.addedAt ?? ""}`; if (!seen.has(id)) { - if (itemsPrimed) await emit("module.plex.item.added", item); + if (itemsPrimed) await emit("item.added", item); seen.add(id); } } @@ -53,7 +53,7 @@ async function pollRecent(): Promise { // A downloader finished somewhere on the mesh: rescan, so what it fetched becomes a visible item // rather than a file Plex has not noticed. Idempotent — a rescan too many costs a little disk I/O. -await on("module.*.download.completed", async () => { +await on("*.download.completed", async () => { await plex.refreshAll(); }); diff --git a/modules/plex/module.json b/modules/plex/module.json index b5c56f0..ae2c1bd 100644 --- a/modules/plex/module.json +++ b/modules/plex/module.json @@ -5,12 +5,12 @@ "container-runtime" ], "emits": [ - "module.plex.playback.started", - "module.plex.playback.stopped", - "module.plex.item.added" + "playback.started", + "playback.stopped", + "item.added" ], "consumes": [ - "module.*.download.completed" + "*.download.completed" ], "own-secrets": { "broker": "/var/lib/mesh/plex/broker", diff --git a/modules/postgres/index.ts b/modules/postgres/index.ts index 61b1cff..5129f4e 100644 --- a/modules/postgres/index.ts +++ b/modules/postgres/index.ts @@ -14,11 +14,11 @@ interface DatabaseEvent { user?: string; } -await on("module.postgres.database.provisioned", async (e) => { +await on("database.provisioned", async (e) => { console.log(`[postgres] database provisioned for ${e.body.consumer} (db ${e.body.database})`); }); -await on("module.postgres.database.deprovisioned", async (e) => { +await on("database.deprovisioned", async (e) => { console.log(`[postgres] database deprovisioned for ${e.body.consumer} (db ${e.body.database})`); }); diff --git a/modules/postgres/module.json b/modules/postgres/module.json index b543e5f..68f3259 100644 --- a/modules/postgres/module.json +++ b/modules/postgres/module.json @@ -17,12 +17,12 @@ "container-runtime" ], "emits": [ - "module.postgres.database.provisioned", - "module.postgres.database.deprovisioned" + "database.provisioned", + "database.deprovisioned" ], "consumes": [ - "module.postgres.database.provisioned", - "module.postgres.database.deprovisioned" + "postgres.database.provisioned", + "postgres.database.deprovisioned" ], "listens": [ { diff --git a/modules/postgres/provisioner/index.ts b/modules/postgres/provisioner/index.ts index 825cd98..6c3916f 100644 --- a/modules/postgres/provisioner/index.ts +++ b/modules/postgres/provisioner/index.ts @@ -34,7 +34,7 @@ runProvisioner("postgres-database", { // Database and owning role share the consumer's login, so the consumer owns exactly its own. const database = p.as; await postgres.createDatabaseAndRole(database, p.as, p.password); - await announce("module.postgres.database.provisioned", { + await announce("database.provisioned", { consumer: p.consumer ?? "", database, user: p.as, @@ -43,7 +43,7 @@ runProvisioner("postgres-database", { async remove(p: { as: string }): Promise { await postgres.dropDatabaseAndRole(p.as, p.as); - await announce("module.postgres.database.deprovisioned", { database: p.as }); + await announce("database.deprovisioned", { database: p.as }); }, // Asked every minute by the harness: whether the backend still holds this consumer exactly as // the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120). diff --git a/modules/qbittorrent/index.ts b/modules/qbittorrent/index.ts index ae737fb..5f136d1 100644 --- a/modules/qbittorrent/index.ts +++ b/modules/qbittorrent/index.ts @@ -30,9 +30,9 @@ async function pollTorrents(): Promise { for (const [hash, t] of now) { const before = progressByHash.get(hash); if (before === undefined) { - await emit("module.qbittorrent.download.added", { name: t.name, category: t.category, sizeBytes: t.sizeBytes }); + await emit("download.added", { name: t.name, category: t.category, sizeBytes: t.sizeBytes }); } else if (before < 1 && t.progress >= 1) { - await emit("module.qbittorrent.download.completed", { name: t.name, category: t.category, sizeBytes: t.sizeBytes }); + await emit("download.completed", { name: t.name, category: t.category, sizeBytes: t.sizeBytes }); } } } diff --git a/modules/qbittorrent/module.json b/modules/qbittorrent/module.json index 1820649..0cea981 100644 --- a/modules/qbittorrent/module.json +++ b/modules/qbittorrent/module.json @@ -6,8 +6,8 @@ "container-runtime" ], "emits": [ - "module.qbittorrent.download.added", - "module.qbittorrent.download.completed" + "download.added", + "download.completed" ], "consumes": [], "own-secrets": { diff --git a/modules/radarr/index.ts b/modules/radarr/index.ts index 03c8fc2..801e727 100644 --- a/modules/radarr/index.ts +++ b/modules/radarr/index.ts @@ -40,12 +40,12 @@ async function pollQueue(radarr: RadarrClient): Promise { if (primed) { // Entered the queue since last look — Radarr grabbed a release. for (const [id, item] of now) { - if (!inQueue.has(id)) await emit("module.radarr.movie.grabbed", { title: item.title, status: item.status }); + if (!inQueue.has(id)) await emit("movie.grabbed", { title: item.title, status: item.status }); } // Left the queue — imported and done, unless it was last seen failing. for (const [id, item] of inQueue) { if (!now.has(id) && !FAILED_STATUSES.has(item.status)) { - await emit("module.radarr.download.completed", { title: item.title }); + await emit("download.completed", { title: item.title }); } } } diff --git a/modules/radarr/module.json b/modules/radarr/module.json index 4b39284..76701d4 100644 --- a/modules/radarr/module.json +++ b/modules/radarr/module.json @@ -5,8 +5,8 @@ "container-runtime" ], "emits": [ - "module.radarr.movie.grabbed", - "module.radarr.download.completed" + "movie.grabbed", + "download.completed" ], "consumes": [], "own-secrets": { diff --git a/modules/redis/index.ts b/modules/redis/index.ts index d15c8ac..c4a42ea 100644 --- a/modules/redis/index.ts +++ b/modules/redis/index.ts @@ -14,11 +14,11 @@ interface CacheEvent { keyspacePrefix?: string; } -await on("module.redis.cache.provisioned", async (e) => { +await on("cache.provisioned", async (e) => { console.log(`[redis] cache provisioned for ${e.body.consumer} (user ${e.body.username})`); }); -await on("module.redis.cache.deprovisioned", async (e) => { +await on("cache.deprovisioned", async (e) => { console.log(`[redis] cache deprovisioned for ${e.body.consumer} (user ${e.body.username})`); }); diff --git a/modules/redis/module.json b/modules/redis/module.json index 907db43..c4d77d0 100644 --- a/modules/redis/module.json +++ b/modules/redis/module.json @@ -14,12 +14,12 @@ "container-runtime" ], "emits": [ - "module.redis.cache.provisioned", - "module.redis.cache.deprovisioned" + "cache.provisioned", + "cache.deprovisioned" ], "consumes": [ - "module.redis.cache.provisioned", - "module.redis.cache.deprovisioned" + "redis.cache.provisioned", + "redis.cache.deprovisioned" ], "serves": { "redis-cache": { diff --git a/modules/redis/provisioner/index.ts b/modules/redis/provisioner/index.ts index 84aea66..0e51476 100644 --- a/modules/redis/provisioner/index.ts +++ b/modules/redis/provisioner/index.ts @@ -32,7 +32,7 @@ runProvisioner("redis-cache", { // The keyspace is scoped to the consumer's own login, so one cannot read another's keys. const keyspacePrefix = p.as; await redis.createAclUser(p.as, p.password, keyspacePrefix); - await announce("module.redis.cache.provisioned", { + await announce("cache.provisioned", { consumer: p.consumer ?? "", username: p.as, keyspacePrefix, @@ -41,7 +41,7 @@ runProvisioner("redis-cache", { async remove(p: { as: string }): Promise { await redis.deleteAclUser(p.as); - await announce("module.redis.cache.deprovisioned", { username: p.as }); + await announce("cache.deprovisioned", { username: p.as }); }, // This server keeps its ACL users in memory only, so a restart of it forgets every consumer while diff --git a/modules/showcase/index.ts b/modules/showcase/index.ts index 02a6ad2..8cd83fe 100644 --- a/modules/showcase/index.ts +++ b/modules/showcase/index.ts @@ -5,8 +5,8 @@ // It is here so the module exercises the shape rather than describing it. import { on, emit } from "@novox/mesh-sdk/events"; -await on<{ who?: string }>("module.showcase.greeted", async (event) => { +await on<{ who?: string }>("greeted", async (event) => { console.log(`[showcase] greeted ${event.body.who ?? "somebody"}`); // A consumer may emit, which is what makes an event graph rather than a list of sinks. - await emit("module.showcase.acknowledged", { who: event.body.who ?? "somebody" }); + await emit("acknowledged", { who: event.body.who ?? "somebody" }); }); diff --git a/modules/showcase/module.json b/modules/showcase/module.json index a72c2e3..8d9412f 100644 --- a/modules/showcase/module.json +++ b/modules/showcase/module.json @@ -35,10 +35,11 @@ } ], "emits": [ - "module.showcase.acknowledged" + "greeted", + "acknowledged" ], "consumes": [ - "module.showcase.greeted" + "showcase.greeted" ], "listens": [ { diff --git a/modules/sonarr/index.ts b/modules/sonarr/index.ts index 67c7b6d..e062ddf 100644 --- a/modules/sonarr/index.ts +++ b/modules/sonarr/index.ts @@ -40,12 +40,12 @@ async function pollQueue(sonarr: SonarrClient): Promise { if (primed) { // Entered the queue since last look — Sonarr grabbed a release. for (const [id, item] of now) { - if (!inQueue.has(id)) await emit("module.sonarr.episode.grabbed", { title: item.title, status: item.status }); + if (!inQueue.has(id)) await emit("episode.grabbed", { title: item.title, status: item.status }); } // Left the queue — imported and done, unless it was last seen failing. for (const [id, item] of inQueue) { if (!now.has(id) && !FAILED_STATUSES.has(item.status)) { - await emit("module.sonarr.download.completed", { title: item.title }); + await emit("download.completed", { title: item.title }); } } } diff --git a/modules/sonarr/module.json b/modules/sonarr/module.json index 32ecce9..1806886 100644 --- a/modules/sonarr/module.json +++ b/modules/sonarr/module.json @@ -5,8 +5,8 @@ "container-runtime" ], "emits": [ - "module.sonarr.episode.grabbed", - "module.sonarr.download.completed" + "episode.grabbed", + "download.completed" ], "consumes": [], "own-secrets": { diff --git a/modules/tautulli/index.ts b/modules/tautulli/index.ts index afbfc21..40aba14 100644 --- a/modules/tautulli/index.ts +++ b/modules/tautulli/index.ts @@ -33,7 +33,7 @@ async function pollHistory(client: TautulliClient): Promise { } async function emitWatch(w: TautulliWatch): Promise { - await emit("module.tautulli.watch.recorded", { + await emit("watch.recorded", { title: w.title, user: w.user, mediaType: w.mediaType, watchedStatus: w.watchedStatus, percentComplete: w.percentComplete, at: w.date, }); diff --git a/modules/tautulli/module.json b/modules/tautulli/module.json index 8752c70..ce94a77 100644 --- a/modules/tautulli/module.json +++ b/modules/tautulli/module.json @@ -2,7 +2,7 @@ "module": "tautulli", "version": "1", "emits": [ - "module.tautulli.watch.recorded" + "watch.recorded" ], "own-secrets": { "broker": "/var/lib/mesh/tautulli/broker" diff --git a/modules/verdaccio/index.ts b/modules/verdaccio/index.ts index 0c23d9e..7a13da0 100644 --- a/modules/verdaccio/index.ts +++ b/modules/verdaccio/index.ts @@ -28,7 +28,7 @@ async function pollPackages(): Promise { const known = latest.get(pkg.name); if (known !== pkg.version) { // A name we have not seen, or a name whose latest version moved — both are a publish. - if (primed) await emit("module.verdaccio.package.published", { name: pkg.name, version: pkg.version }); + if (primed) await emit("package.published", { name: pkg.name, version: pkg.version }); latest.set(pkg.name, pkg.version); } } diff --git a/modules/verdaccio/module.json b/modules/verdaccio/module.json index 5a161d4..b9d12bf 100644 --- a/modules/verdaccio/module.json +++ b/modules/verdaccio/module.json @@ -6,7 +6,7 @@ "container-runtime" ], "emits": [ - "module.verdaccio.package.published" + "package.published" ], "own-secrets": { "broker": "/var/lib/mesh/verdaccio/broker"