@appforge-ci/core 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/agent-readiness.d.ts +232 -1
  2. package/dist/agent-readiness.d.ts.map +1 -1
  3. package/dist/agent-readiness.js +294 -1
  4. package/dist/agent-readiness.js.map +1 -1
  5. package/dist/agent-readiness.test.js +253 -19
  6. package/dist/agent-readiness.test.js.map +1 -1
  7. package/dist/api-client.d.ts +42 -3
  8. package/dist/api-client.d.ts.map +1 -1
  9. package/dist/api-client.js +27 -0
  10. package/dist/api-client.js.map +1 -1
  11. package/dist/api-client.test.js +22 -0
  12. package/dist/api-client.test.js.map +1 -1
  13. package/dist/attestation.d.ts +265 -0
  14. package/dist/attestation.d.ts.map +1 -0
  15. package/dist/attestation.js +327 -0
  16. package/dist/attestation.js.map +1 -0
  17. package/dist/attestation.test.d.ts +2 -0
  18. package/dist/attestation.test.d.ts.map +1 -0
  19. package/dist/attestation.test.js +218 -0
  20. package/dist/attestation.test.js.map +1 -0
  21. package/dist/build-vpn.d.ts +10 -0
  22. package/dist/build-vpn.d.ts.map +1 -0
  23. package/dist/build-vpn.js +23 -0
  24. package/dist/build-vpn.js.map +1 -0
  25. package/dist/index.d.ts +5 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +5 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/schemas.d.ts +342 -7
  30. package/dist/schemas.d.ts.map +1 -1
  31. package/dist/schemas.js +92 -2
  32. package/dist/schemas.js.map +1 -1
  33. package/dist/types.d.ts +149 -0
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/types.js.map +1 -1
  36. package/dist/vpn-egress-presets.d.ts +142 -0
  37. package/dist/vpn-egress-presets.d.ts.map +1 -0
  38. package/dist/vpn-egress-presets.js +318 -0
  39. package/dist/vpn-egress-presets.js.map +1 -0
  40. package/dist/vpn-egress-presets.test.d.ts +2 -0
  41. package/dist/vpn-egress-presets.test.d.ts.map +1 -0
  42. package/dist/vpn-egress-presets.test.js +210 -0
  43. package/dist/vpn-egress-presets.test.js.map +1 -0
  44. package/dist/vpn-egress.d.ts +57 -0
  45. package/dist/vpn-egress.d.ts.map +1 -0
  46. package/dist/vpn-egress.js +126 -0
  47. package/dist/vpn-egress.js.map +1 -0
  48. package/dist/vpn-egress.test.d.ts +2 -0
  49. package/dist/vpn-egress.test.d.ts.map +1 -0
  50. package/dist/vpn-egress.test.js +133 -0
  51. package/dist/vpn-egress.test.js.map +1 -0
  52. package/dist/vpn-ssh.d.ts +45 -0
  53. package/dist/vpn-ssh.d.ts.map +1 -0
  54. package/dist/vpn-ssh.js +108 -0
  55. package/dist/vpn-ssh.js.map +1 -0
  56. package/dist/vpn-ssh.test.d.ts +2 -0
  57. package/dist/vpn-ssh.test.d.ts.map +1 -0
  58. package/dist/vpn-ssh.test.js +75 -0
  59. package/dist/vpn-ssh.test.js.map +1 -0
  60. package/package.json +1 -1
