// umami's provisioner — the adapter that makes umami a provider of the mesh `analytics` interface. // The reconcile loop and the contributions file are the sdk harness's; this writes only the // per-service half: how umami creates and removes a tracked site (novox/hq ADR 0039/0040/0048). // // The `analytics` interface: a consumer contributes `{ domain }` (the site it wants tracked) and // receives `{ siteId, snippet, dashboard }`. // // **A note on scope (ADR 0048).** ADR 0048 corrects *credential* provisions: the mesh mints a secret // and the provider creates a login with it. Analytics is not that shape — it mints no secret the // consumer authenticates with; what the consumer needs back is data umami *generates* (the siteId). // The credential-provisioner contract returns nothing, so the siteId does not travel back to the // consumer here. That return path — for a provider that generates data rather than being handed a // secret — is a separate concern and is not solved by this decision. umami still reconciles its // sites off the mesh's contributions (it keys on the login the mesh derived), which is what this // keeps working. import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner"; import { UmamiClient } from "../client.js"; const umami = UmamiClient.fromEnv(); runProvisioner("analytics", { async create(p: Provision): Promise { const domain = String(p.values.domain ?? p.as); const name = String(p.values.name ?? domain); const token = await umami.getToken(); // Idempotent: only create the site if it is not already there. if (!(await umami.findWebsite(token, domain))) { await umami.createWebsite(token, domain, name); } }, async remove(p: { as: string }): Promise { // The website and its analytics are kept (novox/hq issue 241: a withdrawal never destroys a consumer's data — on 2026-10-04 a misread grants file withdrew every consumer at once); a person deletes a site, never this loop. console.error(`[umami] ${p.as} withdrawn: website and its analytics kept`); }, });