Files
mesh-catalog/modules/mesh-vault/test/ledger.test.ts
jschoubben 82e513a360 Add the mesh-vault module; redis takes its password from it
mesh-vault provides `secret` (novox/hq ADR 0085, design 24). The value is
the pair credential the controller mints — the vault holds no copy, only a
ledger of who holds one, its fingerprint and every rotation, and two tools that
answer by fingerprint and never by value. Rotation is `rotate secret`,
unchanged machinery pointed at a secret with an owner (design 13). Named in the
mesh's own namespace, beside mesh-controller and mesh-catalog, because it is
the mesh's own code rather than wrapped software.

redis is the first consumer: its own password stops being an own-secret nothing
could rotate and becomes a `secret` it requires, read from the same file into
the same hole. The server now restarts on its config, or it would keep the
password it started with through every rotation (playbook 06).
2026-09-21 00:48:13 +02:00

81 lines
3.8 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { Ledger, fingerprint, contributions, deliveredFingerprint } from "../client.ts";
function fresh(): Ledger {
return new Ledger(mkdtempSync(join(tmpdir(), "vault-ledger-")));
}
test("a first delivery is a grant, the same value again is nothing, a new value is a rotation", () => {
const ledger = fresh();
const t0 = new Date("2026-09-20T10:00:00Z");
const t1 = new Date("2026-09-21T10:00:00Z");
const granted = ledger.record("anchor-redis", "anchor", "first-value", t0);
assert.equal(granted.outcome, "granted");
assert.equal(granted.held.rotations, 0);
assert.equal(granted.held.since, t0.toISOString());
assert.equal(granted.held.fingerprint, fingerprint("first-value"));
assert.equal(ledger.record("anchor-redis", "anchor", "first-value", t1).outcome, "unchanged");
assert.equal(ledger.get("anchor-redis")!.rotations, 0, "an unchanged delivery counted as a rotation");
const rotated = ledger.record("anchor-redis", "anchor", "second-value", t1);
assert.equal(rotated.outcome, "rotated");
assert.equal(rotated.held.rotations, 1);
assert.equal(rotated.held.since, t0.toISOString(), "a rotation reset the grant date");
assert.equal(rotated.held.changed, t1.toISOString());
assert.equal(rotated.held.fingerprint, fingerprint("second-value"));
assert.deepEqual(rotated.held.history, [{ fingerprint: fingerprint("first-value"), until: t1.toISOString() }]);
});
test("the ledger holds fingerprints and never the value, in files nobody else can read", () => {
const dir = mkdtempSync(join(tmpdir(), "vault-ledger-"));
const ledger = new Ledger(dir);
ledger.record("anchor-redis", "anchor", "the-actual-password", new Date());
ledger.record("anchor-redis", "anchor", "the-rotated-password", new Date());
for (const name of readdirSync(dir)) {
const raw = readFileSync(join(dir, name), "utf8");
assert.doesNotMatch(raw, /the-actual-password|the-rotated-password/, `${name} holds a value`);
assert.equal(statSync(join(dir, name)).mode & 0o777, 0o600, `${name} is readable by others`);
}
});
test("withdrawing forgets a holder, and listing is by login", () => {
const ledger = fresh();
ledger.record("b-app", "b", "x", new Date());
ledger.record("a-app", "a", "y", new Date());
assert.deepEqual(ledger.list().map((h) => h.as), ["a-app", "b-app"]);
assert.equal(ledger.withdraw("a-app"), true);
assert.equal(ledger.withdraw("a-app"), false, "withdrawing twice said it found something");
assert.deepEqual(ledger.list().map((h) => h.as), ["b-app"]);
});
test("a login is a name, not a path", () => {
const ledger = fresh();
assert.throws(() => ledger.record("../etc/passwd", "n", "v"), /a login is a name/);
});
test("what the mesh delivers is read from the contributions file and fingerprinted, never returned", () => {
const dir = mkdtempSync(join(tmpdir(), "vault-grants-"));
const secret = join(dir, "anchor.redis.secret");
writeFileSync(secret, "minted-value\n"); // the host may leave a trailing newline; the value has none
const receives = join(dir, "mesh.json");
writeFileSync(receives, JSON.stringify({
requirement: "secret",
given: [
{ from: "redis", node: "anchor", as: "anchor-redis", secret },
{ from: "offer-only", node: "anchor" }, // a contribution with no login grants nothing
],
}));
const asked = contributions(receives);
assert.deepEqual(asked.map((c) => c.as), ["anchor-redis"]);
const seen = deliveredFingerprint(asked[0]);
assert.deepEqual(seen, { fingerprint: fingerprint("minted-value"), length: "minted-value".length });
assert.match(JSON.stringify(deliveredFingerprint({ as: "x", secret: join(dir, "missing") })), /not readable/);
});