@@ -0,0 +1,327 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Tamper-evident audit chain primitives for the regulated-VPN program
4
+ * (PR-J). Pure and runtime-neutral: WebCrypto only (`globalThis.crypto`,
5
+ * present in Cloudflare Workers, Node 20+ and browsers), no `node:crypto`,
6
+ * so the Worker sealer (`@appforge/db`'s `audit-chain.ts`), the future
7
+ * signing module and the offline verifier share one implementation of
8
+ * what is hashed and signed.
9
+ *
10
+ * What the chain is: one append-only hash chain per org. Each entry holds
11
+ * the exact canonical JSON that was hashed (`payload`), so verification
12
+ * never has to re-read a mutable source row.
13
+ *
14
+ * hash(seq 1) = sha256("appforge-audit-v1\n" + genesis(orgId) + "\n" + canonicalJson(payload))
15
+ * hash(seq n) = sha256("appforge-audit-v1\n" + hash(n-1) + "\n" + canonicalJson(payload))
16
+ * genesis(org) = sha256("appforge-audit-genesis-v1:" + orgId)
17
+ *
18
+ * What it is not: on its own the chain proves nothing against someone
19
+ * who can write to the database, because they can recompute every hash.
20
+ * The protection comes from signed checkpoints (`AuditCheckpoint`, signed
21
+ * by a key the database does not hold) and from copies of a head hash
22
+ * that the customer keeps. Until checkpoints are signed and issued
23
+ * (PR-K) this is hash-chaining only.
24
+ */
25
+ // ---------------------------------------------------------------- domain separation
26
+ /** Prefix of every chain-entry hash input. */
27
+ export const AUDIT_ENTRY_DOMAIN = "appforge-audit-v1\n";
28
+ /** `genesis(orgId)` input prefix: one org's chain cannot be spliced into another's. */
29
+ export const AUDIT_GENESIS_PREFIX = "appforge-audit-genesis-v1:";
30
+ /** Prefix of every checkpoint signature input: a checkpoint signature can never validate as anything else. */
31
+ export const AUDIT_CHECKPOINT_DOMAIN = "appforge-checkpoint-v1\n";
32
+ // ---------------------------------------------------------------- canonical JSON
33
+ const LONE_SURROGATE = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
34
+ const MAX_CANONICAL_DEPTH = 32;
35
+ function canonicalString(s) {
36
+ if (LONE_SURROGATE.test(s))
37
+ throw new Error("canonicalJson: string contains a lone surrogate");
38
+ return JSON.stringify(s);
39
+ }
40
+ function canonicalValue(v, depth) {
41
+ if (depth > MAX_CANONICAL_DEPTH)
42
+ throw new Error("canonicalJson: nesting too deep");
43
+ if (v === null)
44
+ return "null";
45
+ switch (typeof v) {
46
+ case "boolean":
47
+ return v ? "true" : "false";
48
+ case "string":
49
+ return canonicalString(v);
50
+ case "number":
51
+ if (!Number.isSafeInteger(v))
52
+ throw new Error(`canonicalJson: only safe integers are allowed, got ${String(v)}`);
53
+ return String(v);
54
+ case "object": {
55
+ if (Array.isArray(v)) {
56
+ const parts = [];
57
+ for (let i = 0; i < v.length; i++) {
58
+ if (!(i in v))
59
+ throw new Error(`canonicalJson: sparse array at index ${i}`);
60
+ parts.push(canonicalValue(v[i], depth + 1));
61
+ }
62
+ return `[${parts.join(",")}]`;
63
+ }
64
+ const proto = Object.getPrototypeOf(v);
65
+ if (proto !== Object.prototype && proto !== null)
66
+ throw new Error("canonicalJson: only plain objects are allowed");
67
+ const rec = v;
68
+ // Default sort compares UTF-16 code units, which is the RFC 8785 member order.
69
+ const keys = Object.keys(rec).sort();
70
+ const parts = [];
71
+ for (const k of keys) {
72
+ if (rec[k] === undefined)
73
+ throw new Error(`canonicalJson: undefined value at key ${JSON.stringify(k)}`);
74
+ parts.push(`${canonicalString(k)}:${canonicalValue(rec[k], depth + 1)}`);
75
+ }
76
+ return `{${parts.join(",")}}`;
77
+ }
78
+ default:
79
+ throw new Error(`canonicalJson: unsupported type ${typeof v}`);
80
+ }
81
+ }
82
+ /**
83
+ * Canonical JSON: the RFC 8785 (JCS) subset this product needs. Object keys
84
+ * sorted by UTF-16 code unit, no whitespace, strings as `JSON.stringify`
85
+ * writes them (identical to RFC 8785's string rules for well-formed text).
86
+ * Numbers must be safe integers: the audit schema has no floats (timestamps
87
+ * are strings, sizes are integer bytes), which removes the one place two
88
+ * implementations could disagree. Throws on `undefined`, floats, NaN,
89
+ * bigint, functions, non-plain objects, sparse arrays, lone surrogates and
90
+ * absurd nesting, so a value that cannot be hashed identically everywhere
91
+ * is rejected rather than hashed differently.
92
+ */
93
+ export function canonicalJson(value) {
94
+ return canonicalValue(value, 0);
95
+ }
96
+ // ---------------------------------------------------------------- hashing
97
+ const utf8 = new TextEncoder();
98
+ export function bytesToHex(bytes) {
99
+ let out = "";
100
+ for (const b of bytes)
101
+ out += b.toString(16).padStart(2, "0");
102
+ return out;
103
+ }
104
+ export function hexToBytes(hex) {
105
+ if (!/^(?:[0-9a-f]{2})*$/.test(hex))
106
+ throw new Error("hexToBytes: expected lowercase hex of even length");
107
+ const out = new Uint8Array(hex.length / 2);
108
+ for (let i = 0; i < out.length; i++)
109
+ out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
110
+ return out;
111
+ }
112
+ export function bytesToBase64(bytes) {
113
+ let s = "";
114
+ for (const b of bytes)
115
+ s += String.fromCharCode(b);
116
+ return btoa(s);
117
+ }
118
+ export function base64ToBytes(b64) {
119
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(b64) || b64.length % 4 !== 0)
120
+ throw new Error("base64ToBytes: invalid base64");
121
+ const s = atob(b64);
122
+ const out = new Uint8Array(s.length);
123
+ for (let i = 0; i < s.length; i++)
124
+ out[i] = s.charCodeAt(i);
125
+ return out;
126
+ }
127
+ export async function sha256Hex(data) {
128
+ const bytes = typeof data === "string" ? utf8.encode(data) : data;
129
+ return bytesToHex(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)));
130
+ }
131
+ const HEX64 = /^[0-9a-f]{64}$/;
132
+ /** Hash a chain starts from, so `prevHash` of seq 1 is org-specific. */
133
+ export function auditGenesisHash(orgId) {
134
+ return sha256Hex(AUDIT_GENESIS_PREFIX + orgId);
135
+ }
136
+ /**
137
+ * Hash of one chain entry. `payload` is the value (or its already-canonical
138
+ * JSON text) that is stored verbatim next to the hash.
139
+ */
140
+ export function auditEntryHash(prevHash, payload) {
141
+ if (!HEX64.test(prevHash))
142
+ throw new Error("auditEntryHash: prevHash must be 64 lowercase hex characters");
143
+ const text = typeof payload === "string" ? payload : canonicalJson(payload);
144
+ return sha256Hex(`${AUDIT_ENTRY_DOMAIN}${prevHash}\n${text}`);
145
+ }
146
+ // ---------------------------------------------------------------- schemas
147
+ const isoUtc = z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/, "expected an ISO-8601 UTC timestamp without fractions");
148
+ const hex64 = z.string().regex(HEX64, "expected 64 lowercase hex characters");
149
+ /** Snapshot of a `VpnEvent` row, taken when it is sealed. Later changes to the row (e.g. `tunnelId` going NULL when a tunnel is deleted) never change a hashed value. */
150
+ export const auditVpnEventDataSchema = z
151
+ .object({
152
+ type: z.string().min(1).max(64),
153
+ tunnelId: z.string().min(1).max(128).nullable(),
154
+ buildId: z.string().min(1).max(128).nullable(),
155
+ agentId: z.string().min(1).max(128).nullable(),
156
+ detail: z.string().max(2000).nullable(),
157
+ })
158
+ .strict();
159
+ /**
160
+ * What a chain entry's `payload` column holds. Extended by later PRs with
161
+ * further `kind`s (`attestation`, `gap`, ...); a verifier must reject a
162
+ * kind it does not know rather than skip it.
163
+ */
164
+ export const auditEntryPayloadSchema = z.discriminatedUnion("kind", [
165
+ z
166
+ .object({
167
+ v: z.literal(1),
168
+ seq: z.number().int().min(1),
169
+ orgId: z.string().min(1),
170
+ kind: z.literal("vpn_event"),
171
+ /** `VpnEvent.id` */
172
+ ref: z.string().min(1),
173
+ /** When the source event was recorded (UTC). */
174
+ at: isoUtc,
175
+ data: auditVpnEventDataSchema,
176
+ })
177
+ .strict(),
178
+ ]);
179
+ /** What a checkpoint signature covers: "org X's chain had head hash H at seq N when this was issued". */
180
+ export const auditCheckpointStatementSchema = z
181
+ .object({
182
+ v: z.literal(1),
183
+ type: z.literal("appforge.audit.checkpoint"),
184
+ orgId: z.string().min(1),
185
+ seq: z.number().int().min(1),
186
+ hash: hex64,
187
+ issuedAt: isoUtc,
188
+ })
189
+ .strict();
190
+ /** A stored checkpoint: the statement's fields plus the signature envelope. */
191
+ export const auditCheckpointSchema = auditCheckpointStatementSchema.omit({ v: true, type: true }).extend({
192
+ keyId: z.string().min(1).max(64),
193
+ /** base64 Ed25519 signature over `auditCheckpointSigningBytes(statement)`. */
194
+ signature: z.string().min(1).max(256),
195
+ });
196
+ export function auditCheckpointStatement(input) {
197
+ return auditCheckpointStatementSchema.parse({ v: 1, type: "appforge.audit.checkpoint", ...input });
198
+ }
199
+ /** The exact bytes a checkpoint signature covers. */
200
+ export function auditCheckpointSigningBytes(statement) {
201
+ return utf8.encode(AUDIT_CHECKPOINT_DOMAIN + canonicalJson(auditCheckpointStatementSchema.parse(statement)));
202
+ }
203
+ // ---------------------------------------------------------------- timestamps
204
+ /** `VpnEvent.createdAt` is SQLite's `datetime('now')` (`YYYY-MM-DD HH:MM:SS`, UTC). The chain records ISO-8601. */
205
+ export function sqliteUtcToIso(createdAt) {
206
+ const m = /^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2}:\d{2})(?:\.\d+)?Z?$/.exec(createdAt);
207
+ if (!m)
208
+ throw new Error(`sqliteUtcToIso: unrecognised timestamp ${JSON.stringify(createdAt)}`);
209
+ return `${m[1]}T${m[2]}Z`;
210
+ }
211
+ /** An ISO-8601 UTC string with whole seconds, as the chain and checkpoints use. */
212
+ export function toIsoUtcSeconds(date) {
213
+ return `${date.toISOString().slice(0, 19)}Z`;
214
+ }
215
+ /**
216
+ * Recomputes a slice of one org's chain from the stored payloads alone.
217
+ *
218
+ * `anchor` is the `(seq, hash)` of the entry just before the slice: omit it
219
+ * for a chain that starts at seq 1 (the genesis hash is then the anchor), or
220
+ * pass the covering checkpoint's `(seq, hash)` for a chain whose oldest
221
+ * entries were pruned. Checks, per entry: contiguous seq, `prevHash` links to
222
+ * the previous hash, the payload is canonical JSON of a known schema, its
223
+ * `seq` and `orgId` match the entry and the org, and the hash recomputes.
224
+ * Reports every problem found rather than the first.
225
+ *
226
+ * Authenticity of the anchor / head is the caller's job (verify a checkpoint
227
+ * signature, or compare to a head the customer kept): this proves only that
228
+ * the entries are internally consistent with them.
229
+ */
230
+ export async function verifyAuditChain(input) {
231
+ const problems = [];
232
+ let prevSeq = input.anchor ? input.anchor.seq : 0;
233
+ let prevHash = input.anchor ? input.anchor.hash : await auditGenesisHash(input.orgId);
234
+ if (input.anchor && !HEX64.test(input.anchor.hash))
235
+ problems.push("anchor hash is not 64 lowercase hex characters");
236
+ for (const entry of input.entries) {
237
+ const at = `entry ${entry.seq}`;
238
+ if (entry.seq !== prevSeq + 1)
239
+ problems.push(`${at}: expected seq ${prevSeq + 1} (gap or reorder)`);
240
+ if (entry.prevHash !== prevHash)
241
+ problems.push(`${at}: prevHash does not link to the previous entry`);
242
+ let canonical = null;
243
+ try {
244
+ const parsed = typeof entry.payload === "string" ? JSON.parse(entry.payload) : entry.payload;
245
+ canonical = canonicalJson(parsed);
246
+ if (typeof entry.payload === "string" && entry.payload !== canonical)
247
+ problems.push(`${at}: stored payload is not canonical JSON`);
248
+ const checked = auditEntryPayloadSchema.safeParse(parsed);
249
+ if (!checked.success)
250
+ problems.push(`${at}: payload does not match a known schema`);
251
+ else {
252
+ if (checked.data.seq !== entry.seq)
253
+ problems.push(`${at}: payload seq ${checked.data.seq} does not match`);
254
+ if (checked.data.orgId !== input.orgId)
255
+ problems.push(`${at}: payload belongs to another org`);
256
+ }
257
+ }
258
+ catch {
259
+ problems.push(`${at}: payload is not valid canonical JSON`);
260
+ }
261
+ if (canonical !== null && HEX64.test(entry.prevHash)) {
262
+ const expected = await auditEntryHash(entry.prevHash, canonical);
263
+ if (expected !== entry.hash)
264
+ problems.push(`${at}: hash does not match its payload`);
265
+ }
266
+ else if (canonical !== null)
267
+ problems.push(`${at}: prevHash is not 64 lowercase hex characters`);
268
+ prevSeq = entry.seq;
269
+ prevHash = entry.hash;
270
+ }
271
+ if (problems.length)
272
+ return { ok: false, problems };
273
+ return { ok: true, headSeq: prevSeq, headHash: prevHash, entries: input.entries.length };
274
+ }
275
+ /** PKCS#8 prefix of an Ed25519 private key; the 32-byte seed follows. */
276
+ const PKCS8_ED25519_PREFIX = hexToBytes("302e020100300506032b657004220420");
277
+ /** `sha256(raw 32-byte public key)`, first 16 hex characters. A key set cannot mislabel a key because the id is recomputed from it. */
278
+ export async function ed25519KeyId(rawPublicKey) {
279
+ if (rawPublicKey.length !== 32)
280
+ throw new Error("ed25519KeyId: expected a 32-byte raw public key");
281
+ return (await sha256Hex(rawPublicKey)).slice(0, 16);
282
+ }
283
+ /** Wraps a raw 32-byte Ed25519 seed as PKCS#8, the form Workers' `importKey` takes. */
284
+ export function ed25519SeedToPkcs8(seed) {
285
+ if (seed.length !== 32)
286
+ throw new Error("ed25519SeedToPkcs8: expected a 32-byte seed");
287
+ const out = new Uint8Array(PKCS8_ED25519_PREFIX.length + 32);
288
+ out.set(PKCS8_ED25519_PREFIX, 0);
289
+ out.set(seed, PKCS8_ED25519_PREFIX.length);
290
+ return out;
291
+ }
292
+ /** An `AuditSigner` over a PKCS#8 Ed25519 private key via WebCrypto (`{name: "Ed25519"}`). */
293
+ export async function createEd25519Signer(keyId, pkcs8) {
294
+ const key = await crypto.subtle.importKey("pkcs8", pkcs8, { name: "Ed25519" }, false, ["sign"]);
295
+ return {
296
+ keyId,
297
+ async sign(message) {
298
+ return new Uint8Array(await crypto.subtle.sign({ name: "Ed25519" }, key, message));
299
+ },
300
+ };
301
+ }
302
+ /** Verifies an Ed25519 signature against a raw 32-byte public key. Never throws: a malformed key or signature is a `false`. */
303
+ export async function verifyEd25519(rawPublicKey, message, signature) {
304
+ try {
305
+ if (rawPublicKey.length !== 32 || signature.length !== 64)
306
+ return false;
307
+ const key = await crypto.subtle.importKey("raw", rawPublicKey, { name: "Ed25519" }, false, ["verify"]);
308
+ return await crypto.subtle.verify({ name: "Ed25519" }, key, signature, message);
309
+ }
310
+ catch {
311
+ return false;
312
+ }
313
+ }
314
+ /** Signs a checkpoint statement; returns the base64 signature the `AuditCheckpoint` row stores. */
315
+ export async function signAuditCheckpoint(signer, statement) {
316
+ return bytesToBase64(await signer.sign(auditCheckpointSigningBytes(statement)));
317
+ }
318
+ /** Verifies a stored checkpoint's signature against a raw public key. */
319
+ export async function verifyAuditCheckpointSignature(rawPublicKey, statement, signatureB64) {
320
+ try {
321
+ return await verifyEd25519(rawPublicKey, auditCheckpointSigningBytes(statement), base64ToBytes(signatureB64));
322
+ }
323
+ catch {
324
+ return false;
325
+ }
326
+ }
327
+ //# sourceMappingURL=attestation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation.js","sourceRoot":"","sources":["../src/attestation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=attestation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation.test.d.ts","sourceRoot":"","sources":["../src/attestation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,218 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { auditCheckpointSigningBytes, auditCheckpointStatement, auditEntryHash, auditGenesisHash, base64ToBytes, bytesToBase64, bytesToHex, canonicalJson, createEd25519Signer, ed25519KeyId, ed25519SeedToPkcs8, hexToBytes, sha256Hex, signAuditCheckpoint, sqliteUtcToIso, toIsoUtcSeconds, verifyAuditChain, verifyAuditCheckpointSignature, verifyEd25519, } from "./attestation.js";
4
+ /**
5
+ * The expected hex strings below were produced by the independent
6
+ * zero-dependency prototype verifier from the evidence spike
7
+ * (`node:crypto`, its own canonicalizer), not by this module, so a bug
8
+ * shared by the writer and reader of the chain cannot hide here.
9
+ */
10
+ // ---------------------------------------------------------------- canonical JSON
11
+ test("canonicalJson: sorted keys, no whitespace, RFC 8785 string escaping, integers only (reference vector)", () => {
12
+ const v = { b: 1, a: [true, null, "xé\n\u0001\"\\
😀"], "é": { z: 0, y: -5 }, A: 9007199254740991 };
13
+ assert.equal(canonicalJson(v), '{"A":9007199254740991,"a":[true,null,"xé\\n\\u0001\\"\\\\
😀"],"b":1,"é":{"y":-5,"z":0}}');
14
+ });
15
+ test("canonicalJson: key order is UTF-16 code unit order, so insertion order never matters", () => {
16
+ assert.equal(canonicalJson({ b: 1, a: 2 }), canonicalJson({ a: 2, b: 1 }));
17
+ // U+FF5E sorts after the surrogate pair of U+1F600 in UTF-16 code unit order (0xD83D < 0xFF5E).
18
+ assert.equal(canonicalJson({ "~": 1, "😀": 2 }), '{"😀":2,"~":1}');
19
+ assert.equal(canonicalJson({ "10": 1, "9": 2, "": 3 }), '{"":3,"10":1,"9":2}');
20
+ });
21
+ test("canonicalJson rejects everything that two implementations could hash differently", () => {
22
+ const bad = [
23
+ ["float", 1.5],
24
+ ["NaN", Number.NaN],
25
+ ["Infinity", Number.POSITIVE_INFINITY],
26
+ ["unsafe integer", 2 ** 53],
27
+ ["bigint", 1n],
28
+ ["undefined", undefined],
29
+ ["undefined member", { a: undefined }],
30
+ ["function", () => 1],
31
+ ["symbol", Symbol("x")],
32
+ ["Date", new Date(0)],
33
+ ["Map", new Map()],
34
+ ["class instance", new (class X {
35
+ })()],
36
+ ["sparse array", [1, , 3]], // eslint-disable-line no-sparse-arrays
37
+ ["lone high surrogate", "a\ud800b"],
38
+ ["lone low surrogate", "\udc00"],
39
+ ["lone surrogate in key", { "\ud800": 1 }],
40
+ ];
41
+ for (const [name, value] of bad)
42
+ assert.throws(() => canonicalJson(value), /canonicalJson/, name);
43
+ let deep = 1;
44
+ for (let i = 0; i < 40; i++)
45
+ deep = [deep];
46
+ assert.throws(() => canonicalJson(deep), /too deep/);
47
+ assert.equal(canonicalJson(Object.assign(Object.create(null), { a: 1 })), '{"a":1}');
48
+ assert.equal(canonicalJson("😀"), '"😀"'); // a well-formed surrogate pair is fine
49
+ });
50
+ // ---------------------------------------------------------------- hashing vectors
51
+ test("sha256Hex matches the FIPS 180-4 vectors, for strings and bytes", async () => {
52
+ assert.equal(await sha256Hex(""), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
53
+ assert.equal(await sha256Hex("abc"), "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
54
+ assert.equal(await sha256Hex(new TextEncoder().encode("abc")), await sha256Hex("abc"));
55
+ });
56
+ const P1 = {
57
+ v: 1,
58
+ seq: 1,
59
+ orgId: "org_acme",
60
+ kind: "vpn_event",
61
+ ref: "evt_1",
62
+ at: "2026-10-02T14:00:01Z",
63
+ data: { type: "lock_acquired", tunnelId: "tun_1", buildId: "bld_1", agentId: "agt_1", detail: null },
64
+ };
65
+ const P2 = {
66
+ ...P1,
67
+ seq: 2,
68
+ ref: "evt_2",
69
+ at: "2026-10-02T14:00:05Z",
70
+ data: { ...P1.data, type: "handshake_ok", detail: "gw 1.2.3.4:51820" },
71
+ };
72
+ const GENESIS = "28365294a3c2c4d560dbb49e213ccec2ff65a84a13f4a46bb3ae7ec87e33cc7c";
73
+ const H1 = "1ec5e5a6790c5c27d99239f412d150fc281e53c1a60f6f761736a2e41e02e9cb";
74
+ const H2 = "daf4915af8652438b3e53421b866ee5752e20f75834f4e7d05ca84dde344890e";
75
+ test("chain test vectors: genesis hash, entry 1 and entry 2 match the independent reference", async () => {
76
+ assert.equal(await auditGenesisHash("org_acme"), GENESIS);
77
+ assert.equal(canonicalJson(P1), '{"at":"2026-10-02T14:00:01Z","data":{"agentId":"agt_1","buildId":"bld_1","detail":null,"tunnelId":"tun_1","type":"lock_acquired"},"kind":"vpn_event","orgId":"org_acme","ref":"evt_1","seq":1,"v":1}');
78
+ assert.equal(await auditEntryHash(GENESIS, P1), H1);
79
+ assert.equal(await auditEntryHash(H1, P2), H2);
80
+ // The stored form (canonical text) hashes identically to the object.
81
+ assert.equal(await auditEntryHash(H1, canonicalJson(P2)), H2);
82
+ // Genesis is per org.
83
+ assert.notEqual(await auditGenesisHash("org_other"), GENESIS);
84
+ await assert.rejects(async () => auditEntryHash("not-hex", P1), /prevHash/);
85
+ });
86
+ // ---------------------------------------------------------------- chain verification
87
+ async function chain(n = 5) {
88
+ const entries = [];
89
+ let prev = GENESIS;
90
+ for (let seq = 1; seq <= n; seq++) {
91
+ const payload = canonicalJson({ ...P1, seq, ref: `evt_${seq}`, data: { ...P1.data, detail: `d${seq}` } });
92
+ const hash = await auditEntryHash(prev, payload);
93
+ entries.push({ seq, prevHash: prev, hash, payload });
94
+ prev = hash;
95
+ }
96
+ return entries;
97
+ }
98
+ test("verifyAuditChain accepts an intact chain from genesis and from an anchor (a pruned chain)", async () => {
99
+ const entries = await chain();
100
+ const full = await verifyAuditChain({ orgId: "org_acme", entries });
101
+ assert.deepEqual(full, { ok: true, headSeq: 5, headHash: entries[4].hash, entries: 5 });
102
+ const anchored = await verifyAuditChain({ orgId: "org_acme", anchor: { seq: 2, hash: entries[1].hash }, entries: entries.slice(2) });
103
+ assert.deepEqual(anchored, { ok: true, headSeq: 5, headHash: entries[4].hash, entries: 3 });
104
+ // Parsed-object payloads (a JSON bundle) verify the same way.
105
+ const asObjects = entries.map((e) => ({ ...e, payload: JSON.parse(e.payload) }));
106
+ assert.equal((await verifyAuditChain({ orgId: "org_acme", entries: asObjects })).ok, true);
107
+ assert.equal((await verifyAuditChain({ orgId: "org_acme", entries: [] })).ok, true);
108
+ });
109
+ test("verifyAuditChain catches every kind of tampering", async () => {
110
+ const good = await chain();
111
+ const clone = () => good.map((e) => ({ ...e }));
112
+ const cases = [
113
+ ["edited payload", (e) => { e[2].payload = e[2].payload.replace("d3", "dX"); }, /entry 3: hash does not match/],
114
+ ["edited payload with recomputed own hash still breaks the next link", async (e) => {
115
+ const p = e[1].payload.replace("d2", "FORGED");
116
+ e[1].payload = p;
117
+ e[1].hash = await auditEntryHash(e[1].prevHash, p);
118
+ }, /entry 3: prevHash does not link/],
119
+ ["deleted middle entry", (e) => { e.splice(2, 1); }, /entry 4: expected seq 3/],
120
+ ["reordered entries", (e) => { [e[1], e[2]] = [e[2], e[1]]; }, /expected seq/],
121
+ ["dropped first entry without an anchor", (e) => { e.shift(); }, /entry 2: expected seq 1/],
122
+ ["wrong prevHash", (e) => { e[3].prevHash = "0".repeat(64); }, /entry 4: prevHash does not link/],
123
+ ["non-canonical payload text", (e) => { e[0].payload = JSON.stringify(JSON.parse(e[0].payload), null, 1); }, /not canonical JSON/],
124
+ ["unknown kind", (e) => { e[0].payload = canonicalJson({ ...JSON.parse(e[0].payload), kind: "mystery" }); }, /known schema/],
125
+ ["extra member", (e) => { e[0].payload = canonicalJson({ ...JSON.parse(e[0].payload), extra: 1 }); }, /known schema/],
126
+ ["payload seq differs from entry seq", async (e) => {
127
+ const p = canonicalJson({ ...JSON.parse(e[0].payload), seq: 9 });
128
+ e[0].payload = p;
129
+ e[0].hash = await auditEntryHash(e[0].prevHash, p);
130
+ }, /payload seq 9/],
131
+ ["garbage payload", (e) => { e[0].payload = "{not json"; }, /not valid canonical JSON/],
132
+ ["another org's chain", () => undefined, /belongs to another org|prevHash does not link/, { orgId: "org_other" }],
133
+ ["wrong anchor", () => undefined, /prevHash does not link/, { anchor: { seq: 0, hash: "1".repeat(64) } }],
134
+ ];
135
+ for (const [name, mutate, expected, opts] of cases) {
136
+ const entries = clone();
137
+ await mutate(entries);
138
+ const res = await verifyAuditChain({ orgId: opts?.orgId ?? "org_acme", anchor: opts?.anchor, entries });
139
+ assert.equal(res.ok, false, name);
140
+ if (!res.ok)
141
+ assert.match(res.problems.join("\n"), expected, name);
142
+ }
143
+ });
144
+ // ---------------------------------------------------------------- timestamps
145
+ test("timestamp helpers", () => {
146
+ assert.equal(sqliteUtcToIso("2026-10-02 14:00:01"), "2026-10-02T14:00:01Z");
147
+ assert.equal(sqliteUtcToIso("2026-10-02T14:00:01.123Z"), "2026-10-02T14:00:01Z");
148
+ assert.throws(() => sqliteUtcToIso("yesterday"), /unrecognised/);
149
+ assert.equal(toIsoUtcSeconds(new Date("2026-10-02T14:00:01.999Z")), "2026-10-02T14:00:01Z");
150
+ });
151
+ // ---------------------------------------------------------------- Ed25519 in the Workers WebCrypto subset
152
+ /**
153
+ * Only the WebCrypto surface Cloudflare documents for Workers is used
154
+ * (`importKey` pkcs8/raw with `{name: "Ed25519"}`, `sign`, `verify`,
155
+ * `digest`), with the RFC 8032 section 7.1 vectors as the oracle. The
156
+ * private keys here are public test vectors or generated in-process: there
157
+ * is no production key in this change.
158
+ */
159
+ const RFC8032 = [
160
+ {
161
+ seed: "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
162
+ pub: "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
163
+ msg: "",
164
+ sig: "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
165
+ },
166
+ {
167
+ seed: "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
168
+ pub: "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
169
+ msg: "72",
170
+ sig: "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
171
+ },
172
+ ];
173
+ test("Ed25519 sign and verify reproduce the RFC 8032 vectors through WebCrypto", async () => {
174
+ for (const v of RFC8032) {
175
+ const signer = await createEd25519Signer("k", ed25519SeedToPkcs8(hexToBytes(v.seed)));
176
+ const sig = await signer.sign(hexToBytes(v.msg));
177
+ assert.equal(bytesToHex(sig), v.sig, "Ed25519 is deterministic: the signature must equal the RFC vector");
178
+ assert.equal(await verifyEd25519(hexToBytes(v.pub), hexToBytes(v.msg), hexToBytes(v.sig)), true);
179
+ // Any single-bit change is rejected.
180
+ const flipped = hexToBytes(v.sig);
181
+ flipped[10] = flipped[10] ^ 1;
182
+ assert.equal(await verifyEd25519(hexToBytes(v.pub), hexToBytes(v.msg), flipped), false);
183
+ assert.equal(await verifyEd25519(hexToBytes(v.pub), new Uint8Array([1, 2, 3]), hexToBytes(v.sig)), false);
184
+ }
185
+ // verify never throws on malformed input.
186
+ assert.equal(await verifyEd25519(new Uint8Array(5), new Uint8Array(), new Uint8Array(64)), false);
187
+ assert.equal(await verifyEd25519(hexToBytes(RFC8032[0].pub), new Uint8Array(), new Uint8Array(3)), false);
188
+ assert.equal(await verifyEd25519(new Uint8Array(32).fill(0xff), new Uint8Array(), new Uint8Array(64)), false);
189
+ assert.equal(await ed25519KeyId(hexToBytes(RFC8032[0].pub)), (await sha256Hex(hexToBytes(RFC8032[0].pub))).slice(0, 16));
190
+ });
191
+ test("checkpoint signatures: round trip with a generated test key, tamper, wrong key and domain separation", async () => {
192
+ // Generate the pair through WebCrypto itself and export both halves.
193
+ const pair = (await crypto.subtle.generateKey({ name: "Ed25519" }, true, ["sign", "verify"]));
194
+ const pkcs8 = new Uint8Array((await crypto.subtle.exportKey("pkcs8", pair.privateKey)));
195
+ const rawPub = new Uint8Array((await crypto.subtle.exportKey("raw", pair.publicKey)));
196
+ const keySigner = await createEd25519Signer(await ed25519KeyId(rawPub), pkcs8);
197
+ const stmt = auditCheckpointStatement({ orgId: "org_acme", seq: 7, hash: H2, issuedAt: "2026-10-02T14:03:50Z" });
198
+ const sig = await signAuditCheckpoint(keySigner, stmt);
199
+ assert.equal(base64ToBytes(sig).length, 64);
200
+ assert.equal(await verifyAuditCheckpointSignature(rawPub, stmt, sig), true);
201
+ assert.equal(bytesToBase64(base64ToBytes(sig)), sig);
202
+ // Tampered statement fields.
203
+ for (const bad of [{ seq: 8 }, { hash: H1 }, { orgId: "org_evil" }, { issuedAt: "2026-10-02T14:03:51Z" }]) {
204
+ assert.equal(await verifyAuditCheckpointSignature(rawPub, { ...stmt, ...bad }, sig), false, JSON.stringify(bad));
205
+ }
206
+ // Another key does not verify it, and neither does garbage.
207
+ assert.equal(await verifyAuditCheckpointSignature(hexToBytes(RFC8032[0].pub), stmt, sig), false);
208
+ assert.equal(await verifyAuditCheckpointSignature(rawPub, stmt, "!!not base64!!"), false);
209
+ // Domain separation: the same bytes without the checkpoint prefix are not what was signed.
210
+ const withoutDomain = new TextEncoder().encode(canonicalJson(stmt));
211
+ assert.equal(await verifyEd25519(rawPub, withoutDomain, base64ToBytes(sig)), false);
212
+ assert.match(new TextDecoder().decode(auditCheckpointSigningBytes(stmt)), /^appforge-checkpoint-v1\n\{"hash":/);
213
+ // The statement is validated before signing: no signing of malformed input.
214
+ assert.throws(() => auditCheckpointStatement({ orgId: "o", seq: 0, hash: H2, issuedAt: "2026-10-02T14:03:50Z" }));
215
+ assert.throws(() => auditCheckpointStatement({ orgId: "o", seq: 1, hash: "xyz", issuedAt: "2026-10-02T14:03:50Z" }));
216
+ assert.throws(() => auditCheckpointStatement({ orgId: "o", seq: 1, hash: H2, issuedAt: "2026-10-02 14:03:50" }));
217
+ });
218
+ //# sourceMappingURL=attestation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attestation.test.js","sourceRoot":"","sources":["../src/attestation.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { BuildVpnStatus } from "./types.js";
2
+ /**
3
+ * One-line, human-readable summary of `Build.vpn`, shared by the dashboard's
4
+ * Network panel and `appforge build show` so they never disagree.
5
+ * "Ran through VPN — Lab test gateway · connected"
6
+ * "VPN tunnel failed: <detail>"
7
+ * "No VPN"
8
+ */
9
+ export declare function describeBuildVpn(vpn: BuildVpnStatus | undefined): string;
10
+ //# sourceMappingURL=build-vpn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-vpn.d.ts","sourceRoot":"","sources":["../src/build-vpn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,SAAS,GAAG,MAAM,CAUxE"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * One-line, human-readable summary of `Build.vpn`, shared by the dashboard's
3
+ * Network panel and `appforge build show` so they never disagree.
4
+ * "Ran through VPN — Lab test gateway · connected"
5
+ * "VPN tunnel failed: <detail>"
6
+ * "No VPN"
7
+ */
8
+ export function describeBuildVpn(vpn) {
9
+ if (!vpn)
10
+ return "VPN status not available";
11
+ const name = vpn.tunnelName ?? "tunnel";
12
+ if (vpn.used) {
13
+ if (vpn.outcome === "connected")
14
+ return `Ran through VPN — ${name} · connected`;
15
+ if (vpn.outcome === "failed")
16
+ return `VPN tunnel failed: ${vpn.detail ?? "the handshake did not complete"}`;
17
+ return `VPN reserved — ${name} · no completed handshake recorded`;
18
+ }
19
+ if (vpn.outcome === "unknown")
20
+ return "VPN status unknown: no VPN events were recorded for this build";
21
+ return "No VPN";
22
+ }
23
+ //# sourceMappingURL=build-vpn.js.map