Files
mesh-catalog/modules/mesh-vault/provisioner/index.ts
T
jschoubben 89e0dde9e0 The vault's claim and its own event names return
The uplink branch was split from the vault's with the vault's files reset to a main that did not yet
hold #205; merging it afterwards took the older vault definition along (no claim, the refused event
names), and the vault could not be built. Restored to #205's state.
2026-10-01 17:19:05 +02:00

49 lines
2.3 KiB
TypeScript

// mesh-vault's provisioner — the adapter that makes vault a provider of the mesh `secret` interface. The
// reconcile loop, the contributions file, and reading the mesh's minted value are the sdk harness's;
// this writes only the per-service half (novox/hq ADR 0039/0040/0048) — and for a vault that half is
// taking custody, not creating anything.
//
// The `secret` interface (ADR 0085, design 24): a consumer requires a value for its own use — the
// password of a store it runs privately, an internal token — and reads it from the file the mesh
// writes on its machine. There is no server to create a login on. **The value is the pair
// credential itself**: the controller minted it, sealed it to both nodes, and delivered each its
// copy. What makes it *owned* is this: the vault records who holds it and its fingerprint, notices
// when `rotate secret` delivers a different one, and says so on the mesh. Rotation is not new
// machinery — it is the machinery that already moves a database password, pointed at a secret the
// vault provides (design 13).
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
import { emit } from "@novox/mesh-sdk/events";
import { Ledger } from "../client.js";
const ledger = Ledger.fromEnv();
/** Emit a lifecycle event without letting a broker hiccup fail the custody itself. */
async function announce(type: string, body: Record<string, string | number>): Promise<void> {
try {
await emit(type, body);
} catch (err) {
console.error(`[provisioner:secret] emit ${type} failed: ${err}`);
}
}
runProvisioner("secret", {
async create(p: Provision): Promise<void> {
const { held, outcome } = ledger.record(p.as, p.consumer ?? "", p.password);
if (outcome === "unchanged") return; // the harness re-runs create on restart; nothing happened
console.log(`[mesh-vault] ${outcome}: ${held.as} (${held.fingerprint.slice(0, 19)}…, rotations ${held.rotations})`);
await announce(`module.mesh-vault.secret.${outcome === "granted" ? "provisioned" : "rotated"}`, {
consumer: held.consumer,
as: held.as,
fingerprint: held.fingerprint,
rotations: held.rotations,
});
},
async remove(p: { as: string }): Promise<void> {
if (!ledger.withdraw(p.as)) return;
console.log(`[mesh-vault] withdrawn: ${p.as}`);
await announce("deprovisioned", { as: p.as });
},
});