signafaide 0.0.0-stage → 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.
package/dist/core.cjs ADDED
@@ -0,0 +1,2711 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target2, all) => {
9
+ for (var name in all)
10
+ __defProp(target2, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target2, "default", { value: mod, enumerable: true }) : target2,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/core.ts
31
+ var core_exports = {};
32
+ __export(core_exports, {
33
+ AGENT_RE: () => AGENT_RE,
34
+ ANCHOR_WINDOW_MS: () => ANCHOR_WINDOW_MS,
35
+ CHECKS: () => CHECKS,
36
+ Client: () => Client,
37
+ DEFAULT_URL: () => DEFAULT_URL,
38
+ HEADER: () => HEADER,
39
+ JsonNumber: () => JsonNumber,
40
+ KeysRequired: () => KeysRequired,
41
+ MAX_DECLARED_NAME: () => MAX_DECLARED_NAME,
42
+ MAX_NODES: () => MAX_NODES,
43
+ MAX_PROOF_BYTES: () => MAX_PROOF_BYTES,
44
+ MAX_ROUND: () => MAX_ROUND,
45
+ NOT_INDEPENDENT: () => NOT_INDEPENDENT,
46
+ Report: () => Report,
47
+ SignafaideError: () => SignafaideError,
48
+ SigningKey: () => SigningKey,
49
+ TOOLS: () => TOOLS,
50
+ TOOL_PREFIX: () => TOOL_PREFIX,
51
+ TOOL_UNRECORDED_PREFIX: () => TOOL_UNRECORDED_PREFIX,
52
+ VERSION: () => VERSION,
53
+ agentName: () => agentName,
54
+ assertPortableNumbers: () => assertPortableNumbers,
55
+ auditPath: () => auditPath,
56
+ b64url: () => b64url,
57
+ b64urlDecode: () => b64urlDecode,
58
+ blockAttestations: () => blockAttestations,
59
+ canonicalBytes: () => canonicalBytes,
60
+ canonicalize: () => canonicalize,
61
+ checkContent: () => checkContent,
62
+ checkHeader: () => checkHeader,
63
+ checkRoundTime: () => checkRoundTime,
64
+ clampTime: () => clampTime,
65
+ clientHeader: () => clientHeader,
66
+ contentFromText: () => contentFromText,
67
+ contentHash: () => contentHash,
68
+ countsText: () => countsText,
69
+ coverage: () => coverage,
70
+ coverageLines: () => coverageLines,
71
+ declaredName: () => declaredName,
72
+ detectAgent: () => detectAgent,
73
+ draftErrors: () => draftErrors,
74
+ envelopeErrors: () => envelopeErrors,
75
+ exportStatement: () => exportStatement,
76
+ fetchJson: () => fetchJson,
77
+ hashJson: () => hashJson,
78
+ isLoopbackUrl: () => isLoopbackUrl,
79
+ keyEntryErrors: () => keyEntryErrors,
80
+ keyId: () => keyId,
81
+ leafHash: () => leafHash,
82
+ loadDrandVerifier: () => loadDrandVerifier,
83
+ makeDraft: () => makeDraft,
84
+ makeRecord: () => makeRecord,
85
+ manifestEntry: () => manifestEntry,
86
+ manifestErrors: () => manifestErrors,
87
+ merkleRoot: () => merkleRoot,
88
+ newSalt: () => newSalt,
89
+ now: () => now,
90
+ otsStatus: () => otsStatus,
91
+ parseAgentKeys: () => parseAgentKeys,
92
+ parseHeadFile: () => parseHeadFile,
93
+ parseJson: () => parseJson,
94
+ parseOts: () => parseOts,
95
+ parseServiceKeys: () => parseServiceKeys,
96
+ receiptErrors: () => receiptErrors,
97
+ recordErrors: () => recordErrors,
98
+ recordHash: () => recordHash,
99
+ recordsHash: () => recordsHash,
100
+ registration: () => registration,
101
+ resultLine: () => resultLine,
102
+ rootsFromVector: () => rootsFromVector,
103
+ scopeText: () => scopeText,
104
+ serviceKeysHash: () => serviceKeysHash,
105
+ sha256Hex: () => sha256Hex,
106
+ signDraft: () => signDraft,
107
+ signedMessage: () => signedMessage,
108
+ target: () => target,
109
+ telemetryEnabled: () => telemetryEnabled,
110
+ timestampHeights: () => timestampHeights,
111
+ toolNote: () => toolNote,
112
+ toolNoteName: () => toolNoteName,
113
+ validProjectId: () => validProjectId,
114
+ validTime: () => validTime,
115
+ verifyInclusion: () => verifyInclusion,
116
+ verifyLocalState: () => verifyLocalState,
117
+ verifyPack: () => verifyPack,
118
+ verifySig: () => verifySig,
119
+ wireRecord: () => wireRecord,
120
+ withWallTime: () => withWallTime
121
+ });
122
+ module.exports = __toCommonJS(core_exports);
123
+
124
+ // src/jcs.ts
125
+ var LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
126
+ var encoder = new TextEncoder();
127
+ var JsonNumber = class {
128
+ constructor(text, value, unsafeInteger) {
129
+ this.text = text;
130
+ this.value = value;
131
+ this.unsafeInteger = unsafeInteger;
132
+ }
133
+ text;
134
+ value;
135
+ unsafeInteger;
136
+ toJSON() {
137
+ return this.value;
138
+ }
139
+ };
140
+ function canonicalize(value) {
141
+ if (value === null) return "null";
142
+ if (value instanceof JsonNumber) {
143
+ if (value.unsafeInteger) throw new RangeError(`unsafe_integer: integer ${value.text} is outside \xB1(2^53-1) and cannot be canonicalized exactly`);
144
+ if (!Number.isFinite(value.value)) throw new RangeError(`number ${value.text} is not a finite IEEE 754 double`);
145
+ return JSON.stringify(value.value);
146
+ }
147
+ switch (typeof value) {
148
+ case "boolean":
149
+ return value ? "true" : "false";
150
+ case "number":
151
+ if (!Number.isFinite(value)) throw new RangeError("JCS does not allow NaN or Infinity");
152
+ return JSON.stringify(value);
153
+ case "string":
154
+ if (LONE_SURROGATE.test(value)) throw new RangeError("JCS strings must not contain lone surrogates");
155
+ return JSON.stringify(value);
156
+ case "object": {
157
+ if (Array.isArray(value)) return "[" + value.map(canonicalize).join(",") + "]";
158
+ const proto = Object.getPrototypeOf(value);
159
+ if (proto !== Object.prototype && proto !== null) {
160
+ throw new TypeError(`JCS cannot encode ${value.constructor?.name ?? "this object"}`);
161
+ }
162
+ const obj = value;
163
+ const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort();
164
+ return "{" + keys.map((k) => canonicalize(k) + ":" + canonicalize(obj[k])).join(",") + "}";
165
+ }
166
+ default:
167
+ throw new TypeError(`JCS cannot encode ${typeof value}`);
168
+ }
169
+ }
170
+ function canonicalBytes(value) {
171
+ return encoder.encode(canonicalize(value));
172
+ }
173
+ var isUnsafeInteger = (x) => Number.isInteger(x) && Math.abs(x) > Number.MAX_SAFE_INTEGER && Math.abs(x) < 1e21;
174
+ function assertPortableNumbers(value) {
175
+ if (typeof value === "number" && isUnsafeInteger(value)) throw new RangeError(`unsafe_integer: ${value} is beyond +-(2^53-1); send it as a string`);
176
+ if (value && typeof value === "object") for (const v of Object.values(value)) assertPortableNumbers(v);
177
+ }
178
+ function parseJson(text) {
179
+ let i = 0;
180
+ const fail = (what) => {
181
+ throw new SyntaxError(`JSON: ${what} at position ${i}`);
182
+ };
183
+ const ws = () => {
184
+ while (i < text.length && " \n\r".includes(text[i])) i++;
185
+ };
186
+ const value = (depth) => {
187
+ if (depth > 512) fail("nested too deeply");
188
+ ws();
189
+ const c = text[i];
190
+ if (c === "{") {
191
+ i++;
192
+ const out2 = {};
193
+ ws();
194
+ if (text[i] === "}") {
195
+ i++;
196
+ return out2;
197
+ }
198
+ for (; ; ) {
199
+ ws();
200
+ if (text[i] !== '"') fail("expected a string key");
201
+ const k = str();
202
+ ws();
203
+ if (text[i++] !== ":") fail("expected ':'");
204
+ Object.defineProperty(out2, k, { value: value(depth + 1), enumerable: true, writable: true, configurable: true });
205
+ ws();
206
+ if (text[i] === ",") i++;
207
+ else if (text[i] === "}") {
208
+ i++;
209
+ return out2;
210
+ } else fail("expected ',' or '}'");
211
+ }
212
+ }
213
+ if (c === "[") {
214
+ i++;
215
+ const out2 = [];
216
+ ws();
217
+ if (text[i] === "]") {
218
+ i++;
219
+ return out2;
220
+ }
221
+ for (; ; ) {
222
+ out2.push(value(depth + 1));
223
+ ws();
224
+ if (text[i] === ",") i++;
225
+ else if (text[i] === "]") {
226
+ i++;
227
+ return out2;
228
+ } else fail("expected ',' or ']'");
229
+ }
230
+ }
231
+ if (c === '"') return str();
232
+ for (const [lit2, v2] of [["true", true], ["false", false], ["null", null]]) {
233
+ if (text.startsWith(lit2, i)) {
234
+ i += lit2.length;
235
+ return v2;
236
+ }
237
+ }
238
+ const m = /^-?(?:0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/.exec(text.slice(i, i + 400));
239
+ if (!m) fail("unexpected character");
240
+ i += m[0].length;
241
+ const lit = m[0];
242
+ const v = Number(lit);
243
+ if (!m[1] && !m[2]) return Math.abs(v) <= Number.MAX_SAFE_INTEGER ? v : new JsonNumber(lit, v, true);
244
+ return Number.isInteger(v) ? new JsonNumber(lit, v, false) : v;
245
+ };
246
+ const str = () => {
247
+ const start = i;
248
+ i++;
249
+ let raw = false;
250
+ while (i < text.length && text[i] !== '"') {
251
+ if (text[i] === "\\") {
252
+ raw = true;
253
+ i++;
254
+ } else if (text.charCodeAt(i) < 32) fail("control character in a string");
255
+ i++;
256
+ }
257
+ if (i >= text.length) fail("unterminated string");
258
+ i++;
259
+ const s = text.slice(start, i);
260
+ return raw ? JSON.parse(s) : s.slice(1, -1);
261
+ };
262
+ const out = value(0);
263
+ ws();
264
+ if (i !== text.length) fail("trailing text");
265
+ return out;
266
+ }
267
+
268
+ // node_modules/@noble/ed25519/index.js
269
+ var ed25519_CURVE = {
270
+ p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
271
+ n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
272
+ h: 8n,
273
+ a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
274
+ d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
275
+ Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
276
+ Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n
277
+ };
278
+ var { p: P, n: N, Gx, Gy, a: _a, d: _d } = ed25519_CURVE;
279
+ var h = 8n;
280
+ var L = 32;
281
+ var L2 = 64;
282
+ var err = (m = "") => {
283
+ throw new Error(m);
284
+ };
285
+ var isBig = (n) => typeof n === "bigint";
286
+ var isStr = (s) => typeof s === "string";
287
+ var isBytes = (a) => a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
288
+ var abytes = (a, l) => !isBytes(a) || typeof l === "number" && l > 0 && a.length !== l ? err("Uint8Array expected") : a;
289
+ var u8n = (len) => new Uint8Array(len);
290
+ var u8fr = (buf) => Uint8Array.from(buf);
291
+ var padh = (n, pad) => n.toString(16).padStart(pad, "0");
292
+ var bytesToHex = (b) => Array.from(abytes(b)).map((e) => padh(e, 2)).join("");
293
+ var C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
294
+ var _ch = (ch) => {
295
+ if (ch >= C._0 && ch <= C._9)
296
+ return ch - C._0;
297
+ if (ch >= C.A && ch <= C.F)
298
+ return ch - (C.A - 10);
299
+ if (ch >= C.a && ch <= C.f)
300
+ return ch - (C.a - 10);
301
+ return;
302
+ };
303
+ var hexToBytes = (hex2) => {
304
+ const e = "hex invalid";
305
+ if (!isStr(hex2))
306
+ return err(e);
307
+ const hl = hex2.length;
308
+ const al = hl / 2;
309
+ if (hl % 2)
310
+ return err(e);
311
+ const array = u8n(al);
312
+ for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
313
+ const n1 = _ch(hex2.charCodeAt(hi));
314
+ const n2 = _ch(hex2.charCodeAt(hi + 1));
315
+ if (n1 === void 0 || n2 === void 0)
316
+ return err(e);
317
+ array[ai] = n1 * 16 + n2;
318
+ }
319
+ return array;
320
+ };
321
+ var toU8 = (a, len) => abytes(isStr(a) ? hexToBytes(a) : u8fr(abytes(a)), len);
322
+ var cr = () => globalThis?.crypto;
323
+ var subtle = () => cr()?.subtle ?? err("crypto.subtle must be defined");
324
+ var concatBytes = (...arrs) => {
325
+ const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0));
326
+ let pad = 0;
327
+ arrs.forEach((a) => {
328
+ r.set(a, pad);
329
+ pad += a.length;
330
+ });
331
+ return r;
332
+ };
333
+ var randomBytes = (len = L) => {
334
+ const c = cr();
335
+ return c.getRandomValues(u8n(len));
336
+ };
337
+ var big = BigInt;
338
+ var arange = (n, min, max, msg = "bad number: out of range") => isBig(n) && min <= n && n < max ? n : err(msg);
339
+ var M = (a, b = P) => {
340
+ const r = a % b;
341
+ return r >= 0n ? r : b + r;
342
+ };
343
+ var modN = (a) => M(a, N);
344
+ var invert = (num, md) => {
345
+ if (num === 0n || md <= 0n)
346
+ err("no inverse n=" + num + " mod=" + md);
347
+ let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
348
+ while (a !== 0n) {
349
+ const q = b / a, r = b % a;
350
+ const m = x - u * q, n = y - v * q;
351
+ b = a, a = r, x = u, y = v, u = m, v = n;
352
+ }
353
+ return b === 1n ? M(x, md) : err("no inverse");
354
+ };
355
+ var callHash = (name) => {
356
+ const fn = etc[name];
357
+ if (typeof fn !== "function")
358
+ err("hashes." + name + " not set");
359
+ return fn;
360
+ };
361
+ var apoint = (p) => p instanceof Point ? p : err("Point expected");
362
+ var B256 = 2n ** 256n;
363
+ var Point = class _Point {
364
+ static BASE;
365
+ static ZERO;
366
+ ex;
367
+ ey;
368
+ ez;
369
+ et;
370
+ constructor(ex, ey, ez, et) {
371
+ const max = B256;
372
+ this.ex = arange(ex, 0n, max);
373
+ this.ey = arange(ey, 0n, max);
374
+ this.ez = arange(ez, 1n, max);
375
+ this.et = arange(et, 0n, max);
376
+ Object.freeze(this);
377
+ }
378
+ static fromAffine(p) {
379
+ return new _Point(p.x, p.y, 1n, M(p.x * p.y));
380
+ }
381
+ /** RFC8032 5.1.3: Uint8Array to Point. */
382
+ static fromBytes(hex2, zip215 = false) {
383
+ const d = _d;
384
+ const normed = u8fr(abytes(hex2, L));
385
+ const lastByte = hex2[31];
386
+ normed[31] = lastByte & ~128;
387
+ const y = bytesToNumLE(normed);
388
+ const max = zip215 ? B256 : P;
389
+ arange(y, 0n, max);
390
+ const y2 = M(y * y);
391
+ const u = M(y2 - 1n);
392
+ const v = M(d * y2 + 1n);
393
+ let { isValid, value: x } = uvRatio(u, v);
394
+ if (!isValid)
395
+ err("bad point: y not sqrt");
396
+ const isXOdd = (x & 1n) === 1n;
397
+ const isLastByteOdd = (lastByte & 128) !== 0;
398
+ if (!zip215 && x === 0n && isLastByteOdd)
399
+ err("bad point: x==0, isLastByteOdd");
400
+ if (isLastByteOdd !== isXOdd)
401
+ x = M(-x);
402
+ return new _Point(x, y, 1n, M(x * y));
403
+ }
404
+ /** Checks if the point is valid and on-curve. */
405
+ assertValidity() {
406
+ const a = _a;
407
+ const d = _d;
408
+ const p = this;
409
+ if (p.is0())
410
+ throw new Error("bad point: ZERO");
411
+ const { ex: X, ey: Y, ez: Z, et: T } = p;
412
+ const X2 = M(X * X);
413
+ const Y2 = M(Y * Y);
414
+ const Z2 = M(Z * Z);
415
+ const Z4 = M(Z2 * Z2);
416
+ const aX2 = M(X2 * a);
417
+ const left = M(Z2 * M(aX2 + Y2));
418
+ const right = M(Z4 + M(d * M(X2 * Y2)));
419
+ if (left !== right)
420
+ throw new Error("bad point: equation left != right (1)");
421
+ const XY = M(X * Y);
422
+ const ZT = M(Z * T);
423
+ if (XY !== ZT)
424
+ throw new Error("bad point: equation left != right (2)");
425
+ return this;
426
+ }
427
+ /** Equality check: compare points P&Q. */
428
+ equals(other) {
429
+ const { ex: X1, ey: Y1, ez: Z1 } = this;
430
+ const { ex: X2, ey: Y2, ez: Z2 } = apoint(other);
431
+ const X1Z2 = M(X1 * Z2);
432
+ const X2Z1 = M(X2 * Z1);
433
+ const Y1Z2 = M(Y1 * Z2);
434
+ const Y2Z1 = M(Y2 * Z1);
435
+ return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
436
+ }
437
+ is0() {
438
+ return this.equals(I);
439
+ }
440
+ /** Flip point over y coordinate. */
441
+ negate() {
442
+ return new _Point(M(-this.ex), this.ey, this.ez, M(-this.et));
443
+ }
444
+ /** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */
445
+ double() {
446
+ const { ex: X1, ey: Y1, ez: Z1 } = this;
447
+ const a = _a;
448
+ const A = M(X1 * X1);
449
+ const B = M(Y1 * Y1);
450
+ const C2 = M(2n * M(Z1 * Z1));
451
+ const D = M(a * A);
452
+ const x1y1 = X1 + Y1;
453
+ const E = M(M(x1y1 * x1y1) - A - B);
454
+ const G2 = D + B;
455
+ const F = G2 - C2;
456
+ const H = D - B;
457
+ const X3 = M(E * F);
458
+ const Y3 = M(G2 * H);
459
+ const T3 = M(E * H);
460
+ const Z3 = M(F * G2);
461
+ return new _Point(X3, Y3, Z3, T3);
462
+ }
463
+ /** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */
464
+ add(other) {
465
+ const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
466
+ const { ex: X2, ey: Y2, ez: Z2, et: T2 } = apoint(other);
467
+ const a = _a;
468
+ const d = _d;
469
+ const A = M(X1 * X2);
470
+ const B = M(Y1 * Y2);
471
+ const C2 = M(T1 * d * T2);
472
+ const D = M(Z1 * Z2);
473
+ const E = M((X1 + Y1) * (X2 + Y2) - A - B);
474
+ const F = M(D - C2);
475
+ const G2 = M(D + C2);
476
+ const H = M(B - a * A);
477
+ const X3 = M(E * F);
478
+ const Y3 = M(G2 * H);
479
+ const T3 = M(E * H);
480
+ const Z3 = M(F * G2);
481
+ return new _Point(X3, Y3, Z3, T3);
482
+ }
483
+ /**
484
+ * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.
485
+ * Uses {@link wNAF} for base point.
486
+ * Uses fake point to mitigate side-channel leakage.
487
+ * @param n scalar by which point is multiplied
488
+ * @param safe safe mode guards against timing attacks; unsafe mode is faster
489
+ */
490
+ multiply(n, safe = true) {
491
+ if (!safe && (n === 0n || this.is0()))
492
+ return I;
493
+ arange(n, 1n, N);
494
+ if (n === 1n)
495
+ return this;
496
+ if (this.equals(G))
497
+ return wNAF(n).p;
498
+ let p = I;
499
+ let f = G;
500
+ for (let d = this; n > 0n; d = d.double(), n >>= 1n) {
501
+ if (n & 1n)
502
+ p = p.add(d);
503
+ else if (safe)
504
+ f = f.add(d);
505
+ }
506
+ return p;
507
+ }
508
+ /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
509
+ toAffine() {
510
+ const { ex: x, ey: y, ez: z } = this;
511
+ if (this.equals(I))
512
+ return { x: 0n, y: 1n };
513
+ const iz = invert(z, P);
514
+ if (M(z * iz) !== 1n)
515
+ err("invalid inverse");
516
+ return { x: M(x * iz), y: M(y * iz) };
517
+ }
518
+ toBytes() {
519
+ const { x, y } = this.assertValidity().toAffine();
520
+ const b = numTo32bLE(y);
521
+ b[31] |= x & 1n ? 128 : 0;
522
+ return b;
523
+ }
524
+ toHex() {
525
+ return bytesToHex(this.toBytes());
526
+ }
527
+ // encode to hex string
528
+ clearCofactor() {
529
+ return this.multiply(big(h), false);
530
+ }
531
+ isSmallOrder() {
532
+ return this.clearCofactor().is0();
533
+ }
534
+ isTorsionFree() {
535
+ let p = this.multiply(N / 2n, false).double();
536
+ if (N % 2n)
537
+ p = p.add(this);
538
+ return p.is0();
539
+ }
540
+ static fromHex(hex2, zip215) {
541
+ return _Point.fromBytes(toU8(hex2), zip215);
542
+ }
543
+ get x() {
544
+ return this.toAffine().x;
545
+ }
546
+ get y() {
547
+ return this.toAffine().y;
548
+ }
549
+ toRawBytes() {
550
+ return this.toBytes();
551
+ }
552
+ };
553
+ var G = new Point(Gx, Gy, 1n, M(Gx * Gy));
554
+ var I = new Point(0n, 1n, 1n, 0n);
555
+ Point.BASE = G;
556
+ Point.ZERO = I;
557
+ var numTo32bLE = (num) => hexToBytes(padh(arange(num, 0n, B256), L2)).reverse();
558
+ var bytesToNumLE = (b) => big("0x" + bytesToHex(u8fr(abytes(b)).reverse()));
559
+ var pow2 = (x, power) => {
560
+ let r = x;
561
+ while (power-- > 0n) {
562
+ r *= r;
563
+ r %= P;
564
+ }
565
+ return r;
566
+ };
567
+ var pow_2_252_3 = (x) => {
568
+ const x2 = x * x % P;
569
+ const b2 = x2 * x % P;
570
+ const b4 = pow2(b2, 2n) * b2 % P;
571
+ const b5 = pow2(b4, 1n) * x % P;
572
+ const b10 = pow2(b5, 5n) * b5 % P;
573
+ const b20 = pow2(b10, 10n) * b10 % P;
574
+ const b40 = pow2(b20, 20n) * b20 % P;
575
+ const b80 = pow2(b40, 40n) * b40 % P;
576
+ const b160 = pow2(b80, 80n) * b80 % P;
577
+ const b240 = pow2(b160, 80n) * b80 % P;
578
+ const b250 = pow2(b240, 10n) * b10 % P;
579
+ const pow_p_5_8 = pow2(b250, 2n) * x % P;
580
+ return { pow_p_5_8, b2 };
581
+ };
582
+ var RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n;
583
+ var uvRatio = (u, v) => {
584
+ const v3 = M(v * v * v);
585
+ const v7 = M(v3 * v3 * v);
586
+ const pow = pow_2_252_3(u * v7).pow_p_5_8;
587
+ let x = M(u * v3 * pow);
588
+ const vx2 = M(v * x * x);
589
+ const root1 = x;
590
+ const root2 = M(x * RM1);
591
+ const useRoot1 = vx2 === u;
592
+ const useRoot2 = vx2 === M(-u);
593
+ const noRoot = vx2 === M(-u * RM1);
594
+ if (useRoot1)
595
+ x = root1;
596
+ if (useRoot2 || noRoot)
597
+ x = root2;
598
+ if ((M(x) & 1n) === 1n)
599
+ x = M(-x);
600
+ return { isValid: useRoot1 || useRoot2, value: x };
601
+ };
602
+ var modL_LE = (hash) => modN(bytesToNumLE(hash));
603
+ var sha512s = (...m) => callHash("sha512Sync")(...m);
604
+ var hash2extK = (hashed) => {
605
+ const head = hashed.slice(0, L);
606
+ head[0] &= 248;
607
+ head[31] &= 127;
608
+ head[31] |= 64;
609
+ const prefix = hashed.slice(L, L2);
610
+ const scalar = modL_LE(head);
611
+ const point = G.multiply(scalar);
612
+ const pointBytes = point.toBytes();
613
+ return { head, prefix, scalar, point, pointBytes };
614
+ };
615
+ var getExtendedPublicKey = (priv) => hash2extK(sha512s(toU8(priv, L)));
616
+ var getPublicKey = (priv) => getExtendedPublicKey(priv).pointBytes;
617
+ var hashFinishS = (res) => res.finish(sha512s(res.hashable));
618
+ var _sign = (e, rBytes, msg) => {
619
+ const { pointBytes: P2, scalar: s } = e;
620
+ const r = modL_LE(rBytes);
621
+ const R = G.multiply(r).toBytes();
622
+ const hashable = concatBytes(R, P2, msg);
623
+ const finish = (hashed) => {
624
+ const S = modN(r + modL_LE(hashed) * s);
625
+ return abytes(concatBytes(R, numTo32bLE(S)), L2);
626
+ };
627
+ return { hashable, finish };
628
+ };
629
+ var sign = (msg, privKey) => {
630
+ const m = toU8(msg);
631
+ const e = getExtendedPublicKey(privKey);
632
+ const rBytes = sha512s(e.prefix, m);
633
+ return hashFinishS(_sign(e, rBytes, m));
634
+ };
635
+ var veriOpts = { zip215: true };
636
+ var _verify = (sig, msg, pub, opts = veriOpts) => {
637
+ sig = toU8(sig, L2);
638
+ msg = toU8(msg);
639
+ pub = toU8(pub, L);
640
+ const { zip215 } = opts;
641
+ let A;
642
+ let R;
643
+ let s;
644
+ let SB;
645
+ let hashable = Uint8Array.of();
646
+ try {
647
+ A = Point.fromHex(pub, zip215);
648
+ R = Point.fromHex(sig.slice(0, L), zip215);
649
+ s = bytesToNumLE(sig.slice(L, L2));
650
+ SB = G.multiply(s, false);
651
+ hashable = concatBytes(R.toBytes(), A.toBytes(), msg);
652
+ } catch (error) {
653
+ }
654
+ const finish = (hashed) => {
655
+ if (SB == null)
656
+ return false;
657
+ if (!zip215 && A.isSmallOrder())
658
+ return false;
659
+ const k = modL_LE(hashed);
660
+ const RkA = R.add(A.multiply(k, false));
661
+ return RkA.add(SB.negate()).clearCofactor().is0();
662
+ };
663
+ return { hashable, finish };
664
+ };
665
+ var verify = (s, m, p, opts = veriOpts) => hashFinishS(_verify(s, m, p, opts));
666
+ var etc = {
667
+ sha512Async: async (...messages) => {
668
+ const s = subtle();
669
+ const m = concatBytes(...messages);
670
+ return u8n(await s.digest("SHA-512", m.buffer));
671
+ },
672
+ sha512Sync: void 0,
673
+ bytesToHex,
674
+ hexToBytes,
675
+ concatBytes,
676
+ mod: M,
677
+ invert,
678
+ randomBytes
679
+ };
680
+ var W = 8;
681
+ var scalarBits = 256;
682
+ var pwindows = Math.ceil(scalarBits / W) + 1;
683
+ var pwindowSize = 2 ** (W - 1);
684
+ var precompute = () => {
685
+ const points = [];
686
+ let p = G;
687
+ let b = p;
688
+ for (let w = 0; w < pwindows; w++) {
689
+ b = p;
690
+ points.push(b);
691
+ for (let i = 1; i < pwindowSize; i++) {
692
+ b = b.add(p);
693
+ points.push(b);
694
+ }
695
+ p = b.double();
696
+ }
697
+ return points;
698
+ };
699
+ var Gpows = void 0;
700
+ var ctneg = (cnd, p) => {
701
+ const n = p.negate();
702
+ return cnd ? n : p;
703
+ };
704
+ var wNAF = (n) => {
705
+ const comp = Gpows || (Gpows = precompute());
706
+ let p = I;
707
+ let f = G;
708
+ const pow_2_w = 2 ** W;
709
+ const maxNum = pow_2_w;
710
+ const mask = big(pow_2_w - 1);
711
+ const shiftBy = big(W);
712
+ for (let w = 0; w < pwindows; w++) {
713
+ let wbits = Number(n & mask);
714
+ n >>= shiftBy;
715
+ if (wbits > pwindowSize) {
716
+ wbits -= maxNum;
717
+ n += 1n;
718
+ }
719
+ const off = w * pwindowSize;
720
+ const offF = off;
721
+ const offP = off + Math.abs(wbits) - 1;
722
+ const isEven = w % 2 !== 0;
723
+ const isNeg = wbits < 0;
724
+ if (wbits === 0) {
725
+ f = f.add(ctneg(isEven, comp[offF]));
726
+ } else {
727
+ p = p.add(ctneg(isNeg, comp[offP]));
728
+ }
729
+ }
730
+ return { p, f };
731
+ };
732
+
733
+ // src/crypto.ts
734
+ var import_sha2 = require("@noble/hashes/sha2");
735
+ var import_utils = require("@noble/hashes/utils");
736
+ etc.sha512Sync = (...m) => (0, import_sha2.sha512)((0, import_utils.concatBytes)(...m));
737
+ var DOMAINS = ["record", "batch", "head", "anchor", "register", "rotate", "agent_key", "export", "draft"];
738
+ var HEX64 = /^[0-9a-f]{64}$/;
739
+ var SALT_RE = /^[0-9a-f]{32}$/;
740
+ var encoder2 = new TextEncoder();
741
+ var nativeSha256 = null;
742
+ var nativeVerify = null;
743
+ var digest = (data) => nativeSha256 ? nativeSha256(data) : (0, import_sha2.sha256)(data);
744
+ function sha256Hex(data) {
745
+ return (0, import_utils.bytesToHex)(digest(data));
746
+ }
747
+ function hashJson(value) {
748
+ return sha256Hex(canonicalBytes(value));
749
+ }
750
+ function newSalt() {
751
+ return (0, import_utils.bytesToHex)(randomBytes2(16));
752
+ }
753
+ function contentHash(content, salt) {
754
+ if (!content || typeof content !== "object" || Array.isArray(content)) throw new Error("content must be a JSON object");
755
+ if (typeof salt !== "string" || !SALT_RE.test(salt)) throw new Error("salt must be 32 lowercase hex characters (16 bytes)");
756
+ return sha256Hex((0, import_utils.concatBytes)((0, import_utils.hexToBytes)(salt), canonicalBytes(content)));
757
+ }
758
+ function checkContent(content, salt, expected) {
759
+ if (content === void 0 && salt === void 0) return null;
760
+ if (salt === void 0) throw new Error("content is present without its salt");
761
+ if (content === void 0) throw new Error("salt is present without content");
762
+ const h2 = contentHash(content, salt);
763
+ return h2 === expected ? null : h2;
764
+ }
765
+ var B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
766
+ function b64url(data) {
767
+ let out = "";
768
+ for (let i = 0; i < data.length; i += 3) {
769
+ const n = data[i] << 16 | (data[i + 1] ?? 0) << 8 | (data[i + 2] ?? 0);
770
+ const chars = Math.min(4, Math.ceil((data.length - i) * 8 / 6));
771
+ for (let j = 0; j < chars; j++) out += B64[n >> 18 - 6 * j & 63];
772
+ }
773
+ return out;
774
+ }
775
+ function b64urlDecode(s) {
776
+ if (typeof s !== "string" || !/^[A-Za-z0-9_-]*$/.test(s) || s.length % 4 === 1) {
777
+ throw new Error("not base64url without padding");
778
+ }
779
+ const out = new Uint8Array(Math.floor(s.length * 6 / 8));
780
+ let bits = 0;
781
+ let acc = 0;
782
+ let o = 0;
783
+ for (const c of s) {
784
+ acc = acc << 6 | B64.indexOf(c);
785
+ bits += 6;
786
+ if (bits >= 8) {
787
+ bits -= 8;
788
+ out[o++] = acc >> bits & 255;
789
+ }
790
+ }
791
+ return out;
792
+ }
793
+ function keyId(publicKey) {
794
+ return sha256Hex(publicKey).slice(0, 16);
795
+ }
796
+ function signedMessage(domain, hashHex) {
797
+ if (!DOMAINS.includes(domain)) throw new Error(`unknown signature domain ${domain}`);
798
+ if (!HEX64.test(hashHex ?? "")) throw new Error("hash must be 64 lowercase hex characters");
799
+ return (0, import_utils.concatBytes)(encoder2.encode(`signafaide/v1/${domain}
800
+ `), (0, import_utils.hexToBytes)(hashHex));
801
+ }
802
+ var SigningKey = class _SigningKey {
803
+ publicKey;
804
+ keyId;
805
+ #seed;
806
+ constructor(seed) {
807
+ if (seed.length !== 32) throw new Error("an Ed25519 seed is 32 bytes");
808
+ this.#seed = seed;
809
+ this.publicKey = getPublicKey(seed);
810
+ this.keyId = keyId(this.publicKey);
811
+ }
812
+ static generate(random = randomBytes2) {
813
+ return new _SigningKey(random(32));
814
+ }
815
+ seed() {
816
+ return this.#seed;
817
+ }
818
+ /** base64url Ed25519 signature over `signafaide/v1/<domain>\n` + raw hash. */
819
+ sign(domain, hashHex) {
820
+ return b64url(sign(signedMessage(domain, hashHex), this.#seed));
821
+ }
822
+ };
823
+ function verifySig(publicKeyB64, domain, hashHex, sigB64) {
824
+ try {
825
+ const pk = b64urlDecode(publicKeyB64);
826
+ const sig = b64urlDecode(sigB64);
827
+ if (pk.length !== 32 || sig.length !== 64) return false;
828
+ if (nativeVerify) return nativeVerify(pk, signedMessage(domain, hashHex), sig);
829
+ return verify(sig, signedMessage(domain, hashHex), pk, { zip215: false });
830
+ } catch {
831
+ return false;
832
+ }
833
+ }
834
+ function randomBytes2(n) {
835
+ const c = globalThis.crypto;
836
+ if (!c?.getRandomValues) throw new Error("no secure random source: pass one to SigningKey.generate");
837
+ return c.getRandomValues(new Uint8Array(n));
838
+ }
839
+ function leafHash(hashHex) {
840
+ return digest((0, import_utils.concatBytes)(new Uint8Array([0]), (0, import_utils.hexToBytes)(hashHex)));
841
+ }
842
+ function node(left, right) {
843
+ return digest((0, import_utils.concatBytes)(new Uint8Array([1]), left, right));
844
+ }
845
+ function split(n) {
846
+ let k = 1;
847
+ while (k * 2 < n) k *= 2;
848
+ return k;
849
+ }
850
+ function root(leaves) {
851
+ if (leaves.length === 1) return leaves[0];
852
+ const k = split(leaves.length);
853
+ return node(root(leaves.slice(0, k)), root(leaves.slice(k)));
854
+ }
855
+ function merkleRoot(hashes) {
856
+ if (!hashes.length) throw new Error("a Merkle tree needs at least one leaf");
857
+ return (0, import_utils.bytesToHex)(root(hashes.map(leafHash)));
858
+ }
859
+ function auditPath(hashes, index) {
860
+ if (index < 0 || index >= hashes.length) throw new Error("index out of range");
861
+ const leaves = hashes.map(leafHash);
862
+ const path = [];
863
+ const walk = (lo, hi) => {
864
+ if (hi - lo === 1) return;
865
+ const k = split(hi - lo);
866
+ if (index < lo + k) {
867
+ walk(lo, lo + k);
868
+ path.push((0, import_utils.bytesToHex)(root(leaves.slice(lo + k, hi))));
869
+ } else {
870
+ walk(lo + k, hi);
871
+ path.push((0, import_utils.bytesToHex)(root(leaves.slice(lo, lo + k))));
872
+ }
873
+ };
874
+ walk(0, leaves.length);
875
+ return path;
876
+ }
877
+ function verifyInclusion(hashHex, index, size, path, rootHex) {
878
+ try {
879
+ if (!Number.isInteger(index) || !Number.isInteger(size) || !Array.isArray(path)) return false;
880
+ let fn = index;
881
+ let sn = size - 1;
882
+ if (fn < 0 || fn > sn) return false;
883
+ let r = leafHash(hashHex);
884
+ for (const pHex of path) {
885
+ if (typeof pHex !== "string" || !HEX64.test(pHex) || sn === 0) return false;
886
+ const p = (0, import_utils.hexToBytes)(pHex);
887
+ if (fn % 2 === 1 || fn === sn) {
888
+ r = node(p, r);
889
+ while (fn % 2 === 0 && fn !== 0) {
890
+ fn = Math.floor(fn / 2);
891
+ sn = Math.floor(sn / 2);
892
+ }
893
+ } else {
894
+ r = node(r, p);
895
+ }
896
+ fn = Math.floor(fn / 2);
897
+ sn = Math.floor(sn / 2);
898
+ }
899
+ return sn === 0 && (0, import_utils.bytesToHex)(r) === rootHex;
900
+ } catch {
901
+ return false;
902
+ }
903
+ }
904
+
905
+ // src/records.ts
906
+ var KINDS = ["decision", "outcome", "note"];
907
+ var ENVELOPE_FIELDS = ["v", "kind", "project", "env", "agent", "seq", "prev", "time", "name", "content_hash", "ref"];
908
+ var DRAFT_FIELDS = ["v", "kind", "agent", "seq", "prev", "time", "name", "content_hash", "ref"];
909
+ var AGENT_KEYS_FORMAT = "signafaide-agent-keys/1";
910
+ var MAX_CLOCK_SKEW_MS = 3e5;
911
+ var AGENT_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
912
+ var TIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
913
+ var KEY_ID_RE = /^[0-9a-f]{16}$/;
914
+ var PROJECT_RE = /^prj_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
915
+ var sameKeys = (o, keys) => !!o && typeof o === "object" && !Array.isArray(o) && Object.keys(o).length === keys.length && keys.every((k) => k in o);
916
+ var RECEIPT_FIELDS = ["record_hash", "index", "proof", "batch", "batch_hash", "batch_sig", "service_key_id", "head", "head_sig"];
917
+ var BATCH_FIELDS = ["v", "project", "env", "seq", "prev", "root", "size", "server_time", "drand"];
918
+ var HEAD_FIELDS = ["project", "env", "agent", "seq", "record_hash", "server_time"];
919
+ var DRAND_FIELDS = ["chain", "round", "randomness", "signature"];
920
+ function now() {
921
+ return (/* @__PURE__ */ new Date()).toISOString();
922
+ }
923
+ function clampTime(wall, prev, validFrom) {
924
+ let t = Date.parse(wall);
925
+ for (const f of [prev, validFrom]) if (f && validTime(f)) t = Math.max(t, Date.parse(f));
926
+ const out = new Date(t).toISOString();
927
+ return [out, out !== wall];
928
+ }
929
+ function withWallTime(content, wall) {
930
+ const meta = content.meta;
931
+ if (meta === void 0 || meta === null) return { ...content, meta: { wall_time: wall } };
932
+ if (typeof meta === "object" && !Array.isArray(meta)) return { ...content, meta: { ...meta, wall_time: wall } };
933
+ return { ...content, wall_time: wall };
934
+ }
935
+ function validTime(s) {
936
+ return typeof s === "string" && TIME_RE.test(s) && !s.startsWith("0000") && !Number.isNaN(Date.parse(s)) && new Date(s).toISOString() === s;
937
+ }
938
+ function agentName(raw) {
939
+ const s = raw.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z0-9]+/, "").slice(0, 64).replace(/-+$/, "");
940
+ return s || "agent";
941
+ }
942
+ var isInt = (x) => typeof x === "number" && Number.isInteger(x);
943
+ var isHex = (x) => typeof x === "string" && HEX64.test(x);
944
+ function validProjectId(s) {
945
+ return typeof s === "string" && PROJECT_RE.test(s);
946
+ }
947
+ function receiptErrors(rc) {
948
+ if (!sameKeys(rc, RECEIPT_FIELDS)) return ["receipt fields are " + [...RECEIPT_FIELDS].sort().join(", ")];
949
+ const errs = [];
950
+ const { batch: b, head: h2 } = rc;
951
+ if (!(isHex(rc.record_hash) && isHex(rc.batch_hash) && isInt(rc.index) && rc.index >= 0)) errs.push("receipt record_hash, batch_hash or index is invalid");
952
+ if (!(Array.isArray(rc.proof) && rc.proof.every(isHex))) errs.push("receipt proof must be a list of hashes");
953
+ if (!["batch_sig", "head_sig", "service_key_id"].every((k) => typeof rc[k] === "string")) errs.push("receipt batch_sig, head_sig and service_key_id are strings");
954
+ if (!sameKeys(b, BATCH_FIELDS)) errs.push("batch fields are " + [...BATCH_FIELDS].sort().join(", "));
955
+ else if (!(b.v === 1 && validProjectId(b.project) && (b.env === "test" || b.env === "live") && isInt(b.seq) && b.seq >= 1 && (b.prev === null || isHex(b.prev)) && isHex(b.root) && isInt(b.size) && b.size >= 1 && validTime(b.server_time)))
956
+ errs.push("batch has an invalid value");
957
+ else if (b.drand !== null && !(sameKeys(b.drand, DRAND_FIELDS) && isInt(b.drand.round) && ["chain", "randomness", "signature"].every((k) => typeof b.drand[k] === "string")))
958
+ errs.push("batch drand must be null or {chain, round, randomness, signature}");
959
+ if (!sameKeys(h2, HEAD_FIELDS)) errs.push("head fields are " + [...HEAD_FIELDS].sort().join(", "));
960
+ else if (!(validProjectId(h2.project) && (h2.env === "test" || h2.env === "live") && typeof h2.agent === "string" && AGENT_RE.test(h2.agent) && isInt(h2.seq) && isHex(h2.record_hash) && validTime(h2.server_time)))
961
+ errs.push("head has an invalid value");
962
+ return errs;
963
+ }
964
+ function envelopeErrors(env, fields = ENVELOPE_FIELDS) {
965
+ if (!env || typeof env !== "object" || Array.isArray(env)) return ["envelope is not an object"];
966
+ const e = env;
967
+ const errs = [];
968
+ const missing = fields.filter((f) => !(f in e));
969
+ const extra = Object.keys(e).filter((k) => !fields.includes(k)).sort();
970
+ if (missing.length) errs.push("missing " + missing.join(", "));
971
+ if (extra.length) errs.push("unknown field " + extra.join(", "));
972
+ if (missing.length) return errs;
973
+ if (e.v !== 1) errs.push("v must be 1");
974
+ if (!KINDS.includes(e.kind)) errs.push("kind must be decision, outcome or note");
975
+ if (fields.includes("project") && !validProjectId(e.project)) errs.push("project must be prj_ followed by a 26-character ULID");
976
+ if (fields.includes("env") && e.env !== "test" && e.env !== "live") errs.push("env must be test or live");
977
+ if (typeof e.agent !== "string" || !AGENT_RE.test(e.agent)) errs.push("agent does not match ^[a-z0-9][a-z0-9._-]{0,63}$");
978
+ if (!isInt(e.seq) || e.seq < 1 || e.seq > Number.MAX_SAFE_INTEGER) errs.push("seq must be an integer from 1 to 2^53-1");
979
+ else if (e.seq === 1 && e.prev !== null) errs.push("prev must be null when seq = 1");
980
+ else if (e.seq > 1 && !(typeof e.prev === "string" && HEX64.test(e.prev))) errs.push("prev must be a record hash when seq > 1");
981
+ if (!validTime(e.time)) errs.push(`time ${JSON.stringify(e.time)} is not RFC 3339 UTC with milliseconds`);
982
+ const nameLen = typeof e.name === "string" ? [...e.name].length : 0;
983
+ if (nameLen < 1 || nameLen > 128) errs.push("name must be 1 to 128 characters");
984
+ if (typeof e.content_hash !== "string" || !HEX64.test(e.content_hash)) errs.push("content_hash must be 64 lowercase hex");
985
+ if (e.kind === "outcome") {
986
+ if (!(typeof e.ref === "string" && HEX64.test(e.ref))) errs.push("ref must be the decision's record hash for an outcome");
987
+ } else if (e.ref !== null) errs.push("ref must be null unless kind is outcome");
988
+ return errs;
989
+ }
990
+ function recordErrors(rec) {
991
+ if (!rec || typeof rec !== "object" || Array.isArray(rec)) return ["record is not an object"];
992
+ const r = rec;
993
+ const errs = envelopeErrors(r.envelope);
994
+ const extra = Object.keys(r).filter((k) => !["envelope", "key_id", "sig", "content", "salt", "receipt"].includes(k)).sort();
995
+ if (extra.length) errs.push("unknown record field " + extra.join(", "));
996
+ if (typeof r.key_id !== "string" || !KEY_ID_RE.test(r.key_id)) errs.push("key_id must be 16 lowercase hex");
997
+ let sigOk = false;
998
+ try {
999
+ sigOk = b64urlDecode(r.sig).length === 64;
1000
+ } catch {
1001
+ sigOk = false;
1002
+ }
1003
+ if (!sigOk) errs.push("sig must be a base64url Ed25519 signature");
1004
+ if ("content" in r && (!r.content || typeof r.content !== "object" || Array.isArray(r.content))) errs.push("content must be a JSON object");
1005
+ if ("salt" in r && (typeof r.salt !== "string" || !SALT_RE.test(r.salt))) errs.push("salt must be 32 lowercase hex");
1006
+ return errs;
1007
+ }
1008
+ function makeRecord(key, f) {
1009
+ assertPortableNumbers(f.content);
1010
+ const salt = f.salt ?? newSalt();
1011
+ const envelope = {
1012
+ v: 1,
1013
+ kind: f.kind,
1014
+ project: f.project,
1015
+ env: f.env,
1016
+ agent: f.agent,
1017
+ seq: f.seq,
1018
+ prev: f.prev,
1019
+ time: f.time ?? now(),
1020
+ name: f.name,
1021
+ content_hash: contentHash(f.content, salt),
1022
+ ref: f.ref ?? null
1023
+ };
1024
+ const errs = envelopeErrors(envelope);
1025
+ if (errs.length) throw new Error("invalid record: " + errs.join("; "));
1026
+ return { envelope, key_id: key.keyId, sig: key.sign("record", hashJson(envelope)), content: f.content, salt };
1027
+ }
1028
+ var INSTANCE_START = "signafaide.instance_start";
1029
+ var INSTANCE_STOP = "signafaide.instance_stop";
1030
+ var INSTANCE_NOTE = "a new instance continued the chain, so records an earlier instance wrote but had not sent are not known";
1031
+ var CLEAN_RESTART_NOTE = "a new instance continued the chain right after the earlier instance's signed stop note (a clean restart, nothing to warn about)";
1032
+ function isStopNote(rec, agent, hash) {
1033
+ const e = rec?.envelope;
1034
+ if (!e || e.kind !== "note" || e.name !== INSTANCE_STOP || e.agent !== agent) return false;
1035
+ try {
1036
+ return hashJson(e) === hash;
1037
+ } catch {
1038
+ return false;
1039
+ }
1040
+ }
1041
+ function recordHash(rec) {
1042
+ return hashJson(rec.envelope);
1043
+ }
1044
+ function wireRecord(rec, storeContent) {
1045
+ const out = { envelope: rec.envelope, key_id: rec.key_id, sig: rec.sig };
1046
+ if (storeContent && rec.content !== void 0) {
1047
+ out.content = rec.content;
1048
+ if (rec.salt !== void 0) out.salt = rec.salt;
1049
+ }
1050
+ return out;
1051
+ }
1052
+ function makeDraft(key, f, storeContent) {
1053
+ assertPortableNumbers(f.content);
1054
+ const salt = f.salt ?? newSalt();
1055
+ const d = { v: 1, kind: f.kind, agent: f.agent, seq: f.seq, prev: f.prev, time: f.time ?? now(), name: f.name, content_hash: contentHash(f.content, salt), ref: f.ref ?? null };
1056
+ const errs = envelopeErrors(d, DRAFT_FIELDS);
1057
+ if (errs.length) throw new Error("invalid record: " + errs.join("; "));
1058
+ return { draft: true, envelope: d, key_id: key.keyId, sig: key.sign("draft", hashJson(d)), content: f.content, salt, store_content: storeContent };
1059
+ }
1060
+ function draftErrors(rec) {
1061
+ if (!rec || typeof rec !== "object" || Array.isArray(rec)) return ["draft is not an object"];
1062
+ const r = rec;
1063
+ const errs = envelopeErrors(r.envelope, DRAFT_FIELDS);
1064
+ const extra = Object.keys(r).filter((k) => !["draft", "envelope", "key_id", "sig", "content", "salt", "store_content"].includes(k)).sort();
1065
+ if (extra.length) errs.push("unknown draft field " + extra.join(", "));
1066
+ if (r.draft !== true) errs.push("draft must be true");
1067
+ if (typeof r.key_id !== "string" || !KEY_ID_RE.test(r.key_id)) errs.push("key_id must be 16 lowercase hex (an unsigned draft from an older SDK cannot be verified)");
1068
+ let sigOk = false;
1069
+ try {
1070
+ sigOk = b64urlDecode(r.sig).length === 64;
1071
+ } catch {
1072
+ sigOk = false;
1073
+ }
1074
+ if (!sigOk) errs.push("sig must be a base64url Ed25519 signature");
1075
+ if ("content" in r && (!r.content || typeof r.content !== "object" || Array.isArray(r.content))) errs.push("content must be a JSON object");
1076
+ if ("salt" in r && (typeof r.salt !== "string" || !SALT_RE.test(r.salt))) errs.push("salt must be 32 lowercase hex");
1077
+ if ("store_content" in r && typeof r.store_content !== "boolean") errs.push("store_content must be true or false");
1078
+ return errs;
1079
+ }
1080
+ function signDraft(key, draft, project, env, prev, ref, seq) {
1081
+ const d = draft.envelope;
1082
+ return makeRecord(key, { kind: d.kind, project, env, agent: d.agent, seq: seq ?? d.seq, prev, name: d.name, content: draft.content ?? {}, ref, time: d.time, salt: draft.salt });
1083
+ }
1084
+ function keyEntryErrors(k, agent = false) {
1085
+ const fields = ["key_id", "public_key", "valid_from", "valid_to", ...agent ? ["agent"] : []];
1086
+ if (!sameKeys(k, fields)) return ["key fields are " + [...fields].sort().join(", ")];
1087
+ if (!fields.filter((f) => f !== "valid_to").every((f) => typeof k[f] === "string")) return ["key fields must be strings"];
1088
+ if (!validTime(k.valid_from)) return [`key ${k.key_id}: valid_from is not a time`];
1089
+ if (k.valid_to !== null && !validTime(k.valid_to)) return [`key ${k.key_id}: valid_to is not null or a time`];
1090
+ return [];
1091
+ }
1092
+ function registration(project, env, agent, publicKey, validFrom) {
1093
+ return { project, env, agent, public_key: publicKey, valid_from: validFrom };
1094
+ }
1095
+ function manifestEntry(agent, key, validFrom, validTo = null) {
1096
+ const e = { agent, key_id: key.keyId, public_key: b64url(key.publicKey), valid_from: validFrom, valid_to: validTo };
1097
+ return { ...e, sig: key.sign("agent_key", hashJson(e)) };
1098
+ }
1099
+ function manifestErrors(doc) {
1100
+ if (!sameKeys(doc, ["format", "keys"]) || !Array.isArray(doc.keys)) return ['agent keys must be {"format": "signafaide-agent-keys/1", "keys": [...]}'];
1101
+ if (doc.format !== AGENT_KEYS_FORMAT) return [`format is '${doc.format}', expected '${AGENT_KEYS_FORMAT}'`];
1102
+ for (const e of doc.keys) {
1103
+ if (!sameKeys(e, ["agent", "key_id", "public_key", "valid_from", "valid_to", "sig"]) || !Object.entries(e).every(([k, v]) => typeof v === "string" || k === "valid_to" && v === null)) {
1104
+ return ["each agent key is {agent, key_id, public_key, valid_from, valid_to, sig}"];
1105
+ }
1106
+ if (!AGENT_RE.test(e.agent)) return [`agent ${e.agent} is not a valid agent name`];
1107
+ try {
1108
+ const pk = b64urlDecode(e.public_key);
1109
+ if (pk.length !== 32 || keyId(pk) !== e.key_id) return [`key_id ${e.key_id} does not match its public key`];
1110
+ } catch {
1111
+ return [`key ${e.key_id}: public key is not base64url`];
1112
+ }
1113
+ if (!validTime(e.valid_from)) return [`key ${e.key_id}: valid_from is not a time`];
1114
+ if (e.valid_to !== null && !validTime(e.valid_to)) return [`key ${e.key_id}: valid_to is not null or a time`];
1115
+ const signed = { agent: e.agent, key_id: e.key_id, public_key: e.public_key, valid_from: e.valid_from, valid_to: e.valid_to };
1116
+ if (!verifySig(e.public_key, "agent_key", hashJson(signed), e.sig)) return [`the self-signature of key ${e.key_id} for ${e.agent} does not verify`];
1117
+ }
1118
+ return [];
1119
+ }
1120
+
1121
+ // src/version.ts
1122
+ var VERSION = "0.3.0";
1123
+
1124
+ // src/telemetry.ts
1125
+ var HEADER = "X-Signafaide-Client";
1126
+ var SDK = "signafaide-js";
1127
+ var processEnv = () => typeof process !== "undefined" && process.env ? process.env : {};
1128
+ var hasPrefix = (env, prefix, skip = []) => Object.keys(env).some((k) => k.startsWith(prefix) && !skip.some((s) => k.startsWith(s)));
1129
+ var term = (env) => (env.TERM_PROGRAM ?? "").toLowerCase();
1130
+ var AGENTS = [
1131
+ ["claude-code", (e) => e.CLAUDECODE === "1" || hasPrefix(e, "CLAUDE_CODE_")],
1132
+ ["codex", (e) => hasPrefix(e, "CODEX_", ["CODEX_COMPANION_"])],
1133
+ ["gemini-cli", (e) => hasPrefix(e, "GEMINI_CLI")],
1134
+ ["copilot", (e) => hasPrefix(e, "COPILOT_") || hasPrefix(e, "GITHUB_COPILOT_")],
1135
+ ["windsurf", (e) => hasPrefix(e, "WINDSURF_") || term(e) === "windsurf"],
1136
+ ["cursor", (e) => hasPrefix(e, "CURSOR_") || term(e) === "cursor"]
1137
+ ];
1138
+ function detectAgent(env = processEnv()) {
1139
+ return AGENTS.find(([, test]) => test(env))?.[0] ?? "unknown";
1140
+ }
1141
+ function telemetryEnabled(env = processEnv()) {
1142
+ return (env.SIGNAFAIDE_TELEMETRY ?? "").trim() !== "0";
1143
+ }
1144
+ function runtimeName() {
1145
+ const g = globalThis;
1146
+ if (g.Bun?.version) return `bun ${g.Bun.version}`;
1147
+ if (g.Deno?.version?.deno) return `deno ${g.Deno.version.deno}`;
1148
+ if (typeof process !== "undefined" && process.versions?.node) return `node ${process.versions.node}`;
1149
+ return "js unknown";
1150
+ }
1151
+ function clientHeader(opts = {}) {
1152
+ const on = telemetryEnabled(opts.env);
1153
+ const parts = [runtimeName()];
1154
+ if (on) parts.push(`adapter=${opts.adapter || "none"}`, `agent=${opts.agent || "unknown"}`);
1155
+ parts.push(`via=${opts.via ?? "sdk"}`);
1156
+ if (on && opts.strict) parts.push("strict=1");
1157
+ return `${SDK}/${VERSION} (${parts.join("; ")})`;
1158
+ }
1159
+
1160
+ // src/http.ts
1161
+ var DEFAULT_URL = "https://api.signafaide.co.uk";
1162
+ var NON_RETRYABLE = /* @__PURE__ */ new Set(["quota_exceeded", "invalid_key", "plan_required", "bad_signature", "invalid_record", "content_mismatch", "chain_conflict", "unknown_agent", "key_mismatch", "bad_response"]);
1163
+ var fetchTransport = async (req) => {
1164
+ const ctrl = new AbortController();
1165
+ const timer = setTimeout(() => ctrl.abort(new Error("timeout")), req.timeoutMs);
1166
+ const onAbort = () => ctrl.abort(new Error("aborted"));
1167
+ if (req.signal?.aborted) onAbort();
1168
+ else req.signal?.addEventListener("abort", onAbort, { once: true });
1169
+ try {
1170
+ const resp = await fetch(req.url, { method: req.method, headers: req.headers, body: req.body, signal: ctrl.signal });
1171
+ return { status: resp.status, contentType: resp.headers.get("content-type") ?? "", text: await resp.text() };
1172
+ } finally {
1173
+ clearTimeout(timer);
1174
+ req.signal?.removeEventListener("abort", onAbort);
1175
+ }
1176
+ };
1177
+ var SignafaideError = class extends Error {
1178
+ code;
1179
+ status;
1180
+ docs;
1181
+ body;
1182
+ constructor(message, opts = {}) {
1183
+ super(message);
1184
+ this.name = "SignafaideError";
1185
+ this.code = opts.code ?? "error";
1186
+ this.status = opts.status ?? 0;
1187
+ this.docs = opts.docs;
1188
+ this.body = opts.body ?? {};
1189
+ }
1190
+ get retryable() {
1191
+ return this.code === "network" || this.code === "rate_limited" || this.status >= 500;
1192
+ }
1193
+ /** The service refused for a reason retrying cannot change (quota, key, revoked agent key...). */
1194
+ get permanent() {
1195
+ return !this.retryable && NON_RETRYABLE.has(this.code);
1196
+ }
1197
+ };
1198
+ var Client = class {
1199
+ constructor(url = DEFAULT_URL, apiKey, timeoutMs = 1e4, clientId, opts = {}) {
1200
+ this.apiKey = apiKey;
1201
+ this.timeoutMs = timeoutMs;
1202
+ this.url = url.replace(/\/+$/, "");
1203
+ this.clientId = clientId ?? clientHeader({});
1204
+ this.transport = opts.transport ?? fetchTransport;
1205
+ this.signal = opts.signal;
1206
+ this.timeoutLabel = opts.timeoutLabel;
1207
+ }
1208
+ apiKey;
1209
+ timeoutMs;
1210
+ url;
1211
+ clientId;
1212
+ transport;
1213
+ signal;
1214
+ /** Said instead of "no answer within <timeout> s" when the request shares a larger limit. */
1215
+ timeoutLabel;
1216
+ async request(method, path, opts = {}) {
1217
+ const auth = opts.auth ?? true;
1218
+ let url = this.url + path;
1219
+ const q = Object.entries(opts.query ?? {}).filter((e) => e[1] != null);
1220
+ if (q.length) url += "?" + new URLSearchParams(q).toString();
1221
+ const headers = { Accept: "application/json", [HEADER]: this.clientId };
1222
+ if (typeof process !== "undefined" && process.versions?.node) headers["User-Agent"] = `signafaide-js/${VERSION}`;
1223
+ if (opts.body !== void 0) headers["Content-Type"] = "application/json";
1224
+ if (auth) {
1225
+ if (!this.apiKey) throw new SignafaideError("No API key: run `signafaide init` or set SIGNAFAIDE_API_KEY.", { code: "invalid_key" });
1226
+ headers.Authorization = `Bearer ${this.apiKey}`;
1227
+ }
1228
+ let resp;
1229
+ try {
1230
+ resp = await this.transport({ method, url, headers, body: opts.body === void 0 ? void 0 : JSON.stringify(opts.body), timeoutMs: this.timeoutMs, signal: this.signal });
1231
+ } catch (e) {
1232
+ const err2 = e;
1233
+ const raw = err2.cause?.code ?? err2.code ?? err2.message ?? String(e);
1234
+ const reason = /abort|timeout/i.test(`${err2.name} ${raw}`) ? this.timeoutLabel ?? `no answer within ${this.timeoutMs / 1e3} s` : raw;
1235
+ throw new SignafaideError(`Cannot reach the signafAIde service at ${this.url} (${reason}); check the network or SIGNAFAIDE_URL.`, { code: "network" });
1236
+ }
1237
+ const { status, text, contentType: ctype } = resp;
1238
+ if (status < 200 || status >= 300) throw serviceError(status, text);
1239
+ return ctype.includes("json") || /^[[{]/.test(text) ? JSON.parse(text) : text;
1240
+ }
1241
+ sandbox() {
1242
+ return this.request("POST", "/v1/sandbox", { body: {}, auth: false });
1243
+ }
1244
+ /** §2.4: the registration with its proof of possession (and rotation signature). */
1245
+ registerAgent(name, publicKey, validFrom, proof, rotation) {
1246
+ return this.request("POST", "/v1/agents", { body: { name, public_key: publicKey, valid_from: validFrom, proof, ...rotation ? { rotation } : {} } });
1247
+ }
1248
+ /** `{project, env}` of this API key (§6). */
1249
+ project() {
1250
+ return this.request("GET", "/v1/project");
1251
+ }
1252
+ submit(records) {
1253
+ return this.request("POST", "/v1/records", { body: { records } });
1254
+ }
1255
+ head(agent) {
1256
+ return this.request("GET", `/v1/heads/${encodeURIComponent(agent)}`);
1257
+ }
1258
+ /** `{record, receipt, anchor}` of one record of the key's project. */
1259
+ record(recordHash2) {
1260
+ return this.request("GET", `/v1/records/${encodeURIComponent(recordHash2)}`);
1261
+ }
1262
+ /** A verification pack (§7) of the whole project, or of one agent. */
1263
+ export(agent, from, to) {
1264
+ return this.request("GET", "/v1/export", { query: { agent, from, to } });
1265
+ }
1266
+ usage() {
1267
+ return this.request("GET", "/v1/usage");
1268
+ }
1269
+ /** Usage event (`verify`, `export`). Not part of SPEC §6 yet. */
1270
+ async event(name) {
1271
+ await this.request("POST", "/v1/events", { body: { name }, auth: !!this.apiKey });
1272
+ }
1273
+ serviceKeys() {
1274
+ return this.request("GET", "/.well-known/signafaide-keys.json", { auth: false });
1275
+ }
1276
+ };
1277
+ function serviceError(status, text) {
1278
+ try {
1279
+ const body = JSON.parse(text);
1280
+ const err2 = body.error;
1281
+ if (typeof err2?.message === "string") return new SignafaideError(err2.message, { code: err2.code ?? "error", status, docs: err2.docs, body });
1282
+ } catch {
1283
+ }
1284
+ return new SignafaideError(`The service answered HTTP ${status}: ${text.trim().slice(0, 200) || "no body"}.`, { code: "http_error", status });
1285
+ }
1286
+ async function fetchJson(url, timeoutMs = 1e4) {
1287
+ const u = new URL(url);
1288
+ return new Client(u.origin, void 0, timeoutMs, `signafaide-js/${VERSION}`).request("GET", u.pathname + u.search, { auth: false });
1289
+ }
1290
+
1291
+ // src/plural.ts
1292
+ var plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
1293
+
1294
+ // src/coverage.ts
1295
+ var TOOLS = "signafaide.tools";
1296
+ var TOOL_PREFIX = "signafaide.tool/";
1297
+ var TOOL_UNRECORDED_PREFIX = "signafaide.tool_unrecorded/";
1298
+ var RESERVED_PREFIX = "signafaide.";
1299
+ var MAX_DECLARED_NAME = 101;
1300
+ var KEPT = MAX_DECLARED_NAME - 9;
1301
+ function declaredName(tool) {
1302
+ const chars = [...tool];
1303
+ if (chars.length <= MAX_DECLARED_NAME) return tool;
1304
+ return `${chars.slice(0, KEPT).join("")}~${sha256Hex(new TextEncoder().encode(tool)).slice(0, 8)}`;
1305
+ }
1306
+ function toolNoteName(tool, recorded) {
1307
+ return (recorded ? TOOL_PREFIX : TOOL_UNRECORDED_PREFIX) + declaredName(tool);
1308
+ }
1309
+ function toolNote(kind, name) {
1310
+ if (kind !== "note" || typeof name !== "string") return null;
1311
+ if (name === TOOLS) return "header";
1312
+ const recorded = name.startsWith(TOOL_PREFIX);
1313
+ if (!recorded && !name.startsWith(TOOL_UNRECORDED_PREFIX)) return null;
1314
+ const rest = name.slice((recorded ? TOOL_PREFIX : TOOL_UNRECORDED_PREFIX).length);
1315
+ return rest ? { name: rest, recorded } : null;
1316
+ }
1317
+ function byCodePoint(a, b) {
1318
+ const [x, y] = [[...a], [...b]];
1319
+ for (let i = 0; i < x.length && i < y.length; i++) {
1320
+ const d = x[i].codePointAt(0) - y[i].codePointAt(0);
1321
+ if (d) return d;
1322
+ }
1323
+ return x.length - y.length;
1324
+ }
1325
+ function coverage(records) {
1326
+ let at = null;
1327
+ let tools = /* @__PURE__ */ new Map();
1328
+ const called = /* @__PURE__ */ new Set();
1329
+ for (const { seq, kind, name } of records) {
1330
+ const t = toolNote(kind, name);
1331
+ if (t === "header") {
1332
+ at = seq;
1333
+ tools = /* @__PURE__ */ new Map();
1334
+ } else if (t) {
1335
+ if (at !== null) tools.set(t.name, t.recorded);
1336
+ } else if (kind === "decision" && typeof name === "string" && !name.startsWith(RESERVED_PREFIX)) called.add(declaredName(name));
1337
+ }
1338
+ if (at === null) return { declared: [], undeclared_recorded: [], declared_at_seq: null };
1339
+ return {
1340
+ declared: [...tools.keys()].sort(byCodePoint).map((name) => ({ name, recorded: tools.get(name) })),
1341
+ undeclared_recorded: [...called].filter((n) => !tools.has(n)).sort(byCodePoint),
1342
+ declared_at_seq: at
1343
+ };
1344
+ }
1345
+ var countIs = (n, one) => `${plural(n, one)} ${n === 1 ? "is" : "are"}`;
1346
+ function coverageLines(agent, c) {
1347
+ if (c.declared_at_seq === null) return [`${agent}: no tool list was declared, so the pack cannot show which of the agent's tools are recorded`];
1348
+ const unrecorded = c.declared.filter((t) => !t.recorded).map((t) => t.name);
1349
+ const out = [];
1350
+ if (!c.declared.length) out.push(`${agent}: the declared tool list is empty`);
1351
+ else if (!unrecorded.length) out.push(`${agent}: ${c.declared.length === 1 ? "the" : "all"} ${countIs(c.declared.length, "declared tool")} recorded`);
1352
+ else out.push(`${agent}: ${countIs(unrecorded.length, "declared tool")} not recorded: ${unrecorded.join(", ")}`);
1353
+ if (c.undeclared_recorded.length) out.push(`${agent}: ${countIs(c.undeclared_recorded.length, "recorded tool")} not in the declared tool list: ${c.undeclared_recorded.join(", ")}`);
1354
+ return out;
1355
+ }
1356
+
1357
+ // src/ots.ts
1358
+ var import_legacy = require("@noble/hashes/legacy");
1359
+ var import_sha22 = require("@noble/hashes/sha2");
1360
+ var import_sha3 = require("@noble/hashes/sha3");
1361
+ var import_utils2 = require("@noble/hashes/utils");
1362
+ var MAGIC = new Uint8Array([
1363
+ 0,
1364
+ 79,
1365
+ 112,
1366
+ 101,
1367
+ 110,
1368
+ 84,
1369
+ 105,
1370
+ 109,
1371
+ 101,
1372
+ 115,
1373
+ 116,
1374
+ 97,
1375
+ 109,
1376
+ 112,
1377
+ 115,
1378
+ 0,
1379
+ 0,
1380
+ 80,
1381
+ 114,
1382
+ 111,
1383
+ 111,
1384
+ 102,
1385
+ 0,
1386
+ 191,
1387
+ 137,
1388
+ 226,
1389
+ 232,
1390
+ 132,
1391
+ 232,
1392
+ 146,
1393
+ 148
1394
+ ]);
1395
+ var TAG_BITCOIN = "0588960d73d71901";
1396
+ var TAG_PENDING = "83dfe30d2ef90c8e";
1397
+ var MAX_MSG = 4096;
1398
+ var MAX_DEPTH = 256;
1399
+ var MAX_PROOF_BYTES = 65536;
1400
+ var MAX_NODES = 1024;
1401
+ var UNARY = {
1402
+ 2: (m) => (0, import_legacy.sha1)(m),
1403
+ 3: (m) => (0, import_legacy.ripemd160)(m),
1404
+ 8: (m) => (0, import_sha22.sha256)(m),
1405
+ 103: (m) => (0, import_sha3.keccak_256)(m),
1406
+ 242: (m) => Uint8Array.from(m).reverse(),
1407
+ 243: (m) => new TextEncoder().encode((0, import_utils2.bytesToHex)(m))
1408
+ };
1409
+ var BINARY = {
1410
+ 240: (m, a) => (0, import_utils2.concatBytes)(m, a),
1411
+ 241: (m, a) => (0, import_utils2.concatBytes)(a, m)
1412
+ };
1413
+ var hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
1414
+ var Reader = class {
1415
+ constructor(data) {
1416
+ this.data = data;
1417
+ }
1418
+ data;
1419
+ pos = 0;
1420
+ nodes = 0;
1421
+ byte() {
1422
+ if (this.pos >= this.data.length) throw new Error("ots: unexpected end of proof");
1423
+ return this.data[this.pos++];
1424
+ }
1425
+ take(n) {
1426
+ if (this.data.length - this.pos < n) throw new Error("ots: unexpected end of proof");
1427
+ this.pos += n;
1428
+ return this.data.subarray(this.pos - n, this.pos);
1429
+ }
1430
+ varuint() {
1431
+ let value = 0;
1432
+ for (let shift = 0; shift < 64; shift += 7) {
1433
+ const b = this.byte();
1434
+ value += (b & 127) * 2 ** shift;
1435
+ if (!(b & 128)) return value;
1436
+ }
1437
+ throw new Error("ots: varuint too long");
1438
+ }
1439
+ varbytes(limit) {
1440
+ const n = this.varuint();
1441
+ if (n > limit) throw new Error(`ots: field longer than ${limit} bytes`);
1442
+ return this.take(n);
1443
+ }
1444
+ node() {
1445
+ if (++this.nodes > MAX_NODES) throw new Error(`ots: proof has more than ${MAX_NODES} operations and attestations`);
1446
+ }
1447
+ };
1448
+ function parseOts(data) {
1449
+ if (data.length > MAX_PROOF_BYTES) throw new Error(`ots: proof is longer than ${MAX_PROOF_BYTES} bytes`);
1450
+ const r = new Reader(data);
1451
+ if (hex(r.take(MAGIC.length)) !== hex(MAGIC)) throw new Error("ots: not an OpenTimestamps proof (bad magic)");
1452
+ if (r.varuint() !== 1) throw new Error("ots: unsupported proof version");
1453
+ if (r.byte() !== 8) throw new Error("ots: only SHA-256 file digests are supported");
1454
+ const digest2 = Uint8Array.from(r.take(32));
1455
+ const attestations = [];
1456
+ readTimestamp(r, digest2, attestations, 0);
1457
+ if (r.pos !== data.length) throw new Error("ots: trailing bytes after timestamp");
1458
+ return { digest: hex(digest2), attestations };
1459
+ }
1460
+ function readTimestamp(r, msg, found, depth) {
1461
+ if (depth > MAX_DEPTH) throw new Error("ots: proof nested too deeply");
1462
+ for (; ; ) {
1463
+ let tag = r.byte();
1464
+ const fork = tag === 255;
1465
+ if (fork) tag = r.byte();
1466
+ r.node();
1467
+ if (tag === 0) found.push({ ...readAttestation(r), msg });
1468
+ else {
1469
+ let next;
1470
+ if (tag in BINARY) next = BINARY[tag](msg, r.varbytes(MAX_MSG));
1471
+ else if (tag in UNARY) next = UNARY[tag](msg);
1472
+ else throw new Error(`ots: unknown operation tag 0x${tag.toString(16).padStart(2, "0")}`);
1473
+ if (next.length > MAX_MSG) throw new Error(`ots: operation result longer than ${MAX_MSG} bytes`);
1474
+ readTimestamp(r, next, found, depth + 1);
1475
+ }
1476
+ if (!fork) return;
1477
+ }
1478
+ }
1479
+ function readAttestation(r) {
1480
+ const tag = hex(r.take(8));
1481
+ const p = new Reader(r.varbytes(8192));
1482
+ if (tag === TAG_BITCOIN) return { kind: "bitcoin", height: p.varuint() };
1483
+ if (tag === TAG_PENDING) {
1484
+ const uri = p.varbytes(1e3);
1485
+ if (!uri.every((c) => /[A-Za-z0-9\-.:/_]/.test(String.fromCharCode(c)))) throw new Error("ots: calendar URI has invalid characters");
1486
+ return { kind: "pending", uri: new TextDecoder().decode(uri) };
1487
+ }
1488
+ return { kind: "unknown", tag };
1489
+ }
1490
+ function otsStatus(data) {
1491
+ const { digest: digest2, attestations } = parseOts(data);
1492
+ const heights = attestations.flatMap((a) => a.kind === "bitcoin" ? [a.height] : []);
1493
+ if (heights.length) return { digest: digest2, height: Math.min(...heights) };
1494
+ if (attestations.some((a) => a.kind === "pending")) return { digest: digest2, height: null };
1495
+ throw new Error("OTS proof has no attestation");
1496
+ }
1497
+ function blockAttestations(data) {
1498
+ return parseOts(data).attestations.flatMap((a) => {
1499
+ if (a.kind !== "bitcoin") return [];
1500
+ if (a.msg.length !== 32) throw new Error(`ots: the timestamp attestation for block ${a.height} is not over a 32-byte Merkle root`);
1501
+ return [[a.height, hex(a.msg)]];
1502
+ });
1503
+ }
1504
+ function base64Decode(s) {
1505
+ if (typeof s !== "string" || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(s)) throw new Error("ots is not standard base64");
1506
+ const bin = atob(s);
1507
+ return Uint8Array.from(bin, (c) => c.charCodeAt(0));
1508
+ }
1509
+
1510
+ // src/timestamps.ts
1511
+ var import_sha23 = require("@noble/hashes/sha2");
1512
+ var import_utils3 = require("@noble/hashes/utils");
1513
+ function target(bits) {
1514
+ const exponent = bits >>> 24;
1515
+ const mantissa = BigInt(bits & 8388607);
1516
+ if (bits & 8388608 || mantissa === 0n) return null;
1517
+ const t = exponent >= 3 ? mantissa << BigInt(8 * (exponent - 3)) : mantissa >> BigInt(8 * (3 - exponent));
1518
+ return t > 0n && t < 1n << 256n ? t : null;
1519
+ }
1520
+ function checkHeader(hash, headerHex) {
1521
+ const hx = String(headerHex).trim();
1522
+ if (!/^(?:[0-9a-fA-F]{2})*$/.test(hx)) throw new Error("the block header is not hex");
1523
+ const header = (0, import_utils3.hexToBytes)(hx.toLowerCase());
1524
+ if (header.length !== 80) throw new Error(`the block header is ${header.length} bytes, not 80`);
1525
+ const id = (0, import_utils3.bytesToHex)((0, import_sha23.sha256)((0, import_sha23.sha256)(header)).reverse());
1526
+ if (id !== String(hash).trim().toLowerCase()) throw new Error(`the block header does not hash to block ${hash}`);
1527
+ const bits = new DataView(header.buffer, header.byteOffset).getUint32(72, true);
1528
+ const t = target(bits);
1529
+ if (t === null) throw new Error(`the block header's nBits 0x${bits.toString(16).padStart(8, "0")} is not a valid target`);
1530
+ if (BigInt("0x" + id) > t) throw new Error(`block ${hash} does not meet the proof of work its header claims`);
1531
+ return (0, import_utils3.bytesToHex)(header.slice(36, 68));
1532
+ }
1533
+ function rootsFromVector(m) {
1534
+ return new Map(
1535
+ Object.entries(m).filter(([, r]) => !!r && typeof r === "object").map(([h2, r]) => [Number(h2), [true, r]])
1536
+ );
1537
+ }
1538
+
1539
+ // src/verifier.ts
1540
+ var import_utils4 = require("@noble/hashes/utils");
1541
+ var CHECKS = [
1542
+ ["schema", "Envelope schema"],
1543
+ ["record_hash", "Record hashes"],
1544
+ ["record_sig", "Client signatures"],
1545
+ ["chain", "Agent chains"],
1546
+ ["merkle", "Merkle proofs"],
1547
+ ["batch_sig", "Batch hashes and signatures"],
1548
+ ["batch_chain", "Batch chain"],
1549
+ ["head_sig", "Signed heads"],
1550
+ ["drand", "drand rounds"],
1551
+ ["anchor", "Anchors"],
1552
+ ["ots", "OpenTimestamps"],
1553
+ ["content", "Content hashes"]
1554
+ ];
1555
+ var NO_RECORDS = "no records: an empty pack proves nothing";
1556
+ var DRAND_NOT_CHECKED = "drand: not checked (BLS library not installed; npm install @noble/curves)";
1557
+ var NO_LOCAL_RECORDS = "no records: nothing has been recorded here yet";
1558
+ var NOT_INDEPENDENT = "NOT INDEPENDENT: agent keys came from the pack; pass --agent-keys <file|url> (the customer's `signafaide keys export`), or --trust-embedded-agent-keys";
1559
+ var QUICKNET_GENESIS = 1692803367;
1560
+ var MAX_ROUND_AGE_MS = 12e4;
1561
+ var MAX_ROUND = Math.floor((253402300799 - QUICKNET_GENESIS) / 3) + 1;
1562
+ var ANCHOR_WINDOW_MS = 72e5;
1563
+ var UNITS = [["record"], ["record"], ["record"], ["record"], ["record"], ["batch", "batches"], ["batch", "batches"], ["head"], ["round"], ["anchor"], ["proof"], ["record"]];
1564
+ var QUICKNET_CHAIN = "52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971";
1565
+ var QUICKNET_PUBLIC_KEY = "83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a";
1566
+ var KeysRequired = class extends Error {
1567
+ name = "KeysRequired";
1568
+ };
1569
+ function keyReasons(service, agent) {
1570
+ const a = service === "independent" ? null : service;
1571
+ const b = agent === "independent" ? null : agent;
1572
+ if (a && b) return a === b ? `service keys and agent keys ${a}` : `service keys ${a} and agent keys ${b}`;
1573
+ if (a) return `service keys ${a}`;
1574
+ if (b) return `agent keys ${b}`;
1575
+ return null;
1576
+ }
1577
+ function scopeText(filter, generated) {
1578
+ const a = filter.agents;
1579
+ const who = a === null ? "every agent" : a.length === 1 ? `agent ${a[0]}` : `agents ${a.join(", ")}`;
1580
+ const { from: f, to: t } = filter;
1581
+ const window = f === null && t === null ? "all records" : t === null ? `records from ${f}` : f === null ? `records before ${t}` : `records from ${f} before ${t}`;
1582
+ return `export of ${who}, ${window}, generated ${generated}`;
1583
+ }
1584
+ var SHORT_NAMES = {
1585
+ schema: "envelope schema",
1586
+ record_hash: "record hashes",
1587
+ record_sig: "client signatures",
1588
+ chain: "agent chains",
1589
+ merkle: "Merkle proofs",
1590
+ batch_sig: "batch signatures",
1591
+ batch_chain: "batch chain",
1592
+ head_sig: "signed heads",
1593
+ drand: "drand rounds",
1594
+ anchor: "anchors",
1595
+ ots: "public timestamp",
1596
+ content: "content hashes"
1597
+ };
1598
+ var WITNESS_CHECKS = ["record_hash", "merkle", "batch_sig", "batch_chain", "head_sig", "drand", "anchor", "ots"];
1599
+ var NOTHING_COSIGNED = "nothing co-signed yet";
1600
+ function countsText(checks, nothingCosigned = false) {
1601
+ const total = checks.length;
1602
+ const failed = checks.filter((c) => c.status === "fail").map((c) => SHORT_NAMES[c.id]);
1603
+ const pending = checks.filter((c) => c.status === "pending").map((c) => SHORT_NAMES[c.id]);
1604
+ const unchecked = checks.filter((c) => c.status === "not_checked").map((c) => c.id);
1605
+ const passed = total - failed.length - pending.length - unchecked.length;
1606
+ if (failed.length) {
1607
+ let out2 = `${failed.length} of ${total} checks failed (${failed.join(", ")})`;
1608
+ if (passed) out2 += `, ${passed} passed`;
1609
+ if (pending.length) out2 += `, ${pending.length} pending`;
1610
+ if (unchecked.length) out2 += `, ${unchecked.length} not checked`;
1611
+ return out2;
1612
+ }
1613
+ let out = `${passed} of ${total} checks passed`;
1614
+ if (pending.length) out += `, ${pending.length} pending (${pending.join(", ")})`;
1615
+ if (unchecked.length) {
1616
+ let names = unchecked.map((i) => SHORT_NAMES[i]);
1617
+ if (nothingCosigned && unchecked.some((i) => WITNESS_CHECKS.includes(i))) {
1618
+ names = [NOTHING_COSIGNED, ...unchecked.filter((i) => !WITNESS_CHECKS.includes(i)).map((i) => SHORT_NAMES[i])];
1619
+ }
1620
+ out += `, ${unchecked.length} not checked (${names.join(", ")})`;
1621
+ }
1622
+ return out;
1623
+ }
1624
+ function resultLine(result, service = "independent", agent = "independent", byService = false, scope = null, counts = "") {
1625
+ let out = result;
1626
+ if (result === "PASS") {
1627
+ if (byService) out += " (checked by the service with its own key registry; not independent)";
1628
+ else {
1629
+ const r = keyReasons(service, agent);
1630
+ if (r) out += ` (${r}, not independent)`;
1631
+ }
1632
+ }
1633
+ if (counts) out += `: ${counts}`;
1634
+ if (scope) out += counts ? `; ${scope}` : `: ${scope}`;
1635
+ return out;
1636
+ }
1637
+ var PENDING_MEANS = "pending: co-signed, waiting for the public timestamp (an anchor is made every hour and usually confirms within a few hours); never counted as passed";
1638
+ var Report = class {
1639
+ constructor(checks, project = null, env = null, records = 0, warnings = [], notes = [], independent = { service_keys: true, agent_keys: true }, scope = null) {
1640
+ this.checks = checks;
1641
+ this.project = project;
1642
+ this.env = env;
1643
+ this.records = records;
1644
+ this.warnings = warnings;
1645
+ this.notes = notes;
1646
+ this.independent = independent;
1647
+ this.scope = scope;
1648
+ }
1649
+ checks;
1650
+ project;
1651
+ env;
1652
+ records;
1653
+ warnings;
1654
+ notes;
1655
+ independent;
1656
+ scope;
1657
+ /** Lines shown after the checks, before the result line: for local state, what still needs
1658
+ * the witness (co-signature, public timestamp). */
1659
+ after = [];
1660
+ /** Local state with no record co-signed yet: the result line says so (§7). */
1661
+ nothingCosigned = false;
1662
+ /** Local state: chains that cannot ship (refused by the witness, or wedged here), as
1663
+ * `<chain>: <reason>`. Any makes the result BLOCKED, which always fails (§7). */
1664
+ blocked = [];
1665
+ /** Local state with --fail-on-pending-send: records not co-signed yet, which then fail. */
1666
+ notSent = 0;
1667
+ /** The project's name from the signed export statement, when the pack has one. */
1668
+ projectName = null;
1669
+ /** Tool coverage per agent (§7; per chain for local state): the agent's current tool
1670
+ * declaration and the recorded tools it does not list. A note, never a failure. */
1671
+ coverage = {};
1672
+ /** e.g. `PASS: export of every agent, all records, generated <t>`, or
1673
+ * `PASS (agent keys taken from the pack, not independent): export of ...` (§7). */
1674
+ get resultLine() {
1675
+ const s = this.scope;
1676
+ let counts = countsText(this.checks, this.nothingCosigned);
1677
+ if (this.blocked.length) counts = `${plural(this.blocked.length, "chain")} cannot ship (${this.blocked.map((b) => b.split(":")[0]).join(", ")}); ${counts}`;
1678
+ else if (this.notSent) counts = `${plural(this.notSent, "record")} not sent yet (--fail-on-pending-send); ${counts}`;
1679
+ if (!s) return resultLine(this.result, this.independent.service_keys ? "independent" : "fetched over plain http", "independent", false, null, counts);
1680
+ return resultLine(this.result, s.sources[0], s.sources[1], s.byService, scopeText(s.filter, s.generated), counts);
1681
+ }
1682
+ get ok() {
1683
+ return !this.blocked.length && !this.notSent && this.checks.every((c) => c.status !== "fail");
1684
+ }
1685
+ get result() {
1686
+ if (this.checks.some((c) => c.status === "fail") || this.notSent && !this.blocked.length) return "FAIL";
1687
+ return this.blocked.length ? "BLOCKED" : "PASS";
1688
+ }
1689
+ check(id) {
1690
+ return this.checks.find((c) => c.id === id);
1691
+ }
1692
+ firstFailed() {
1693
+ return this.checks.find((c) => c.status === "fail")?.id ?? null;
1694
+ }
1695
+ /** Tool coverage in words (§7), agent by agent: what each agent declared, then the recorded
1696
+ * tools its declaration does not list. */
1697
+ coverageLines() {
1698
+ return Object.entries(this.coverage).flatMap(([agent, c]) => coverageLines(agent, c));
1699
+ }
1700
+ lines() {
1701
+ const out = [...this.notes];
1702
+ if (this.project && this.env) {
1703
+ out.push(
1704
+ this.projectName ? `signafAIde pack of project ${this.projectName} (${this.project}, ${this.env}), ${plural(this.records, "record")}` : `signafAIde pack ${this.project} (${this.env}), ${plural(this.records, "record")}`
1705
+ );
1706
+ }
1707
+ out.push(...this.warnings.map((w) => `WARNING: ${w}`));
1708
+ for (const c of this.checks) {
1709
+ const label = c.status === "fail" ? "FAIL" : c.status === "not_checked" ? "NOT CHECKED" : c.status === "pending" ? "PENDING" : c.value?.startsWith("confirmed@") ? "CONFIRMED" : (c.value ?? "pass").toUpperCase();
1710
+ out.push(`${String(c.n).padStart(2)}. ${c.title.padEnd(28)} ${label.padEnd(12)} ${c.detail}`);
1711
+ out.push(...c.failures.slice(0, 20).map((f) => ` - ${f.item}: ${f.message}`));
1712
+ if (c.failures.length > 20) out.push(` ... and ${c.failures.length - 20} more`);
1713
+ }
1714
+ if (this.checks.some((c) => c.status === "pending")) out.push(PENDING_MEANS);
1715
+ out.push(...this.coverageLines().map((l) => `coverage: ${l}`));
1716
+ out.push(...this.after);
1717
+ out.push(...this.blocked.map((b) => `BLOCKED ${b}`));
1718
+ out.push(this.resultLine);
1719
+ return out;
1720
+ }
1721
+ toString() {
1722
+ return this.lines().join("\n");
1723
+ }
1724
+ toJSON() {
1725
+ return {
1726
+ result: this.result,
1727
+ result_line: this.resultLine,
1728
+ project: this.project,
1729
+ ...this.projectName ? { project_name: this.projectName } : {},
1730
+ env: this.env,
1731
+ records: this.records,
1732
+ generated: this.scope?.generated ?? null,
1733
+ filter: this.scope?.filter ?? null,
1734
+ warnings: this.warnings,
1735
+ independent: this.independent,
1736
+ notes: this.notes,
1737
+ checks: this.checks,
1738
+ coverage: this.coverage,
1739
+ ...this.after.length ? { not_yet: this.after } : {},
1740
+ ...this.blocked.length ? { blocked: this.blocked } : {}
1741
+ };
1742
+ }
1743
+ };
1744
+ function parseServiceKeys(doc) {
1745
+ const keys = Array.isArray(doc) ? doc : doc?.keys;
1746
+ if (!Array.isArray(keys)) throw new Error('service keys must be {"keys": [...]} as served at /.well-known/signafaide-keys.json');
1747
+ for (const k of keys) {
1748
+ const errs = keyEntryErrors(k);
1749
+ if (errs.length) throw new Error("keys: " + errs.join("; "));
1750
+ }
1751
+ return keys;
1752
+ }
1753
+ function parseAgentKeys(doc) {
1754
+ const errs = manifestErrors(doc);
1755
+ if (errs.length) throw new Error("agent keys: " + errs.join("; "));
1756
+ return doc.keys;
1757
+ }
1758
+ function parseHeadFile(doc) {
1759
+ const obj = (v, what) => {
1760
+ if (!v || typeof v !== "object" || Array.isArray(v)) throw new Error(`head file: ${what} must be an object`);
1761
+ return v;
1762
+ };
1763
+ const out = [];
1764
+ for (const envs of Object.values(obj(doc, "the file")))
1765
+ for (const agents of Object.values(obj(envs, "each project")))
1766
+ for (const sh of Object.values(obj(agents, "each env"))) {
1767
+ const why = signedHeadError(sh);
1768
+ if (why) throw new Error(`head file: ${why}`);
1769
+ out.push(sh);
1770
+ }
1771
+ return out;
1772
+ }
1773
+ function contentFromText(text, into = /* @__PURE__ */ new Map()) {
1774
+ let items;
1775
+ try {
1776
+ const doc = parseJson(text);
1777
+ items = Array.isArray(doc) ? doc : [doc];
1778
+ } catch {
1779
+ items = text.split("\n").filter((l) => l.trim()).map((l) => parseJson(l));
1780
+ }
1781
+ for (const it of items) {
1782
+ if (it?.envelope && typeof it.envelope === "object" && it.content && typeof it.content === "object" && !Array.isArray(it.content) && !it.draft) {
1783
+ into.set(hashJson(it.envelope), { content: it.content, salt: typeof it.salt === "string" ? it.salt : void 0 });
1784
+ }
1785
+ }
1786
+ return into;
1787
+ }
1788
+ function timestampHeights(pack) {
1789
+ const out = /* @__PURE__ */ new Set();
1790
+ for (const a of Array.isArray(pack?.anchors) ? pack.anchors : []) {
1791
+ try {
1792
+ for (const [h2] of blockAttestations(base64Decode(a.anchor.ots))) out.add(h2);
1793
+ } catch {
1794
+ }
1795
+ }
1796
+ return [...out].sort((x, y) => x - y);
1797
+ }
1798
+ function recordsHash(records) {
1799
+ return hashJson(
1800
+ records.map((r) => {
1801
+ const h2 = r?.receipt?.record_hash;
1802
+ if (typeof h2 !== "string") throw new Error("a record has no receipt.record_hash");
1803
+ return h2;
1804
+ })
1805
+ );
1806
+ }
1807
+ function exportStatement(pack) {
1808
+ return { project: pack.project, ...pack.project_name !== void 0 ? { project_name: pack.project_name } : {}, env: pack.env, generated: pack.generated, filter: pack.filter, heads: pack.heads.map((sh) => sh.head), records_hash: recordsHash(pack.records) };
1809
+ }
1810
+ function isLoopbackUrl(url) {
1811
+ if (!url.startsWith("http://")) return false;
1812
+ const authority = url.slice(7).split(/[/?#]/)[0];
1813
+ if (authority.includes("@")) return false;
1814
+ let host = authority.startsWith("[") ? authority.slice(1).split("]")[0] : authority.includes(":") ? authority.slice(0, authority.lastIndexOf(":")) : authority;
1815
+ host = host.toLowerCase();
1816
+ if (host === "localhost" || host === "::1" || /^0*:(0*:){0,6}0*1$/.test(host) || host === "0:0:0:0:0:0:0:1") return true;
1817
+ const v4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
1818
+ return !!v4 && Number(v4[1]) === 127 && v4.slice(1).every((o) => Number(o) <= 255 && String(Number(o)) === o);
1819
+ }
1820
+ function verifyPack(pack, serviceKeys, opts = {}) {
1821
+ const byService = !!opts.byService;
1822
+ let serviceSrc = !serviceKeys ? "taken from the pack" : opts.serviceKeysPlainHttp ? "fetched over plain http" : "independent";
1823
+ let agentSrc = !opts.agentKeys ? "taken from the pack" : opts.agentKeysPlainHttp ? "fetched over plain http" : "independent";
1824
+ if (byService) serviceSrc = agentSrc = "taken from the pack";
1825
+ const independent = { service_keys: serviceSrc === "independent", agent_keys: agentSrc === "independent" };
1826
+ const why = packError(pack);
1827
+ if (why) return malformed(why, independent);
1828
+ const warnings = [];
1829
+ if (!serviceKeys) {
1830
+ if (!opts.trustEmbeddedKeys || byService) throw new KeysRequired("service keys are required: pass --keys <file|url>, or --trust-embedded-keys to use the keys inside the pack");
1831
+ warnings.push("service keys were taken from the pack itself (--trust-embedded-keys); this does not prove the service signed it");
1832
+ serviceKeys = pack.service_keys;
1833
+ }
1834
+ for (const [src, what] of [[serviceSrc, "service"], [agentSrc, "agent"]]) {
1835
+ if (src === "fetched over plain http") warnings.push(`${what} keys were fetched over plain http, which anyone on the network path can alter: not independent`);
1836
+ }
1837
+ let agentKeys;
1838
+ let notIndependent = false;
1839
+ if (opts.agentKeys && !byService) {
1840
+ agentKeys = opts.agentKeys.map((e) => {
1841
+ const held = pack.agent_keys.find((k) => k.agent === e.agent && k.key_id === e.key_id && k.public_key === e.public_key);
1842
+ return { agent: e.agent, key_id: e.key_id, public_key: e.public_key, valid_from: e.valid_from, valid_to: earliest(e.valid_to ?? null, held?.valid_to ?? null) };
1843
+ });
1844
+ } else {
1845
+ if (byService) {
1846
+ } else if (opts.trustEmbeddedAgentKeys) warnings.push("agent keys were taken from the pack itself (--trust-embedded-agent-keys); this does not prove the customer's agents signed it");
1847
+ else notIndependent = true;
1848
+ agentKeys = pack.agent_keys;
1849
+ }
1850
+ const v = new Verifier(serviceKeys, agentKeys, opts.contents ?? /* @__PURE__ */ new Map(), pack.project, pack.env, false, {}, opts.drand ?? null, {
1851
+ filter: pack.filter,
1852
+ exportHeads: pack.heads,
1853
+ clientHeads: opts.heads ?? null,
1854
+ timestamps: opts.timestamps ?? null,
1855
+ notIndependent,
1856
+ generated: pack.generated,
1857
+ exportSig: pack.export_sig,
1858
+ pack
1859
+ });
1860
+ const report = new Report(v.run(pack.records, pack.anchors), pack.project, pack.env, pack.records.length, warnings, [], independent, {
1861
+ generated: pack.generated,
1862
+ filter: pack.filter,
1863
+ sources: [serviceSrc, agentSrc],
1864
+ byService
1865
+ });
1866
+ report.projectName = typeof pack.project_name === "string" ? pack.project_name : null;
1867
+ report.coverage = v.coverage;
1868
+ return report;
1869
+ }
1870
+ function earliest(a, b) {
1871
+ if (a === null || b === null) return a ?? b;
1872
+ return ms(b) < ms(a) ? b : a;
1873
+ }
1874
+ function verifyLocalState(local, serviceKeys, drand = null, serviceKeysPlainHttp = false) {
1875
+ const v = new Verifier(serviceKeys, local.agentKeys, /* @__PURE__ */ new Map(), null, null, true, local.heads, drand);
1876
+ v.drafts = local.drafts ?? 0;
1877
+ v.localBases = local.bases ?? {};
1878
+ const checks = v.run(local.records, [], local.chainIds);
1879
+ const report = new Report(checks, null, null, local.records.length, [], [], { service_keys: !serviceKeysPlainHttp, agent_keys: true });
1880
+ report.nothingCosigned = v.nothingCosigned;
1881
+ report.coverage = v.coverage;
1882
+ return report;
1883
+ }
1884
+ var isU64 = (x) => typeof x === "number" && Number.isInteger(x) && x >= 0;
1885
+ var isHex2 = (x) => typeof x === "string" && HEX64.test(x);
1886
+ function signedHeadError(sh) {
1887
+ if (!exactKeys(sh, ["head", "head_sig"]) || typeof sh.head_sig !== "string") return "a signed head is {head, head_sig}";
1888
+ const h2 = sh.head;
1889
+ if (!exactKeys(h2, ["project", "env", "agent", "seq", "record_hash", "server_time"])) return "head fields are agent, env, project, record_hash, seq, server_time";
1890
+ if (!(typeof h2.project === "string" && (h2.env === "test" || h2.env === "live") && typeof h2.agent === "string" && AGENT_RE.test(h2.agent) && isU64(h2.seq) && isHex2(h2.record_hash) && validTime(h2.server_time))) {
1891
+ return "a head has an invalid value";
1892
+ }
1893
+ return "";
1894
+ }
1895
+ function packError(pack) {
1896
+ if (!pack || typeof pack !== "object" || Array.isArray(pack)) return "the pack is not a JSON object";
1897
+ if (pack.format !== "signafaide-pack/1") return `format is '${pack.format}', expected 'signafaide-pack/1'`;
1898
+ const fields = ["format", "generated", "project", "env", "service_keys", "agent_keys", "filter", "heads", "records", "anchors", "export_sig"];
1899
+ for (const k of fields) if (!(k in pack)) return `missing field \`${k}\``;
1900
+ const extra = Object.keys(pack).filter((k) => !fields.includes(k) && k !== "project_name").sort();
1901
+ if (extra.length) return `unknown field \`${extra[0]}\``;
1902
+ for (const k of ["service_keys", "agent_keys", "heads", "records", "anchors"]) if (!Array.isArray(pack[k])) return `\`${k}\` must be a list`;
1903
+ if (typeof pack.export_sig !== "string") return "`export_sig` must be a string";
1904
+ if (pack.env !== "test" && pack.env !== "live") return "env must be test or live";
1905
+ if (!validProjectId(pack.project)) return "project is not a valid project id";
1906
+ if ("project_name" in pack) {
1907
+ const n = pack.project_name;
1908
+ if (typeof n !== "string" || !n.length || [...n].length > 200 || /[\u0000-\u001f\u007f-\u009f]/.test(n)) return "project_name must be 1 to 200 characters, without control characters";
1909
+ }
1910
+ if (!validTime(pack.generated)) return "generated is not a time";
1911
+ for (const k of pack.service_keys) {
1912
+ const e = keyEntryErrors(k);
1913
+ if (e.length) return e[0];
1914
+ }
1915
+ for (const k of pack.agent_keys) {
1916
+ const e = keyEntryErrors(k, true);
1917
+ if (e.length) return e[0];
1918
+ }
1919
+ const f = pack.filter;
1920
+ if (!exactKeys(f, ["agents", "from", "to"])) return "filter is {agents, from, to}";
1921
+ for (const n of ["from", "to"]) if (f[n] !== null && !validTime(f[n])) return `filter.${n} is not a time`;
1922
+ if (f.agents !== null && !(Array.isArray(f.agents) && f.agents.every((a) => typeof a === "string" && AGENT_RE.test(a)))) return "filter.agents holds an invalid agent name";
1923
+ for (const sh of pack.heads) if (signedHeadError(sh)) return "heads holds an invalid head";
1924
+ return null;
1925
+ }
1926
+ function malformed(why, independent) {
1927
+ return new Report(
1928
+ CHECKS.map(([id, title], i) => ({
1929
+ n: i + 1,
1930
+ id,
1931
+ title,
1932
+ status: i === 0 ? "fail" : "not_checked",
1933
+ checked: 0,
1934
+ detail: i === 0 ? "the pack itself is malformed" : "pack unreadable",
1935
+ failures: i === 0 ? [{ item: "pack", message: why }] : []
1936
+ })),
1937
+ null,
1938
+ null,
1939
+ 0,
1940
+ [],
1941
+ [],
1942
+ independent
1943
+ );
1944
+ }
1945
+ var ms = (s) => validTime(s) ? Date.parse(s) : null;
1946
+ var fmt = (t) => new Date(t).toISOString();
1947
+ function active(key, at) {
1948
+ const s = ms(key.valid_from);
1949
+ const e = ms(key.valid_to);
1950
+ if (at === null || key.valid_from != null && s === null || key.valid_to != null && e === null) return false;
1951
+ return (s === null || s <= at) && (e === null || at < e);
1952
+ }
1953
+ function serviceKeysHash(keys, createdMs) {
1954
+ const list = keys.filter((k) => ms(k.valid_from) !== null && ms(k.valid_from) <= createdMs).map((k) => ({ key_id: k.key_id, public_key: k.public_key, valid_from: k.valid_from })).sort((a, b) => a.valid_from < b.valid_from ? -1 : a.valid_from > b.valid_from ? 1 : a.key_id < b.key_id ? -1 : a.key_id > b.key_id ? 1 : 0);
1955
+ return hashJson(list);
1956
+ }
1957
+ function checkRoundTime(serverMs, round) {
1958
+ if (!(round >= 1 && round <= MAX_ROUND)) return `drand round ${round} is out of range (1 to ${MAX_ROUND})`;
1959
+ const published = (QUICKNET_GENESIS + (round - 1) * 3) * 1e3;
1960
+ if (serverMs < published) return `server_time ${fmt(serverMs)} is before drand round ${round} was published at ${fmt(published)}`;
1961
+ if (serverMs - published > MAX_ROUND_AGE_MS) return `drand round ${round} is ${Math.floor((serverMs - published) / 1e3)} s older than server_time; at most ${MAX_ROUND_AGE_MS / 1e3} s is allowed`;
1962
+ return null;
1963
+ }
1964
+ var Acc = class {
1965
+ checked = 0;
1966
+ failures = [];
1967
+ notes = [];
1968
+ notChecked = false;
1969
+ pending = false;
1970
+ value;
1971
+ fail(item, message) {
1972
+ this.failures.push({ item, message });
1973
+ }
1974
+ };
1975
+ var Verifier = class {
1976
+ constructor(serviceKeys, agentKeys, contents, project, env, local, localHeads, drandVerifier, extras) {
1977
+ this.serviceKeys = serviceKeys;
1978
+ this.agentKeys = agentKeys;
1979
+ this.contents = contents;
1980
+ this.project = project;
1981
+ this.env = env;
1982
+ this.local = local;
1983
+ this.localHeads = localHeads;
1984
+ this.drandVerifier = drandVerifier;
1985
+ this.x = extras ?? { filter: { agents: null, from: null, to: null }, exportHeads: [], clientHeads: null, timestamps: null, notIndependent: false };
1986
+ this.generatedMs = this.x.generated ? ms(this.x.generated) : null;
1987
+ this.fromMs = this.x.filter.from !== null ? ms(this.x.filter.from) : null;
1988
+ this.toMs = this.x.filter.to !== null ? ms(this.x.filter.to) : null;
1989
+ for (const k of agentKeys) {
1990
+ const f = ms(k.valid_from);
1991
+ if (f !== null && typeof k.agent === "string") this.firstKey.set(k.agent, Math.min(this.firstKey.get(k.agent) ?? f, f));
1992
+ }
1993
+ }
1994
+ serviceKeys;
1995
+ agentKeys;
1996
+ contents;
1997
+ project;
1998
+ env;
1999
+ local;
2000
+ localHeads;
2001
+ drandVerifier;
2002
+ acc = CHECKS.map(() => new Acc());
2003
+ x;
2004
+ recs = [];
2005
+ generatedMs = null;
2006
+ fromMs = null;
2007
+ toMs = null;
2008
+ /** The earliest any key of each agent was valid: no record of that agent is older. */
2009
+ firstKey = /* @__PURE__ */ new Map();
2010
+ drafts = 0;
2011
+ nothingCosigned = false;
2012
+ /** Tool coverage per chain (§7), filled by `run`. */
2013
+ coverage = {};
2014
+ /** Local state: chain id -> the witness's signed head a chain continues after (§8). */
2015
+ localBases = {};
2016
+ /** A chain may start after seq 1 only when the export's from is after the agent's first key. */
2017
+ fromCuts(agent) {
2018
+ const k = this.firstKey.get(agent);
2019
+ return this.fromMs !== null && k !== void 0 && this.fromMs > k;
2020
+ }
2021
+ /** A head's record may be cut off by to only when it could be at or after to (its time is at
2022
+ * most 300 s after the head's server_time). */
2023
+ toMayCut(h2) {
2024
+ const s = ms(h2.server_time);
2025
+ return this.toMs !== null && (s === null || s + MAX_CLOCK_SKEW_MS >= this.toMs);
2026
+ }
2027
+ serviceKey(kid, at) {
2028
+ const k = this.serviceKeys.find((k2) => k2.key_id === kid);
2029
+ if (!k) throw new Error(`unknown service key ${kid}`);
2030
+ if (!active(k, ms(at))) throw new Error(`service key ${kid} was not active at ${at}`);
2031
+ return k;
2032
+ }
2033
+ anyKey(domain, h2, sig, at) {
2034
+ const t = ms(at);
2035
+ return this.serviceKeys.some((k) => active(k, t) && verifySig(k.public_key, domain, h2, sig));
2036
+ }
2037
+ chainOf = /* @__PURE__ */ new Map();
2038
+ /** The chain a record belongs to: its agent in a pack, `<project>/<env>/<agent>` locally. */
2039
+ chainId(r) {
2040
+ return this.chainOf.get(r) ?? r.envelope.agent;
2041
+ }
2042
+ headChain(head) {
2043
+ return this.local ? `${head.project}/${head.env}/${head.agent}` : head.agent;
2044
+ }
2045
+ run(raw, anchors, chainIds) {
2046
+ const a = this.acc;
2047
+ if (this.x.notIndependent) a[2].fail("agent keys", NOT_INDEPENDENT);
2048
+ if (!raw.length && !this.drafts) a[0].fail(this.local ? "local state" : "pack", this.local ? NO_LOCAL_RECORDS : NO_RECORDS);
2049
+ if (chainIds) raw.forEach((r, i) => this.chainOf.set(r, chainIds[i]));
2050
+ const recs = this.schema(raw);
2051
+ this.recs = recs;
2052
+ const shipped = recs.filter((x) => "receipt" in x.r);
2053
+ for (const { label, r, h: h2 } of shipped) {
2054
+ a[1].checked++;
2055
+ if (h2 !== r.receipt.record_hash) a[1].fail(label, `envelope hashes to ${h2}, receipt says ${r.receipt.record_hash}`);
2056
+ }
2057
+ if (shipped.length < recs.length) a[1].notes.push(`${recs.length - shipped.length} not yet shipped (no receipt)`);
2058
+ for (const x of recs) {
2059
+ a[2].checked++;
2060
+ const err2 = this.clientSig(x);
2061
+ if (err2) a[2].fail(x.label, err2);
2062
+ }
2063
+ const chains = this.chains(recs);
2064
+ this.merkle(shipped);
2065
+ const batches = this.batchSigs(shipped);
2066
+ this.batchChain(batches);
2067
+ this.heads(shipped, chains);
2068
+ this.drand(batches);
2069
+ this.anchors(anchors, batches);
2070
+ this.content(recs);
2071
+ if (this.local && !shipped.length && recs.length) {
2072
+ this.nothingCosigned = true;
2073
+ for (const i of [1, 4, 5, 6, 7]) {
2074
+ if (a[i].checked || a[i].failures.length) continue;
2075
+ a[i].notChecked = true;
2076
+ a[i].notes.push("nothing co-signed yet");
2077
+ }
2078
+ }
2079
+ return a.map((acc, i) => ({
2080
+ n: i + 1,
2081
+ id: CHECKS[i][0],
2082
+ title: CHECKS[i][1],
2083
+ status: acc.failures.length ? "fail" : acc.notChecked ? "not_checked" : acc.pending ? "pending" : "pass",
2084
+ checked: acc.checked,
2085
+ detail: plural(acc.checked, ...UNITS[i]) + acc.notes.map((n) => `; ${n}`).join(""),
2086
+ ...acc.value !== void 0 ? { value: acc.value } : {},
2087
+ failures: acc.failures
2088
+ }));
2089
+ }
2090
+ // 1
2091
+ schema(raw) {
2092
+ const out = [];
2093
+ raw.forEach((r, i) => {
2094
+ this.acc[0].checked++;
2095
+ const draft = this.local && !!r && typeof r === "object" && r.draft === true;
2096
+ let errs = draft ? draftErrors(r) : recordErrors(r);
2097
+ if (draft) {
2098
+ if (!errs.length && !this.chainId(r).startsWith("_pending/")) errs = [`a draft belongs in a pending chain, not ${this.chainId(r)}`];
2099
+ } else if (!errs.length && r && "receipt" in r) errs = receiptErrors(r.receipt).length ? receiptErrors(r.receipt) : this.consistent(r);
2100
+ else if (!errs.length && !this.local) errs = ["missing receipt"];
2101
+ else if (!errs.length) errs = this.envelopeBucket(r);
2102
+ if (errs.length) return this.acc[0].fail(`record[${i}]`, errs.join("; "));
2103
+ out.push({ label: `${this.chainId(r)}#${r.envelope.seq}`, r, h: hashJson(r.envelope) });
2104
+ });
2105
+ return out;
2106
+ }
2107
+ /** Locally, a record's envelope names the project and env of its chain. */
2108
+ envelopeBucket(r) {
2109
+ const e = r.envelope;
2110
+ const bucket = this.chainId(r).split("/").slice(0, 2).join("/");
2111
+ if (this.local && !bucket.startsWith("_pending/") && bucket !== `${e.project}/${e.env}`) return [`envelope is for ${e.project}/${e.env}, but the record is in chain ${this.chainId(r)}`];
2112
+ return [];
2113
+ }
2114
+ consistent(r) {
2115
+ const e = r.envelope;
2116
+ const { batch: b, head: h2 } = r.receipt;
2117
+ if (!this.local && (e.project !== this.project || e.env !== this.env)) return ["envelope belongs to another project or env"];
2118
+ if (!this.local && (b.project !== this.project || b.env !== this.env)) return ["receipt batch belongs to another project or env"];
2119
+ if (this.local && (e.project !== b.project || e.env !== b.env)) return ["envelope and receipt batch name different projects or envs"];
2120
+ const errs = this.envelopeBucket(r);
2121
+ if (errs.length) return errs;
2122
+ if (h2.project !== b.project || h2.env !== b.env || h2.agent !== e.agent) return ["receipt head belongs to another project, env or agent"];
2123
+ return [];
2124
+ }
2125
+ clientSig({ r, h: h2 }) {
2126
+ const env = r.envelope;
2127
+ const k = this.agentKeys.find((k2) => k2.agent === env.agent && k2.key_id === r.key_id);
2128
+ if (!k) return `no key ${r.key_id} for agent ${env.agent} in the agent keys`;
2129
+ try {
2130
+ if (keyId(b64urlDecode(k.public_key)) !== k.key_id) return "agent key_id does not match its public key";
2131
+ } catch {
2132
+ return "agent public key is not base64url";
2133
+ }
2134
+ if (!active(k, ms(env.time))) return `key ${k.key_id} was not active at ${env.time}`;
2135
+ if (k.valid_to != null && "receipt" in r) {
2136
+ const server = r.receipt.batch.server_time;
2137
+ if (ms(server) >= ms(k.valid_to)) return `key ${k.key_id} was revoked at ${k.valid_to}, before the service received this record at ${server}`;
2138
+ }
2139
+ if (!verifySig(k.public_key, r.draft === true && this.local ? "draft" : "record", h2, r.sig)) return "signature does not verify";
2140
+ return null;
2141
+ }
2142
+ // 4
2143
+ chains(recs) {
2144
+ const acc = this.acc[3];
2145
+ const chains = /* @__PURE__ */ new Map();
2146
+ for (const x of recs) {
2147
+ const id = this.chainId(x.r);
2148
+ const chain = chains.get(id) ?? /* @__PURE__ */ new Map();
2149
+ chains.set(id, chain);
2150
+ const seq = x.r.envelope.seq;
2151
+ const held = chain.get(seq);
2152
+ if (held && held.h !== x.h) {
2153
+ acc.fail(x.label, "two different records with the same seq (fork)");
2154
+ continue;
2155
+ }
2156
+ if (!held) chain.set(seq, x);
2157
+ }
2158
+ let edge = 0;
2159
+ const based = [];
2160
+ const starts = [];
2161
+ const clean = [];
2162
+ for (const agent of [...chains.keys()].sort()) {
2163
+ const chain = chains.get(agent);
2164
+ acc.checked += chain.size;
2165
+ let prev = null;
2166
+ for (const seq of [...chain.keys()].sort((x, y) => x - y)) {
2167
+ const x = chain.get(seq);
2168
+ const e = x.r.envelope;
2169
+ if (prev && prev.r.envelope.seq + 1 === seq) {
2170
+ if (e.prev !== prev.h) acc.fail(x.label, `prev does not equal the record_hash of seq ${seq - 1}`);
2171
+ if (ms(e.time) < ms(prev.r.envelope.time)) acc.fail(x.label, `time ${e.time} is earlier than the time of seq ${seq - 1} (${prev.r.envelope.time})`);
2172
+ } else if (prev) {
2173
+ const a = prev.r.envelope.seq + 1;
2174
+ acc.fail(x.label, this.local ? `the local chain is missing seq ${a}..${seq - 1}` : `the pack is missing seq ${a}..${seq - 1} of agent ${agent} (a gap inside the chain)`);
2175
+ } else if (seq > 1) {
2176
+ const base = this.local ? this.localBases[agent]?.head ?? {} : {};
2177
+ if (this.local && base.seq === seq - 1 && base.record_hash === e.prev) based.push(`${agent} after seq ${seq - 1}`);
2178
+ else if (this.local) acc.fail(x.label, `the local chain is missing seq 1..${seq - 1}`);
2179
+ else if (this.fromCuts(agent)) edge++;
2180
+ else if (this.x.filter.from !== null) acc.fail(x.label, `the pack is missing seq 1..${seq - 1} of agent ${agent}; its export's from filter is not after the agent's first key, so they belong in it`);
2181
+ else acc.fail(x.label, `the pack is missing seq 1..${seq - 1} of agent ${agent} and its export declares no from filter`);
2182
+ }
2183
+ if (e.kind === "note" && e.name === INSTANCE_START) {
2184
+ const p = prev && prev.r.envelope.seq + 1 === seq ? prev.r.envelope : null;
2185
+ const base = this.local && !prev ? this.localBases[agent] : void 0;
2186
+ const afterStop = p ? p.kind === "note" && p.name === INSTANCE_STOP : !!base?.stop && base.head?.seq === seq - 1 && e.prev === base.head?.record_hash && isStopNote(base.stop, e.agent, base.head.record_hash);
2187
+ (afterStop ? clean : starts).push(`${agent} after seq ${seq - 1}`);
2188
+ }
2189
+ if ("receipt" in x.r) {
2190
+ const server = x.r.receipt.batch.server_time;
2191
+ if (ms(e.time) > ms(server) + MAX_CLOCK_SKEW_MS) acc.fail(x.label, `time ${e.time} is more than 300 s after its batch's server_time ${server}`);
2192
+ }
2193
+ prev = x;
2194
+ }
2195
+ }
2196
+ if (edge) acc.notes.push(`${plural(edge, "chain")} start after seq 1, as the export's from filter allows`);
2197
+ if (starts.length) acc.notes.push(`${INSTANCE_NOTE}: ${starts.join(", ")}`);
2198
+ if (clean.length) acc.notes.push(`${CLEAN_RESTART_NOTE}: ${clean.join(", ")}`);
2199
+ if (based.length) acc.notes.push(`continues after a co-signed record this machine does not hold: ${based.join(", ")} (the witness's signed head, see signed heads)`);
2200
+ for (const agent of [...chains.keys()].sort()) {
2201
+ const chain = chains.get(agent);
2202
+ const seqs = [...chain.keys()].sort((x, y) => x - y);
2203
+ this.coverage[agent] = coverage(seqs.map((seq) => ({ seq, kind: chain.get(seq).r.envelope.kind, name: chain.get(seq).r.envelope.name })));
2204
+ }
2205
+ return chains;
2206
+ }
2207
+ // 5
2208
+ merkle(shipped) {
2209
+ for (const { label, r, h: h2 } of shipped) {
2210
+ this.acc[4].checked++;
2211
+ const rc = r.receipt;
2212
+ if (!verifyInclusion(h2, rc.index, rc.batch.size, rc.proof, rc.batch.root)) this.acc[4].fail(label, `proof does not lead to the root of batch ${rc.batch.seq}`);
2213
+ }
2214
+ }
2215
+ // 6
2216
+ batchSigs(shipped) {
2217
+ const acc = this.acc[5];
2218
+ const batches = /* @__PURE__ */ new Map();
2219
+ const seen = /* @__PURE__ */ new Set();
2220
+ for (const { r } of shipped) {
2221
+ const rc = r.receipt;
2222
+ const computed = hashJson(rc.batch);
2223
+ const key = [computed, rc.batch_hash, rc.batch_sig, rc.service_key_id].join("\n");
2224
+ if (seen.has(key)) continue;
2225
+ seen.add(key);
2226
+ acc.checked++;
2227
+ const item = `batch ${rc.batch.seq}`;
2228
+ if (computed !== rc.batch_hash) acc.fail(item, `batch hashes to ${computed}, receipt says ${rc.batch_hash}`);
2229
+ try {
2230
+ const k = this.serviceKey(rc.service_key_id, rc.batch.server_time);
2231
+ if (!verifySig(k.public_key, "batch", computed, rc.batch_sig)) throw new Error("signature does not verify");
2232
+ } catch (e) {
2233
+ acc.fail(item, `batch_sig: ${e.message}`);
2234
+ }
2235
+ batches.set(`${String(rc.batch.seq).padStart(16, "0")}
2236
+ ${computed}`, rc);
2237
+ }
2238
+ return batches;
2239
+ }
2240
+ // 7
2241
+ batchChain(batches) {
2242
+ const acc = this.acc[6];
2243
+ const bySeq = /* @__PURE__ */ new Map();
2244
+ for (const key of [...batches.keys()].sort()) {
2245
+ acc.checked++;
2246
+ const rc = batches.get(key);
2247
+ const b = rc.batch;
2248
+ const h2 = key.split("\n")[1];
2249
+ const id = `${b.project}
2250
+ ${b.env}
2251
+ ${String(b.seq).padStart(16, "0")}`;
2252
+ const held = bySeq.get(id);
2253
+ if (held) {
2254
+ if (held.h !== h2) acc.fail(`batch ${b.seq}`, "two different batches with the same seq (fork)");
2255
+ continue;
2256
+ }
2257
+ bySeq.set(id, { h: h2, b });
2258
+ }
2259
+ let gaps = 0;
2260
+ let prev = null;
2261
+ for (const id of [...bySeq.keys()].sort()) {
2262
+ const { h: h2, b } = bySeq.get(id);
2263
+ const chain = `${b.project}
2264
+ ${b.env}`;
2265
+ if (b.seq === 1 !== (b.prev === null)) acc.fail(`batch ${b.seq}`, "prev must be null exactly when seq is 1");
2266
+ if (prev && prev.chain === chain) {
2267
+ if (prev.seq + 1 === b.seq && b.prev !== prev.h) acc.fail(`batch ${b.seq}`, `prev does not equal the batch_hash of batch ${prev.seq}`);
2268
+ else if (prev.seq + 1 !== b.seq) gaps++;
2269
+ if (ms(b.server_time) < ms(prev.b.server_time)) {
2270
+ acc.fail(`batch ${b.seq}`, `server_time ${b.server_time} is earlier than the server_time of batch ${prev.seq} (${prev.b.server_time})`);
2271
+ }
2272
+ }
2273
+ prev = { chain, seq: b.seq, h: h2, b };
2274
+ }
2275
+ if (gaps) acc.notes.push(`${plural(gaps, "gap")} between batches held in the pack`);
2276
+ }
2277
+ // 8
2278
+ heads(shipped, chains) {
2279
+ const acc = this.acc[7];
2280
+ const seen = /* @__PURE__ */ new Set();
2281
+ const held = (chain, seq) => chains.get(chain)?.get(seq)?.h;
2282
+ const lastHeld = (chain) => Math.max(0, ...chains.get(chain)?.keys() ?? []);
2283
+ for (const { r } of shipped) {
2284
+ const rc = r.receipt;
2285
+ const item = `head ${rc.head.agent}#${rc.head.seq}`;
2286
+ const hh = hashJson(rc.head);
2287
+ if (!seen.has(`${hh}
2288
+ ${rc.head_sig}`)) {
2289
+ seen.add(`${hh}
2290
+ ${rc.head_sig}`);
2291
+ try {
2292
+ const k = this.serviceKey(rc.service_key_id, rc.head.server_time);
2293
+ if (!verifySig(k.public_key, "head", hh, rc.head_sig)) throw new Error("signature does not verify");
2294
+ } catch (e) {
2295
+ acc.fail(item, `head_sig: ${e.message}`);
2296
+ }
2297
+ const x = held(this.headChain(rc.head), rc.head.seq);
2298
+ if (x && x !== rc.head.record_hash) acc.fail(item, "head names a different record than the pack holds at that seq");
2299
+ }
2300
+ if (rc.head.seq < r.envelope.seq) acc.fail(item, `head is older than record seq ${r.envelope.seq} it was issued with`);
2301
+ }
2302
+ let inFilter = (_a2) => true;
2303
+ if (!this.local) {
2304
+ try {
2305
+ const sh = hashJson(exportStatement(this.x.pack));
2306
+ if (!this.anyKey("export", sh, this.x.exportSig ?? "", this.x.generated)) {
2307
+ acc.fail("export statement", "export_sig does not verify with any service key active at generated: the pack's filter, heads, generated time or records were changed after the service signed it");
2308
+ }
2309
+ } catch (e) {
2310
+ acc.fail("export statement", e.message);
2311
+ }
2312
+ const agents = this.x.filter.agents;
2313
+ inFilter = (a) => agents === null || agents.includes(a);
2314
+ for (const { label, r } of this.recs) {
2315
+ const e = r.envelope;
2316
+ const t = ms(e.time);
2317
+ if (!inFilter(e.agent)) acc.fail(label, "the record's agent is not in the export's filter");
2318
+ else if (this.fromMs !== null && t < this.fromMs) acc.fail(label, `time ${e.time} is before the export's from filter`);
2319
+ else if (this.toMs !== null && t >= this.toMs) acc.fail(label, `time ${e.time} is not before the export's to filter`);
2320
+ }
2321
+ }
2322
+ const exp = /* @__PURE__ */ new Map();
2323
+ for (const sh of this.x.exportHeads) {
2324
+ const h2 = sh.head;
2325
+ const item = `export head ${h2.agent}#${h2.seq}`;
2326
+ if (h2.project !== this.project || h2.env !== this.env) {
2327
+ acc.fail(item, "the export head belongs to another project or env");
2328
+ continue;
2329
+ }
2330
+ const hh = hashJson(h2);
2331
+ if (!seen.has(`${hh}
2332
+ ${sh.head_sig}`)) {
2333
+ seen.add(`${hh}
2334
+ ${sh.head_sig}`);
2335
+ if (!this.anyKey("head", hh, sh.head_sig, h2.server_time)) acc.fail(item, "head_sig does not verify with any service key active at its server_time");
2336
+ }
2337
+ const sMs = ms(h2.server_time);
2338
+ if (sMs === null || this.generatedMs === null || sMs > this.generatedMs) acc.fail(item, `the export head's server_time ${h2.server_time} is after the pack was generated (${this.x.generated})`);
2339
+ if (!inFilter(h2.agent)) acc.fail(item, "the export head is for an agent outside the export's filter");
2340
+ const x = held(h2.agent, h2.seq);
2341
+ if (x && x !== h2.record_hash) acc.fail(item, "the export head names a different record than the pack holds at that seq");
2342
+ if (exp.has(h2.agent)) acc.fail(item, "two export heads for the same agent");
2343
+ else exp.set(h2.agent, h2);
2344
+ }
2345
+ if (!this.local) {
2346
+ for (const agent of [...chains.keys()].sort()) {
2347
+ const have = lastHeld(agent);
2348
+ const h2 = exp.get(agent);
2349
+ if (!h2) acc.fail(`agent ${agent}`, "the pack has no export head for this agent, so records cut off at the end cannot be detected");
2350
+ else if (have > h2.seq) acc.fail(`export head ${agent}#${h2.seq}`, `the pack holds seq ${have}, beyond the export head seq ${h2.seq}`);
2351
+ else if (have < h2.seq && !this.toMayCut(h2)) acc.fail(`export head ${agent}#${h2.seq}`, `the pack ends at seq ${have} but the service's head at export was seq ${h2.seq} (records missing at the end)`);
2352
+ }
2353
+ for (const agent of [...exp.keys()].sort()) if (!chains.has(agent)) acc.fail(`export head ${agent}#${exp.get(agent).seq}`, "the pack holds none of this agent's records, but the export names its head");
2354
+ }
2355
+ let newer = 0;
2356
+ for (const sh of this.x.clientHeads ?? []) {
2357
+ const h2 = sh.head;
2358
+ if (h2.project !== this.project || h2.env !== this.env) continue;
2359
+ if (!inFilter(h2.agent)) continue;
2360
+ const item = `head file ${h2.agent}#${h2.seq}`;
2361
+ const hh = hashJson(h2);
2362
+ if (!seen.has(`${hh}
2363
+ ${sh.head_sig}`)) {
2364
+ seen.add(`${hh}
2365
+ ${sh.head_sig}`);
2366
+ if (!this.anyKey("head", hh, sh.head_sig, h2.server_time)) acc.fail(item, "head_sig does not verify with any service key active at its server_time");
2367
+ }
2368
+ const x = held(h2.agent, h2.seq);
2369
+ if (x && x !== h2.record_hash) acc.fail(item, "the client's head names a different record than the pack holds at that seq");
2370
+ const sMs = ms(h2.server_time);
2371
+ if (sMs === null || this.generatedMs === null || sMs > this.generatedMs) {
2372
+ newer++;
2373
+ continue;
2374
+ }
2375
+ const have = lastHeld(h2.agent);
2376
+ const applies = (have > 0 || !this.fromCuts(h2.agent)) && !this.toMayCut(h2);
2377
+ if (applies && have < h2.seq) acc.fail(item, `the client's head is seq ${h2.seq} but the pack ends at seq ${have} (records missing at the end)`);
2378
+ }
2379
+ if (newer) acc.notes.push(`${plural(newer, "client head")} newer than the export, not applied`);
2380
+ for (const id of Object.keys(this.localBases).sort()) {
2381
+ const sh = this.localBases[id] ?? {};
2382
+ const head = sh.head ?? {};
2383
+ const hh = hashJson(head);
2384
+ const sig = sh.head_sig ?? "";
2385
+ if (this.serviceKeys.length && !seen.has(`${hh}
2386
+ ${sig}`)) {
2387
+ seen.add(`${hh}
2388
+ ${sig}`);
2389
+ if (!this.anyKey("head", hh, sig, head.server_time)) acc.fail(`base ${id}#${head.seq}`, "head_sig: signature does not verify");
2390
+ }
2391
+ }
2392
+ for (const id of Object.keys(this.localHeads).sort()) {
2393
+ const sh = this.localHeads[id] ?? {};
2394
+ const head = sh.head ?? {};
2395
+ const item = `head.json ${id}#${head.seq}`;
2396
+ const hh = hashJson(head);
2397
+ const sig = sh.head_sig ?? "";
2398
+ if (!seen.has(`${hh}
2399
+ ${sig}`)) {
2400
+ seen.add(`${hh}
2401
+ ${sig}`);
2402
+ if (!this.anyKey("head", hh, sig, head.server_time)) acc.fail(item, "head_sig: signature does not verify");
2403
+ const x = held(id, head.seq);
2404
+ if (x && x !== head.record_hash) acc.fail(item, "head names a different record than the pack holds at that seq");
2405
+ }
2406
+ const have = lastHeld(id);
2407
+ if (have < (head.seq ?? 0)) acc.fail(item, `the service holds seq ${head.seq} but the local chain ends at seq ${have} (truncated)`);
2408
+ }
2409
+ acc.checked = seen.size;
2410
+ }
2411
+ // 9
2412
+ drand(batches) {
2413
+ const acc = this.acc[8];
2414
+ let nulls = 0;
2415
+ const rounds = /* @__PURE__ */ new Set();
2416
+ for (const rc of batches.values()) {
2417
+ const d = rc.batch.drand;
2418
+ if (d === null) {
2419
+ nulls++;
2420
+ continue;
2421
+ }
2422
+ const item = `batch ${rc.batch.seq} round ${d.round}`;
2423
+ const why = checkRoundTime(ms(rc.batch.server_time) ?? 0, d.round);
2424
+ if (why) acc.fail(item, why);
2425
+ if (rounds.has(`${d.round}
2426
+ ${d.signature}`)) continue;
2427
+ rounds.add(`${d.round}
2428
+ ${d.signature}`);
2429
+ if (d.chain !== QUICKNET_CHAIN) acc.fail(item, `unknown drand chain ${d.chain}`);
2430
+ else if (!/^([0-9a-f]{2})+$/.test(d.signature)) acc.fail(item, "signature is not hex");
2431
+ else if (this.drandVerifier && !this.drandVerifier(d)) acc.fail(item, `BLS signature does not verify for round ${d.round}`);
2432
+ else if (sha256Hex((0, import_utils4.hexToBytes)(d.signature)) !== d.randomness) acc.fail(item, "randomness is not sha256(signature)");
2433
+ }
2434
+ acc.checked = rounds.size;
2435
+ if (nulls) acc.notes.push(`${plural(nulls, "batch", "batches")} without a drand round (beacon unreachable when written)`);
2436
+ if (!rounds.size || !this.drandVerifier) {
2437
+ acc.notChecked = true;
2438
+ if (rounds.size) acc.notes.push(DRAND_NOT_CHECKED.split(": ")[1]);
2439
+ }
2440
+ }
2441
+ // 10, 11
2442
+ anchors(anchors, batches) {
2443
+ const [a10, a11] = [this.acc[9], this.acc[10]];
2444
+ const batchTimes = /* @__PURE__ */ new Map();
2445
+ for (const [k, rc] of batches) batchTimes.set(k.split("\n")[1], [rc.batch.seq, rc.batch.server_time]);
2446
+ const anchored = /* @__PURE__ */ new Set();
2447
+ const bySeq = /* @__PURE__ */ new Map();
2448
+ const proofs = [];
2449
+ anchors.forEach((entry, i) => {
2450
+ a10.checked++;
2451
+ const errs = anchorError(entry);
2452
+ if (errs) return a10.fail(`anchors[${i}]`, errs);
2453
+ const a = entry.anchor;
2454
+ const item = `anchor ${a.seq}`;
2455
+ const { ots: _o, status: _s, bitcoin_block: _b, ...core } = a;
2456
+ const ah = hashJson(core);
2457
+ const created = ms(a.created);
2458
+ if (!this.anyKey("anchor", ah, entry.anchor_sig, a.created)) a10.fail(item, "anchor_sig does not verify with any service key active at its creation");
2459
+ if (serviceKeysHash(this.serviceKeys, created) !== a.keys_hash) a10.fail(item, "keys_hash does not match the service keys supplied: the key list was changed after this anchor was signed");
2460
+ for (const [bh, p] of Object.entries(entry.paths)) {
2461
+ if (verifyInclusion(bh, p.index, a.size, p.path, a.root)) {
2462
+ anchored.add(bh);
2463
+ const bt = batchTimes.get(bh);
2464
+ if (bt && created < ms(bt[1])) a10.fail(item, `created ${a.created} is before the server_time of batch ${bt[0]} (${bt[1]}) it covers`);
2465
+ } else a10.fail(item, `path for batch ${bh} does not lead to the anchor root`);
2466
+ }
2467
+ const held = bySeq.get(a.seq);
2468
+ if (held) {
2469
+ if (held.ah !== ah) a10.fail(item, "two different signed anchors with the same seq");
2470
+ } else bySeq.set(a.seq, { ah, a });
2471
+ a11.checked++;
2472
+ try {
2473
+ proofs.push(otsCheck(a, ah, this.x.timestamps));
2474
+ } catch (e) {
2475
+ a11.fail(item, e.message);
2476
+ }
2477
+ });
2478
+ let prev = null;
2479
+ for (const seq of [...bySeq.keys()].sort((x, y) => x - y)) {
2480
+ const { ah, a } = bySeq.get(seq);
2481
+ if (prev && prev.seq + 1 === seq && a.prev !== prev.ah) a10.fail(`anchor ${seq}`, `prev does not equal the anchor_hash of anchor ${prev.seq}`);
2482
+ prev = { seq, ah };
2483
+ }
2484
+ let stale = 0;
2485
+ let young = 0;
2486
+ const refMs = this.local ? Date.now() : this.generatedMs;
2487
+ for (const [k, rc] of batches) {
2488
+ if (anchored.has(k.split("\n")[1])) continue;
2489
+ const t = ms(rc.batch.server_time);
2490
+ if (t === null || refMs === null || t + ANCHOR_WINDOW_MS < refMs) stale++;
2491
+ else young++;
2492
+ }
2493
+ if (stale) {
2494
+ a10.notChecked = true;
2495
+ a10.notes.push(
2496
+ this.local ? `${plural(stale, "batch", "batches")} older than the 2-hour anchor window: local state holds no anchors, so verify an export (\`signafaide export\`) to check them` : `${plural(stale, "batch", "batches")} not timestamped: older than the 2-hour anchor window, and no anchor in the pack covers them`
2497
+ );
2498
+ }
2499
+ if (!anchors.length && !stale && !(this.local && young)) {
2500
+ if (this.local) a10.notChecked = true;
2501
+ else a10.pending = true;
2502
+ a10.notes.push(this.local ? "local state holds no anchors" : "no anchors in the pack yet (an anchor is made every hour)");
2503
+ } else if (young) {
2504
+ a10.pending = true;
2505
+ a10.notes.push(`${plural(young, "batch", "batches")} not yet anchored`);
2506
+ }
2507
+ const unchecked = proofs.filter((p) => p[0] === "unchecked");
2508
+ if (!proofs.length) {
2509
+ if (a10.pending && !a10.notChecked) {
2510
+ a11.pending = true;
2511
+ a11.notes.push("waiting for the anchor");
2512
+ } else a11.notChecked = true;
2513
+ } else if (unchecked.length) {
2514
+ a11.notChecked = true;
2515
+ a11.notes.push(`public timestamp not checked (${unchecked[0][2]}); claims block ${unchecked.map((p) => p[1]).join(", ")}`);
2516
+ } else if (proofs.every((p) => p[0] === "confirmed")) {
2517
+ const h2 = Math.max(...proofs.map((p) => p[1]));
2518
+ a11.value = `confirmed@${h2}`;
2519
+ a11.notes.push(`each public timestamp checked against its public record (block ${h2})`);
2520
+ } else {
2521
+ a11.value = "pending";
2522
+ a11.pending = true;
2523
+ a11.notes.push(`waiting for the public timestamp to confirm (usually within a few hours): ${proofs.filter((p) => p[0] === "pending").length} of ${proofs.length}`);
2524
+ }
2525
+ }
2526
+ // 12
2527
+ content(recs) {
2528
+ const acc = this.acc[11];
2529
+ for (const { label, r, h: h2 } of recs) {
2530
+ let c = r.content;
2531
+ let salt = r.salt;
2532
+ const local = this.contents.get(h2);
2533
+ if (c === void 0 && salt === void 0 && local) ({ content: c, salt } = local);
2534
+ if (c === void 0 && salt === void 0) continue;
2535
+ acc.checked++;
2536
+ let ch;
2537
+ try {
2538
+ ch = checkContent(c, salt, r.envelope.content_hash);
2539
+ } catch (e) {
2540
+ acc.fail(label, e.message);
2541
+ continue;
2542
+ }
2543
+ if (ch !== null) acc.fail(label, `content hashes to ${ch}, envelope says ${r.envelope.content_hash}`);
2544
+ }
2545
+ if (!acc.checked) {
2546
+ acc.notChecked = true;
2547
+ acc.notes.push("no content supplied (hash-only records)");
2548
+ }
2549
+ }
2550
+ };
2551
+ var exactKeys = (o, keys) => !!o && typeof o === "object" && !Array.isArray(o) && Object.keys(o).length === keys.length && keys.every((k) => k in o);
2552
+ function anchorError(entry) {
2553
+ if (!exactKeys(entry, ["anchor", "anchor_sig", "paths"])) return "an anchor entry has exactly anchor, anchor_sig and paths";
2554
+ const a = entry.anchor;
2555
+ if (!exactKeys(a, ["v", "seq", "prev", "root", "size", "created", "keys_hash", "ots", "status", "bitcoin_block"])) {
2556
+ return "anchor fields are v, seq, prev, root, size, created, keys_hash, ots, status, bitcoin_block";
2557
+ }
2558
+ if (a.v !== 1 || !isU64(a.seq) || !isU64(a.size) || !validTime(a.created)) return "anchor v, seq, size or created is invalid";
2559
+ if (!(isHex2(a.root) && isHex2(a.keys_hash) && (a.prev === null || isHex2(a.prev)))) return "anchor root, keys_hash or prev is not a hash";
2560
+ if (typeof a.ots !== "string" || typeof entry.anchor_sig !== "string" || !(a.bitcoin_block === null || isU64(a.bitcoin_block))) return "anchor ots, anchor_sig or bitcoin_block is invalid";
2561
+ if (a.status !== "pending" && a.status !== "confirmed" || !entry.paths || typeof entry.paths !== "object" || Array.isArray(entry.paths)) return "anchor status or paths is invalid";
2562
+ if (!Object.entries(entry.paths).every(([k, p]) => isHex2(k) && exactKeys(p, ["index", "path"]) && isU64(p.index) && Array.isArray(p.path) && p.path.every(isHex2))) {
2563
+ return 'each anchor path is {"index": n, "path": [...]}';
2564
+ }
2565
+ return "";
2566
+ }
2567
+ function otsCheck(a, anchorHash, headers) {
2568
+ const data = base64Decode(a.ots);
2569
+ const { digest: digest2, height } = otsStatus(data);
2570
+ if (digest2 !== anchorHash) throw new Error("OTS proof is for a different digest than the anchor hash");
2571
+ if (a.status === "confirmed" && height === null) throw new Error("anchor says confirmed but the proof is still pending");
2572
+ if (a.status === "confirmed" && a.bitcoin_block !== null && height !== a.bitcoin_block) throw new Error(`anchor says block ${a.bitcoin_block} but the proof attests block ${height}`);
2573
+ if (height === null) return ["pending"];
2574
+ const attested = blockAttestations(data);
2575
+ if (!headers) return ["unchecked", height, "offline"];
2576
+ const roots = /* @__PURE__ */ new Map();
2577
+ for (const [h2, got] of headers) {
2578
+ if (!got[0]) roots.set(h2, [false, got[1]]);
2579
+ else
2580
+ try {
2581
+ roots.set(h2, [true, checkHeader(got[1].hash, got[1].header)]);
2582
+ } catch (e) {
2583
+ roots.set(h2, [false, e.message]);
2584
+ }
2585
+ }
2586
+ for (const [h2, msg] of attested) {
2587
+ const r = roots.get(h2);
2588
+ if (r && r[0] && r[1] !== msg) throw new Error(`the timestamp claims block ${h2}, but its Merkle root does not match that block's header`);
2589
+ }
2590
+ for (const [h2] of attested) {
2591
+ const r = roots.get(h2);
2592
+ if (!r) return ["unchecked", height, `block ${h2} was not looked up`];
2593
+ if (!r[0]) return ["unchecked", height, r[1]];
2594
+ }
2595
+ return ["confirmed", height];
2596
+ }
2597
+ async function loadDrandVerifier() {
2598
+ let bls;
2599
+ try {
2600
+ bls = (await import("@noble/curves/bls12-381")).bls12_381;
2601
+ } catch {
2602
+ return null;
2603
+ }
2604
+ const { sha256: sha2564 } = await import("@noble/hashes/sha2");
2605
+ const DST = "BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_";
2606
+ return (d) => {
2607
+ try {
2608
+ const msg = new Uint8Array(8);
2609
+ new DataView(msg.buffer).setBigUint64(0, BigInt(d.round));
2610
+ return bls.shortSignatures.verify(d.signature, bls.shortSignatures.hash(sha2564(msg), DST), QUICKNET_PUBLIC_KEY);
2611
+ } catch {
2612
+ return false;
2613
+ }
2614
+ };
2615
+ }
2616
+ // Annotate the CommonJS export names for ESM import in node:
2617
+ 0 && (module.exports = {
2618
+ AGENT_RE,
2619
+ ANCHOR_WINDOW_MS,
2620
+ CHECKS,
2621
+ Client,
2622
+ DEFAULT_URL,
2623
+ HEADER,
2624
+ JsonNumber,
2625
+ KeysRequired,
2626
+ MAX_DECLARED_NAME,
2627
+ MAX_NODES,
2628
+ MAX_PROOF_BYTES,
2629
+ MAX_ROUND,
2630
+ NOT_INDEPENDENT,
2631
+ Report,
2632
+ SignafaideError,
2633
+ SigningKey,
2634
+ TOOLS,
2635
+ TOOL_PREFIX,
2636
+ TOOL_UNRECORDED_PREFIX,
2637
+ VERSION,
2638
+ agentName,
2639
+ assertPortableNumbers,
2640
+ auditPath,
2641
+ b64url,
2642
+ b64urlDecode,
2643
+ blockAttestations,
2644
+ canonicalBytes,
2645
+ canonicalize,
2646
+ checkContent,
2647
+ checkHeader,
2648
+ checkRoundTime,
2649
+ clampTime,
2650
+ clientHeader,
2651
+ contentFromText,
2652
+ contentHash,
2653
+ countsText,
2654
+ coverage,
2655
+ coverageLines,
2656
+ declaredName,
2657
+ detectAgent,
2658
+ draftErrors,
2659
+ envelopeErrors,
2660
+ exportStatement,
2661
+ fetchJson,
2662
+ hashJson,
2663
+ isLoopbackUrl,
2664
+ keyEntryErrors,
2665
+ keyId,
2666
+ leafHash,
2667
+ loadDrandVerifier,
2668
+ makeDraft,
2669
+ makeRecord,
2670
+ manifestEntry,
2671
+ manifestErrors,
2672
+ merkleRoot,
2673
+ newSalt,
2674
+ now,
2675
+ otsStatus,
2676
+ parseAgentKeys,
2677
+ parseHeadFile,
2678
+ parseJson,
2679
+ parseOts,
2680
+ parseServiceKeys,
2681
+ receiptErrors,
2682
+ recordErrors,
2683
+ recordHash,
2684
+ recordsHash,
2685
+ registration,
2686
+ resultLine,
2687
+ rootsFromVector,
2688
+ scopeText,
2689
+ serviceKeysHash,
2690
+ sha256Hex,
2691
+ signDraft,
2692
+ signedMessage,
2693
+ target,
2694
+ telemetryEnabled,
2695
+ timestampHeights,
2696
+ toolNote,
2697
+ toolNoteName,
2698
+ validProjectId,
2699
+ validTime,
2700
+ verifyInclusion,
2701
+ verifyLocalState,
2702
+ verifyPack,
2703
+ verifySig,
2704
+ wireRecord,
2705
+ withWallTime
2706
+ });
2707
+ /*! Bundled license information:
2708
+
2709
+ @noble/ed25519/index.js:
2710
+ (*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) *)
2711
+ */