/* Unit tests for wba signer covering resolveOutboundAgentClient. It also guards the documented public contract and regression-sensitive edge cases. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-1311: Compass header sweep — declares the wba suite's contract for test-file scaffold coverage. */ // ============================================================================ // COMPASS MODULE HEADER // ============================================================================ // - Module: packages/werkstatt-engine/src/leitstand/tests/wba-signer.test.ts // - Summary: RFC-1188 Leitstand outbound-signing tests — identity resolution, // degraded unsigned fallback, and signature validity of signed fetches. // - Author: Devin (AI agent) // - Date: 2026-10-03 // - Scope: test // - Dependencies: vitest, leitstand/wba-signer.ts, werkstatt-shared/agent, // werkstatt-shared/passport // - Consumers: test suite // - Last updated: 2026-10-03 // ============================================================================ import { getPublicKeyAsync } from "@noble/ed25519"; import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { toMultibase } from "@warpgogol/werkstatt-shared/passport/sign"; import { verifyBytes } from "@warpgogol/werkstatt-shared/signing"; import { jwkToPublicKeyBytes, passportKeyToJwk, type WbaJwk, } from "@warpgogol/werkstatt-shared/agent"; import { resolveOutboundAgentClient } from "../wba-signer.ts"; const TEST_PRIVATE_HEX = "b3f84ddb6973a143c4b2fc6b0b8d6b3cb33863b2e30dca94241029111e81e14f"; const TEST_PRIVATE_KEY = new Uint8Array( TEST_PRIVATE_HEX.match(/.{2}/g)!.map((b) => parseInt(b, 16)), ); let sandboxDir = ""; let workspaceRoot = ""; const SYSTEM_ID = "testsys"; const CHANNEL_ORIGIN = "https://main.testsys.example"; const DIRECTORY_URL = `${CHANNEL_ORIGIN}/.well-known/http-message-signatures-directory`; async function writeFixture(options: { keyFile: boolean; envKey: boolean }): Promise { const appDir = resolve(workspaceRoot, "..", "systems-cache", SYSTEM_ID); await mkdir(join(appDir, "public", ".well-known"), { recursive: true }); const pub = await getPublicKeyAsync(TEST_PRIVATE_KEY); await writeFile( join(appDir, "system-config.yaml"), [ "schemaVersion: 1.0.0", `id: ${SYSTEM_ID}`, "cosmicStar: Vega", "mirrors:", " - path: ../systems-git/testsys", " storageType: bare", "pinnedPlatform: 6.0.0", "status: active", "registeredAt: 2026-01-01T00:00:00.000Z", "deployment:", " adapter: cloudflare-workers", " channels:", " dev:", " workerName: dev-testsys", " url: https://dev.testsys.example", " alt:", " workerName: alt-testsys", " url: https://alt.testsys.example", " main:", " workerName: testsys", ` url: ${CHANNEL_ORIGIN}`, "", ].join("\n"), ); if (options.keyFile) { await writeFile( join(appDir, "public", ".well-known", "cosmic-passport-key.json"), JSON.stringify( { schemaVersion: "1.0", appId: SYSTEM_ID, keys: [ { version: "v1", active: true, type: "Ed25519VerificationKey2020", publicKeyMultibase: toMultibase(pub), createdAt: "2026-01-01T00:00:00.000Z", }, ], }, null, 2, ), ); } if (options.envKey) { await writeFile(join(appDir, ".env"), `PASSPORT_SIGNING_KEY=${TEST_PRIVATE_HEX}\n`); } } beforeEach(async () => { // Unique sandbox per test — systems-cache must NOT resolve to the shared // /tmp/systems-cache pool (EC-16 flake class: another worker's cleanup // deletes the pool mid-test → ENOENT on fixture writes). sandboxDir = await mkdtemp(join(tmpdir(), "wba-signer-")); workspaceRoot = join(sandboxDir, "werkstatt"); await mkdir(workspaceRoot, { recursive: true }); vi.stubEnv("PASSPORT_SIGNING_KEY", ""); vi.stubEnv("WBA_IDENTITY_SITE", ""); }); afterEach(async () => { vi.unstubAllEnvs(); await rm(sandboxDir, { recursive: true, force: true }); }); function capturedFetch(): { impl: typeof fetch; request: () => Request | null; } { let seen: Request | null = null; const impl: typeof fetch = (async (input: RequestInfo | URL, init?: RequestInit) => { seen = input instanceof Request ? input : new Request(input, init); return new Response("{}", { status: 200 }); }) as typeof fetch; return { impl, request: () => seen }; } /** * Independent verifier — rebuilds the RFC 9421 signature base from the * emitted headers (no shared code path with the signer) and verifies with * the published JWK material. Mirrors the AC-3 verifier in * werkstatt-shared's wba-sign.test.ts. */ async function independentVerify(request: Request, jwk: WbaJwk): Promise { const signatureInput = request.headers.get("Signature-Input")!; const signature = request.headers.get("Signature")!; const m = signatureInput.match( /^sig1=\(([^)]*)\);keyid="([^"]+)";alg=ed25519;created=(\d+);expires=(\d+)$/, ); if (!m) return false; const covered = m[1].split(" ").map((s) => s.replaceAll('"', "")); const url = new URL(request.url); const lines: string[] = []; for (const comp of covered) { if (comp === "@method") lines.push(`"@method": ${request.method.toUpperCase()}`); else if (comp === "@authority") lines.push(`"@authority": ${url.host}`); else if (comp === "@path") lines.push(`"@path": ${url.pathname}`); else if (comp === "signature-agent") lines.push(`"signature-agent": ${request.headers.get("Signature-Agent")}`); else if (comp === "content-digest") lines.push(`"content-digest": ${request.headers.get("Content-Digest")}`); else return false; } const params = signatureInput.slice("sig1=".length); lines.push(`"@signature-params": ${params}`); const sigB64 = signature.match(/^sig1=:([A-Za-z0-9+/=]+):$/)?.[1]; if (!sigB64) return false; const sigBytes = new Uint8Array([...atob(sigB64)].map((c) => c.charCodeAt(0))); return verifyBytes( jwkToPublicKeyBytes(jwk), new TextEncoder().encode(lines.join("\n")), sigBytes, ); } describe("resolveOutboundAgentClient", () => { it("returns a signing client that produces RFC-9421 headers", async () => { await writeFixture({ keyFile: true, envKey: true }); const capture = capturedFetch(); const outbound = await resolveOutboundAgentClient(workspaceRoot, SYSTEM_ID, capture.impl); expect(outbound.mode).toBe("signed"); expect(outbound.reason).toBeUndefined(); await outbound.client.fetch(`${CHANNEL_ORIGIN}/agent`, { method: "GET" }); const req = capture.request()!; expect(req.headers.get("Signature-Agent")).toBe(DIRECTORY_URL); expect(req.headers.get("Signature-Input")).toContain( 'sig1=("@method" "@authority" "@path" "signature-agent")', ); expect(req.headers.get("Signature-Input")).toContain('keyid="v1"'); expect(req.headers.get("Signature")).toMatch(/^sig1=:[A-Za-z0-9+/=]+:$/); expect(req.headers.get("User-Agent")).toContain("WarpgogolBot/"); expect(req.headers.get("User-Agent")).toContain("http-message-signatures-directory"); // Independent verification against the fixture public key — same path a // verifier takes after resolving our published directory JWK. const pub = await getPublicKeyAsync(TEST_PRIVATE_KEY); const jwk = passportKeyToJwk(pub, "v1"); expect(await independentVerify(req, jwk)).toBe(true); }); it("degrades to unsigned pass-through without a private key", async () => { await writeFixture({ keyFile: true, envKey: false }); const capture = capturedFetch(); const outbound = await resolveOutboundAgentClient(workspaceRoot, SYSTEM_ID, capture.impl); expect(outbound.mode).toBe("degraded"); expect(outbound.reason).toContain("PASSPORT_SIGNING_KEY"); await outbound.client.fetch(`${CHANNEL_ORIGIN}/agent`, { method: "GET" }); expect(capture.request()!.headers.get("Signature")).toBeNull(); expect(capture.request()!.headers.get("User-Agent")).toContain("WarpgogolBot/"); }); it("degrades to unsigned when no channel origin resolves", async () => { const capture = capturedFetch(); const outbound = await resolveOutboundAgentClient( workspaceRoot, "unknown-system", capture.impl, ); expect(outbound.mode).toBe("degraded"); await outbound.client.fetch("https://anything.example/x", { method: "GET", }); expect(capture.request()!.headers.get("Signature")).toBeNull(); }); });