/*
Unit tests for leitstand sign covering RFC-0931: signBuildIdentity; RFC-0931: writeReleasePublicKey; RFC-0931: verifyBuildIdentitySignature.
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- RFC-0931: initial tests for signBuildIdentity, writeReleasePublicKey, verifyBuildIdentitySignature, and leitstand.verify --verify-signature.
*/
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { mkdtempSync, writeFileSync, readFileSync, existsSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import {
signBuildIdentity,
writeReleasePublicKey,
verifyBuildIdentitySignature,
} from "../../integrity/signing.ts";
import { generateKeyPair, toHex } from "@warpgogol/werkstatt-engine/signing";
import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io";
vi.mock("../../sternsystem/registry-io.ts", () => ({
readSystemConfigSmart: vi.fn(),
readSystemStateSmart: vi.fn(),
resolveCacheClonePath: vi.fn(),
}));
const { runLeitstandVerify } = await import("../leitstand-commands.ts");
const { readSystemConfigSmart, readSystemStateSmart } =
await import("../../sternsystem/registry-io.ts");
function makeInput(flags: Record) {
return { flags, args: [] } as unknown as Parameters[0];
}
function makeContext(workspaceRoot: string) {
return {
io: createDefaultIO().io,
workspaceRoot,
dryRun: false,
site: undefined,
} as unknown as Parameters[1];
}
const defaultConfig = {
schemaVersion: "1.0.0",
id: "test-site",
deployment: {
adapter: "cloudflare-workers" as const,
channels: {
dev: { workerName: "dev-test", url: "https://dev.test.com" },
alt: { workerName: "alt-test", url: "https://alt.test.com" },
main: { workerName: "test", url: "https://test.com" },
},
},
};
const defaultState = {
schemaVersion: "1.0.0",
systemId: "test-site",
currentMission: null,
lastRelease: null,
lastPropagated: {
dev: {
releaseId: "r000005",
at: "2026-08-23T12:00:00.000Z",
healthy: true,
state: "succeeded" as const,
operationId: "op-1",
leaseExpiresAt: null,
},
alt: {
releaseId: "r000005",
at: "2026-08-23T13:00:00.000Z",
healthy: true,
state: "succeeded" as const,
operationId: "op-2",
leaseExpiresAt: null,
},
main: {
releaseId: "r000005",
at: "2026-08-23T14:00:00.000Z",
healthy: true,
state: "succeeded" as const,
operationId: "op-3",
leaseExpiresAt: null,
},
},
accessPin: null as string | null,
};
function mockBuildIdentity(distTreeHash: string, releaseId = "r000005") {
return JSON.stringify({
distTreeHash,
releaseId,
missionId: "m000076",
buildTimestamp: "2026-08-23T12:45:06Z",
});
}
let tmpDir: string;
let keyPair: { privateKey: Uint8Array; publicKey: Uint8Array };
beforeEach(async () => {
tmpDir = mkdtempSync(join(tmpdir(), "leitstand-sign-"));
keyPair = await generateKeyPair();
});
afterEach(() => {
vi.restoreAllMocks();
});
describe("RFC-0931: signBuildIdentity", () => {
it("signs build-identity.json and embeds signature, publicKeyUrl, signedAt", async () => {
const idPath = join(tmpDir, "build-identity.json");
writeFileSync(
idPath,
JSON.stringify({
releaseId: "test-r000001",
systemId: "test-site",
missionId: "m000001",
semver: "1.0.0",
distTreeHash: "sha256:abc",
behaviorSnapshotHash: "sha256:def",
siteContentHash: "sha256:ghi",
platformVersion: "6.0.0",
platformSemanticHash: "sha256:sem",
commitSha: "abc123",
buildTimestamp: "2026-08-23T12:00:00.000Z",
targetPlatform: "cloudflare",
}),
);
const result = await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: keyPair.privateKey,
publicKeyUrl: "https://example.com/.well-known/release-pubkey.json",
});
expect(result.signatureHex).toMatch(/^[0-9a-f]{128}$/);
expect(result.publicKeyHex).toMatch(/^[0-9a-f]{64}$/);
expect(result.publicKeyUrl).toBe("https://example.com/.well-known/release-pubkey.json");
expect(result.signedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
expect(result.reusedExistingSignature).toBe(false);
const signed = JSON.parse(readFileSync(idPath, "utf8"));
expect(signed.signature).toBe(result.signatureHex);
expect(signed.publicKeyUrl).toBe("https://example.com/.well-known/release-pubkey.json");
expect(signed.signedAt).toBe(result.signedAt);
expect(signed.distTreeHash).toBe("sha256:abc");
});
it("reuses existing signature when payload hasn't changed (idempotency)", async () => {
const idPath = join(tmpDir, "build-identity.json");
const baseIdentity = {
releaseId: "test-r000001",
systemId: "test-site",
missionId: "m000001",
semver: "1.0.0",
distTreeHash: "sha256:abc",
behaviorSnapshotHash: "sha256:def",
siteContentHash: "sha256:ghi",
platformVersion: "6.0.0",
platformSemanticHash: "sha256:sem",
commitSha: "abc123",
buildTimestamp: "2026-08-23T12:00:00.000Z",
targetPlatform: "cloudflare",
};
writeFileSync(idPath, JSON.stringify(baseIdentity));
const first = await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: keyPair.privateKey,
});
const second = await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: keyPair.privateKey,
});
expect(second.reusedExistingSignature).toBe(true);
expect(second.signatureHex).toBe(first.signatureHex);
});
it("produces new signature when payload has changed", async () => {
const idPath = join(tmpDir, "build-identity.json");
writeFileSync(
idPath,
JSON.stringify({
releaseId: "test-r000001",
systemId: "test-site",
missionId: "m000001",
semver: "1.0.0",
distTreeHash: "sha256:abc",
behaviorSnapshotHash: "sha256:def",
siteContentHash: "sha256:ghi",
platformVersion: "6.0.0",
platformSemanticHash: "sha256:sem",
commitSha: "abc123",
buildTimestamp: "2026-08-23T12:00:00.000Z",
targetPlatform: "cloudflare",
}),
);
const first = await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: keyPair.privateKey,
});
// Modify payload
const signed = JSON.parse(readFileSync(idPath, "utf8"));
signed.distTreeHash = "sha256:changed";
writeFileSync(idPath, JSON.stringify(signed));
const second = await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: keyPair.privateKey,
});
expect(second.reusedExistingSignature).toBe(false);
expect(second.signatureHex).not.toBe(first.signatureHex);
});
});
describe("RFC-0931: writeReleasePublicKey", () => {
it("writes release-pubkey.json to dist/client/.well-known/", async () => {
const distDir = join(tmpDir, "dist");
const publicKeyHex = toHex(keyPair.publicKey);
const result = await writeReleasePublicKey({ distDir, publicKeyHex });
expect(existsSync(result)).toBe(true);
const content = JSON.parse(readFileSync(result, "utf8"));
expect(content.publicKeyHex).toBe(publicKeyHex);
expect(content.algorithm).toBe("Ed25519");
expect(content.keyId).toMatch(/^[0-9a-f]{64}$/);
expect(content.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
});
});
describe("RFC-0931: verifyBuildIdentitySignature", () => {
it("verifies a valid signature", async () => {
const idPath = join(tmpDir, "build-identity.json");
const identity = {
releaseId: "test-r000001",
systemId: "test-site",
missionId: "m000001",
semver: "1.0.0",
distTreeHash: "sha256:abc",
behaviorSnapshotHash: "sha256:def",
siteContentHash: "sha256:ghi",
platformVersion: "6.0.0",
platformSemanticHash: "sha256:sem",
commitSha: "abc123",
buildTimestamp: "2026-08-23T12:00:00.000Z",
targetPlatform: "cloudflare",
};
writeFileSync(idPath, JSON.stringify(identity));
const signResult = await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: keyPair.privateKey,
});
const signedIdentity = JSON.parse(readFileSync(idPath, "utf8"));
const valid = await verifyBuildIdentitySignature({
buildIdentity: signedIdentity,
publicKeyHex: signResult.publicKeyHex,
});
expect(valid).toBe(true);
});
it("rejects tampered build-identity.json", async () => {
const idPath = join(tmpDir, "build-identity.json");
writeFileSync(
idPath,
JSON.stringify({
releaseId: "test-r000001",
systemId: "test-site",
missionId: "m000001",
semver: "1.0.0",
distTreeHash: "sha256:abc",
behaviorSnapshotHash: "sha256:def",
siteContentHash: "sha256:ghi",
platformVersion: "6.0.0",
platformSemanticHash: "sha256:sem",
commitSha: "abc123",
buildTimestamp: "2026-08-23T12:00:00.000Z",
targetPlatform: "cloudflare",
}),
);
const signResult = await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: keyPair.privateKey,
});
const signedIdentity = JSON.parse(readFileSync(idPath, "utf8"));
signedIdentity.distTreeHash = "sha256:tampered";
const valid = await verifyBuildIdentitySignature({
buildIdentity: signedIdentity,
publicKeyHex: signResult.publicKeyHex,
});
expect(valid).toBe(false);
});
it("returns false when no signature present", async () => {
const valid = await verifyBuildIdentitySignature({
buildIdentity: { releaseId: "test", distTreeHash: "sha256:abc" },
publicKeyHex: toHex(keyPair.publicKey),
});
expect(valid).toBe(false);
});
});
describe("RFC-0931: leitstand.verify --verify-signature", () => {
it("signatureValid is null when --verify-signature is not set", async () => {
vi.mocked(readSystemConfigSmart).mockResolvedValueOnce(defaultConfig as never);
vi.mocked(readSystemStateSmart).mockResolvedValueOnce(defaultState as never);
vi.spyOn(globalThis, "fetch").mockImplementation(() =>
Promise.resolve(new Response(mockBuildIdentity("sha256:abc123"), { status: 200 })),
);
const result = await runLeitstandVerify(makeInput({ site: "test-site" }), makeContext("/tmp"));
for (const ch of result.data!.channels) {
expect(ch.signatureValid).toBeNull();
expect(ch.publicKeyUrl).toBeNull();
}
});
it("signatureValid is true when signature matches public key", async () => {
vi.mocked(readSystemConfigSmart).mockResolvedValueOnce(defaultConfig as never);
vi.mocked(readSystemStateSmart).mockResolvedValueOnce(defaultState as never);
const kp = await generateKeyPair();
const pubKeyHex = toHex(kp.publicKey);
// Create signed build-identity
const identity = {
distTreeHash: "sha256:abc123",
releaseId: "r000005",
missionId: "m000076",
buildTimestamp: "2026-08-23T12:45:06Z",
};
const idPath = join(tmpDir, "build-identity.json");
writeFileSync(idPath, JSON.stringify(identity));
await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: kp.privateKey,
});
const signedIdentity = JSON.parse(readFileSync(idPath, "utf8"));
const pubKeyJson = JSON.stringify({
keyId: "test-key-id",
publicKeyHex: pubKeyHex,
algorithm: "Ed25519",
createdAt: "2026-08-23T12:00:00.000Z",
});
vi.spyOn(globalThis, "fetch").mockImplementation((url: string | URL | Request) => {
const urlStr = typeof url === "string" ? url : url.toString();
if (urlStr.includes("release-pubkey.json")) {
return Promise.resolve(new Response(pubKeyJson, { status: 200 }));
}
// build-identity.json fetch — return signed version
return Promise.resolve(new Response(JSON.stringify(signedIdentity), { status: 200 }));
});
const result = await runLeitstandVerify(
makeInput({ site: "test-site", "verify-signature": true }),
makeContext("/tmp"),
);
for (const ch of result.data!.channels) {
expect(ch.signatureValid).toBe(true);
expect(ch.publicKeyUrl).toContain("release-pubkey.json");
}
});
it("signatureValid is false when signature does not match (wrong key)", async () => {
vi.mocked(readSystemConfigSmart).mockResolvedValueOnce(defaultConfig as never);
vi.mocked(readSystemStateSmart).mockResolvedValueOnce(defaultState as never);
const kp1 = await generateKeyPair();
const kp2 = await generateKeyPair();
const pubKeyHex2 = toHex(kp2.publicKey);
const identity = {
distTreeHash: "sha256:abc123",
releaseId: "r000005",
missionId: "m000076",
buildTimestamp: "2026-08-23T12:45:06Z",
};
const idPath = join(tmpDir, "build-identity.json");
writeFileSync(idPath, JSON.stringify(identity));
await signBuildIdentity({
buildIdentityPath: idPath,
privateKeyBytes: kp1.privateKey,
});
const signedIdentity = JSON.parse(readFileSync(idPath, "utf8"));
const pubKeyJson = JSON.stringify({
keyId: "test-key-id",
publicKeyHex: pubKeyHex2,
algorithm: "Ed25519",
createdAt: "2026-08-23T12:00:00.000Z",
});
vi.spyOn(globalThis, "fetch").mockImplementation((url: string | URL | Request) => {
const urlStr = typeof url === "string" ? url : url.toString();
if (urlStr.includes("release-pubkey.json")) {
return Promise.resolve(new Response(pubKeyJson, { status: 200 }));
}
return Promise.resolve(new Response(JSON.stringify(signedIdentity), { status: 200 }));
});
const result = await runLeitstandVerify(
makeInput({ site: "test-site", "verify-signature": true }),
makeContext("/tmp"),
);
for (const ch of result.data!.channels) {
expect(ch.signatureValid).toBe(false);
}
});
it("signatureValid is null when release-pubkey.json is not found (404)", async () => {
vi.mocked(readSystemConfigSmart).mockResolvedValueOnce(defaultConfig as never);
vi.mocked(readSystemStateSmart).mockResolvedValueOnce(defaultState as never);
vi.spyOn(globalThis, "fetch").mockImplementation((url: string | URL | Request) => {
const urlStr = typeof url === "string" ? url : url.toString();
if (urlStr.includes("release-pubkey.json")) {
return Promise.resolve(new Response("Not Found", { status: 404 }));
}
return Promise.resolve(new Response(mockBuildIdentity("sha256:abc123"), { status: 200 }));
});
const result = await runLeitstandVerify(
makeInput({ site: "test-site", "verify-signature": true }),
makeContext("/tmp"),
);
for (const ch of result.data!.channels) {
expect(ch.signatureValid).toBeNull();
}
});
it("signatureValid is null when build-identity.json has no signature field", async () => {
vi.mocked(readSystemConfigSmart).mockResolvedValueOnce(defaultConfig as never);
vi.mocked(readSystemStateSmart).mockResolvedValueOnce(defaultState as never);
const pubKeyJson = JSON.stringify({
keyId: "test-key-id",
publicKeyHex: toHex(keyPair.publicKey),
algorithm: "Ed25519",
createdAt: "2026-08-23T12:00:00.000Z",
});
vi.spyOn(globalThis, "fetch").mockImplementation((url: string | URL | Request) => {
const urlStr = typeof url === "string" ? url : url.toString();
if (urlStr.includes("release-pubkey.json")) {
return Promise.resolve(new Response(pubKeyJson, { status: 200 }));
}
// Unsigned build-identity.json
return Promise.resolve(new Response(mockBuildIdentity("sha256:abc123"), { status: 200 }));
});
const result = await runLeitstandVerify(
makeInput({ site: "test-site", "verify-signature": true }),
makeContext("/tmp"),
);
for (const ch of result.data!.channels) {
expect(ch.signatureValid).toBe(false);
}
});
});