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.
54 lines
2.5 KiB
TypeScript
54 lines
2.5 KiB
TypeScript
// mssql's provisioner — the adapter that makes mssql a provider of the mesh `mssql-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 mssql creates and removes a
|
|
// consumer's database + login/user with the mesh-minted credential (novox/hq ADR 0039/0040/0048).
|
|
//
|
|
// The `mssql-database` interface: a consumer connects to a database it alone owns, as `as` with
|
|
// the password the mesh minted.
|
|
//
|
|
// **The login 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. mssql creates a login, a
|
|
// same-named database, and a db_owner user under exactly that login — a name the consumer cannot
|
|
// learn is a database it cannot reach.
|
|
//
|
|
// The DDL runs through MssqlClient, which is the module's one pending boundary (see client.ts).
|
|
|
|
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
|
|
import { emit } from "@novox/mesh-sdk/events";
|
|
import { MssqlClient } from "../client.js";
|
|
|
|
const mssql = MssqlClient.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:mssql-database] emit ${type} failed: ${err}`);
|
|
}
|
|
}
|
|
|
|
runProvisioner("mssql-database", {
|
|
async create(p: Provision): Promise<void> {
|
|
// 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("database.provisioned", {
|
|
consumer: p.consumer ?? "",
|
|
database,
|
|
user: p.as,
|
|
});
|
|
},
|
|
|
|
async remove(p: { as: string }): Promise<void> {
|
|
// Disabled, 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 enables the login again.
|
|
await mssql.disableLogin(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 mssql.holdsLogin(p.as, p.as, p.password);
|
|
},
|
|
});
|