Files
mesh-catalog/modules/mongodb/provisioner/index.ts
T
jschoubben 1fb7ca3d72 A withdrawn consumer keeps its data, in every provider that holds some (hq issue 241)
mssql disables the login, mongodb takes the user's roles, minio revokes the key and keeps the bucket,
mailu disables the mailbox, gitea prohibits the login instead of purging the user and their
repositories, umami keeps the website. Each provider's create already enables what this locks.
2026-10-05 00:34:40 +02:00

54 lines
2.5 KiB
TypeScript

// mongodb's provisioner — the adapter that makes mongodb a provider of the mesh `mongodb-database`
// interface. The reconcile loop, the contributions file, and reading the mesh's minted password are
// the sdk harness's; this writes only the per-service half: how mongodb creates and removes a
// consumer's database + owning user (novox/hq ADR 0039/0040/0048).
//
// The `mongodb-database` interface: a consumer connects to a database it alone owns, as `as` with the
// password the mesh minted, authenticating against that same database.
//
// **The user name and password are the mesh's, not the provisioner's (ADR 0048).** The mesh derives
// the login and hands it to both ends, and mints the password. mongodb creates a user and a
// same-named database under exactly that login — a name the consumer cannot learn is a database it
// cannot reach.
//
// The commands run through MongoClient, the official driver inside this bundle (see client.ts).
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
import { emit } from "@novox/mesh-sdk/events";
import { MongoClient } from "../client.js";
const mongo = MongoClient.fromEnv();
/** Emit a lifecycle event without letting a broker hiccup fail the provisioning itself. */
async function announce(type: string, body: Record<string, string>): Promise<void> {
try {
await emit(type, body);
} catch (err) {
console.error(`[provisioner:mongodb-database] emit ${type} failed: ${err}`);
}
}
runProvisioner("mongodb-database", {
async create(p: Provision): Promise<void> {
// 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("database.provisioned", {
consumer: p.consumer ?? "",
database,
user: p.as,
});
},
async remove(p: { as: string }): Promise<void> {
// Locked, never dropped (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). create gives the roles back.
await mongo.lockUser(p.as, p.as);
await announce("database.deprovisioned", { database: p.as, kept: "true" });
},
// 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).
async holds(p: Provision): Promise<boolean> {
return mongo.canAuthenticateAs(p.as, p.as, p.password);
},
});