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.
@@ -0,0 +1,652 @@
1
+ /**
2
+ * RFC 8785 JSON Canonicalization Scheme (JCS).
3
+ *
4
+ * Numbers use ECMAScript Number serialization, object keys are sorted by UTF-16
5
+ * code units, strings are escaped like JSON.stringify. Lone surrogates, NaN and
6
+ * Infinity are rejected. Object properties whose value is `undefined` are skipped,
7
+ * as in JSON. Values the SDK signs itself also pass `assertPortableNumbers` (SPEC §1).
8
+ */
9
+ /**
10
+ * A number literal that a plain JS number would misrepresent, from {@link parseJson}:
11
+ * an integer literal beyond ±(2^53−1) (JCS rejects it, as Rust and Python do), or a float
12
+ * literal with an integral value such as `1.0` (JCS writes the double; schema checks
13
+ * that need an integer reject it, as Rust and Python do).
14
+ */
15
+ declare class JsonNumber {
16
+ readonly text: string;
17
+ readonly value: number;
18
+ readonly unsafeInteger: boolean;
19
+ constructor(text: string, value: number, unsafeInteger: boolean);
20
+ toJSON(): number;
21
+ }
22
+ declare function canonicalize(value: unknown): string;
23
+ declare function canonicalBytes(value: unknown): Uint8Array;
24
+ /**
25
+ * SPEC §1: JavaScript cannot see literals, so content it canonicalizes itself must not
26
+ * hold integer-valued numbers with 2^53 <= |x| < 1e21 (JSON.stringify writes them as
27
+ * integer literals, which other implementations reject). Throws on the first one.
28
+ */
29
+ declare function assertPortableNumbers(value: unknown): void;
30
+ /**
31
+ * Parse JSON keeping what each number literal was (SPEC §1): integer literals beyond
32
+ * ±(2^53−1) and integral float literals become {@link JsonNumber}; every other number is a
33
+ * plain number. Verifiers parse packs with this, so they judge literals like Rust and Python.
34
+ * NaN, Infinity and trailing text are rejected, as in JSON.parse.
35
+ */
36
+ declare function parseJson(text: string): any;
37
+
38
+ /** Hashes, domain-separated Ed25519 and RFC 6962 Merkle proofs (SPEC §1, §3.2). */
39
+ declare const DOMAINS: readonly ["record", "batch", "head", "anchor", "register", "rotate", "agent_key", "export", "draft"];
40
+ type Domain = (typeof DOMAINS)[number];
41
+ declare function sha256Hex(data: Uint8Array): string;
42
+ /** sha256(JCS(value)) as 64 lowercase hex. */
43
+ declare function hashJson(value: unknown): string;
44
+ /** A fresh content salt (SPEC §2.1): 16 random bytes as 32 lowercase hex. */
45
+ declare function newSalt(): string;
46
+ /** sha256(salt || JCS(content)) as 64 lowercase hex (SPEC §2.1); content is a JSON object. */
47
+ declare function contentHash(content: unknown, salt: string): string;
48
+ /** The §2.3 content rule: `content` and `salt` together or not at all. Returns null when they
49
+ * match `expected` (or are both absent); throws when they cannot be checked; returns the actual
50
+ * hash on a mismatch. */
51
+ declare function checkContent(content: unknown, salt: unknown, expected: string): string | null;
52
+ declare function b64url(data: Uint8Array): string;
53
+ declare function b64urlDecode(s: string): Uint8Array;
54
+ /** First 16 hex characters of SHA-256 of the raw 32-byte public key. */
55
+ declare function keyId(publicKey: Uint8Array): string;
56
+ declare function signedMessage(domain: Domain, hashHex: string): Uint8Array;
57
+ /** An Ed25519 private key. Only `sign` touches the private half. */
58
+ declare class SigningKey {
59
+ #private;
60
+ readonly publicKey: Uint8Array;
61
+ readonly keyId: string;
62
+ constructor(seed: Uint8Array);
63
+ static generate(random?: (n: number) => Uint8Array): SigningKey;
64
+ seed(): Uint8Array;
65
+ /** base64url Ed25519 signature over `signafaide/v1/<domain>\n` + raw hash. */
66
+ sign(domain: Domain, hashHex: string): string;
67
+ }
68
+ declare function verifySig(publicKeyB64: string, domain: Domain, hashHex: string, sigB64: string): boolean;
69
+ declare function leafHash(hashHex: string): Uint8Array;
70
+ /** Root of the RFC 6962 tree over `hashes` (hex). */
71
+ declare function merkleRoot(hashes: string[]): string;
72
+ /** Audit path for leaf `index`, from the leaf upward (hex). */
73
+ declare function auditPath(hashes: string[], index: number): string[];
74
+ /** RFC 9162 §2.1.3.2 inclusion check of `hashHex` at `index` in a tree of `size`. */
75
+ declare function verifyInclusion(hashHex: string, index: unknown, size: unknown, path: unknown, rootHex: unknown): boolean;
76
+
77
+ /** Envelopes and signed records (SPEC §2). */
78
+
79
+ type Kind = "decision" | "outcome" | "note";
80
+ declare const AGENT_RE: RegExp;
81
+ interface Envelope {
82
+ v: 1;
83
+ kind: Kind;
84
+ project: string;
85
+ env: "test" | "live";
86
+ agent: string;
87
+ seq: number;
88
+ prev: string | null;
89
+ time: string;
90
+ name: string;
91
+ content_hash: string;
92
+ ref: string | null;
93
+ }
94
+ interface SignedRecord {
95
+ envelope: Envelope;
96
+ key_id: string;
97
+ sig: string;
98
+ /** A draft (§8): the envelope has no project and env yet; `sig` is over domain `draft`. */
99
+ draft?: true;
100
+ store_content?: boolean;
101
+ content?: Record<string, unknown>;
102
+ /** The content's salt (§2.1): kept with the content, sent only with stored content. */
103
+ salt?: string;
104
+ receipt?: Receipt;
105
+ }
106
+ interface Receipt {
107
+ record_hash: string;
108
+ index: number;
109
+ proof: string[];
110
+ batch: Record<string, any>;
111
+ batch_hash: string;
112
+ batch_sig: string;
113
+ service_key_id: string;
114
+ head: Record<string, any>;
115
+ head_sig: string;
116
+ }
117
+ /** Current time as RFC 3339 UTC with milliseconds. */
118
+ declare function now(): string;
119
+ /** §2.2 monotonic time: the machine's clock (`wall`), raised to the previous record's time in
120
+ * the chain and to the signing key's valid_from when it reads earlier (an NTP step back, a VM
121
+ * resumed). Returns [time, raised]. When raised, the SDK keeps `wall` in the content's
122
+ * `meta.wall_time`. A chain written this way never goes back in time. */
123
+ declare function clampTime(wall: string, prev?: string | null, validFrom?: string | null): [string, boolean];
124
+ /** `content` with the clock's own reading kept in `meta.wall_time` (when the record's time was raised). */
125
+ declare function withWallTime(content: Record<string, unknown>, wall: string): Record<string, unknown>;
126
+ declare function validTime(s: unknown): boolean;
127
+ /** Turn a folder or free-form name into a valid agent id. */
128
+ declare function agentName(raw: string): string;
129
+ /** `prj_` + a 26-character ULID in Crockford base32. */
130
+ declare function validProjectId(s: unknown): boolean;
131
+ /** Schema problems with a receipt (§3.4): exact fields and types; signatures are only
132
+ * checked to be strings (their validity is checks 6 and 8). */
133
+ declare function receiptErrors(rc: unknown): string[];
134
+ /** Schema problems with an envelope; empty when valid (§2.2). */
135
+ declare function envelopeErrors(env: unknown, fields?: readonly string[]): string[];
136
+ /** Schema problems with a signed record (§2.3); `receipt` is allowed. */
137
+ declare function recordErrors(rec: unknown): string[];
138
+ interface RecordFields {
139
+ kind: Kind;
140
+ project: string;
141
+ env: "test" | "live";
142
+ agent: string;
143
+ seq: number;
144
+ prev: string | null;
145
+ name: string;
146
+ content: Record<string, unknown>;
147
+ ref?: string | null;
148
+ time?: string;
149
+ /** The content salt (§2.1); a fresh random one when not given. */
150
+ salt?: string;
151
+ }
152
+ /** Build and sign `{envelope, key_id, sig, content, salt}`: the content hash is salted (SPEC
153
+ * §2.1). Throws on a bad envelope or on content numbers other implementations could not re-read
154
+ * (SPEC §1). */
155
+ declare function makeRecord(key: SigningKey, f: RecordFields): SignedRecord;
156
+ declare function recordHash(rec: {
157
+ envelope: unknown;
158
+ }): string;
159
+ /** The record as sent to the service: content and its salt only when stored there (§2.3). */
160
+ declare function wireRecord(rec: SignedRecord, storeContent: boolean): SignedRecord;
161
+ /** A draft (§8) for a chain whose project is not known yet (no API key, or the key's project
162
+ * not learned): the envelope without project and env, signed by the agent's key over domain
163
+ * `draft`, so a local chain verifies before anything is sent. `recordHash` of a draft is its
164
+ * draft hash. */
165
+ declare function makeDraft(key: SigningKey, f: Omit<RecordFields, "project" | "env">, storeContent: boolean): SignedRecord;
166
+ /** Schema problems with a draft line (§8); empty when valid. */
167
+ declare function draftErrors(rec: unknown): string[];
168
+ /** The signed record for a draft once its project is known (§8). */
169
+ declare function signDraft(key: SigningKey, draft: SignedRecord, project: string, env: "test" | "live", prev: string | null, ref: string | null, seq?: number): SignedRecord;
170
+ /** A service key (§5) or, with `agent`, a pack's agent key: exact fields, valid times. */
171
+ declare function keyEntryErrors(k: unknown, agent?: boolean): string[];
172
+ declare function registration(project: string, env: string, agent: string, publicKey: string, validFrom: string): {
173
+ project: string;
174
+ env: string;
175
+ agent: string;
176
+ public_key: string;
177
+ valid_from: string;
178
+ };
179
+ interface AgentKeyEntry {
180
+ agent: string;
181
+ key_id: string;
182
+ public_key: string;
183
+ valid_from: string;
184
+ /** When a retired key was retired (the service's time, §2.4); null for a current key. */
185
+ valid_to: string | null;
186
+ sig: string;
187
+ }
188
+ /** A self-signed agent-key manifest entry (§2.5). */
189
+ declare function manifestEntry(agent: string, key: SigningKey, validFrom: string, validTo?: string | null): AgentKeyEntry;
190
+ /** Problems with an agent-key manifest (§2.5), self-signatures included. */
191
+ declare function manifestErrors(doc: any): string[];
192
+
193
+ /** Minimal JSON client for the signafAIde HTTP API (SPEC §6). Built-in fetch only. */
194
+ /** The hosted witness. Configurable with SIGNAFAIDE_URL (a self-hosted witness, or a local dev
195
+ * server such as http://127.0.0.1:7474); a release test fails if this is ever a loopback address. */
196
+ declare const DEFAULT_URL = "https://api.signafaide.co.uk";
197
+ /** A response, as the transport hands it to the client. */
198
+ interface TransportResponse {
199
+ status: number;
200
+ contentType: string;
201
+ text: string;
202
+ }
203
+ /** How a request is made. The default is `fetch`; the Node runtime passes one whose idle
204
+ * sockets do not keep the process alive. `signal` aborts it. */
205
+ type Transport = (req: {
206
+ method: string;
207
+ url: string;
208
+ headers: Record<string, string>;
209
+ body?: string;
210
+ timeoutMs: number;
211
+ signal?: AbortSignal;
212
+ }) => Promise<TransportResponse>;
213
+ /**
214
+ * An error from the service or the SDK. `message` is the one-sentence fix.
215
+ * `code` is the service error code (`chain_conflict`, `invalid_key`...), or
216
+ * `network` when the service could not be reached.
217
+ */
218
+ declare class SignafaideError extends Error {
219
+ readonly code: string;
220
+ readonly status: number;
221
+ readonly docs?: string;
222
+ readonly body: Record<string, any>;
223
+ constructor(message: string, opts?: {
224
+ code?: string;
225
+ status?: number;
226
+ docs?: string;
227
+ body?: Record<string, any>;
228
+ });
229
+ get retryable(): boolean;
230
+ /** The service refused for a reason retrying cannot change (quota, key, revoked agent key...). */
231
+ get permanent(): boolean;
232
+ }
233
+ declare class Client {
234
+ readonly apiKey?: string | undefined;
235
+ readonly timeoutMs: number;
236
+ readonly url: string;
237
+ readonly clientId: string;
238
+ readonly transport: Transport;
239
+ readonly signal?: AbortSignal;
240
+ constructor(url?: string, apiKey?: string | undefined, timeoutMs?: number, clientId?: string, opts?: {
241
+ transport?: Transport;
242
+ signal?: AbortSignal;
243
+ timeoutLabel?: string;
244
+ });
245
+ /** Said instead of "no answer within <timeout> s" when the request shares a larger limit. */
246
+ readonly timeoutLabel?: string;
247
+ request(method: string, path: string, opts?: {
248
+ body?: unknown;
249
+ auth?: boolean;
250
+ query?: Record<string, string | undefined>;
251
+ }): Promise<any>;
252
+ sandbox(): Promise<{
253
+ project: string;
254
+ api_key: string;
255
+ claim_url: string;
256
+ free_quota: number;
257
+ terms_url?: string;
258
+ privacy_url?: string;
259
+ }>;
260
+ /** §2.4: the registration with its proof of possession (and rotation signature). */
261
+ registerAgent(name: string, publicKey: string, validFrom: string, proof: string, rotation?: {
262
+ key_id: string;
263
+ sig: string;
264
+ }): Promise<{
265
+ agent: string;
266
+ key_id: string;
267
+ retired?: {
268
+ key_id: string;
269
+ valid_to: string;
270
+ }[];
271
+ }>;
272
+ /** `{project, env}` of this API key (§6). */
273
+ project(): Promise<{
274
+ project: string;
275
+ env: string;
276
+ claimed?: boolean;
277
+ name?: string | null;
278
+ }>;
279
+ submit(records: unknown[]): Promise<{
280
+ receipts: any[];
281
+ heads: any[];
282
+ }>;
283
+ head(agent: string): Promise<any>;
284
+ /** `{record, receipt, anchor}` of one record of the key's project. */
285
+ record(recordHash: string): Promise<any>;
286
+ /** A verification pack (§7) of the whole project, or of one agent. */
287
+ export(agent?: string, from?: string, to?: string): Promise<any>;
288
+ usage(): Promise<{
289
+ period: string;
290
+ used: number;
291
+ quota: number | null;
292
+ plan?: string;
293
+ }>;
294
+ /** Usage event (`verify`, `export`). Not part of SPEC §6 yet. */
295
+ event(name: string): Promise<void>;
296
+ serviceKeys(): Promise<{
297
+ keys: any[];
298
+ }>;
299
+ }
300
+ /** GET a JSON document (used for `--keys <url>`). */
301
+ declare function fetchJson(url: string, timeoutMs?: number): Promise<any>;
302
+
303
+ /**
304
+ * Tool declarations (SPEC §2.6) and the coverage they give (§7): which of an agent's tools are
305
+ * recorded, read from envelopes alone (kind, name, seq), so it holds for hash-only records.
306
+ * Pure: no I/O. The same names, rules and wording as the Rust core and the Python SDK.
307
+ *
308
+ * Coverage is a note, never a failure, and never a claim of completeness: a declaration is the
309
+ * client's own signed statement of the tools the agent has.
310
+ */
311
+ /** The name of the note that starts a new tool declaration for the agent's chain. */
312
+ declare const TOOLS = "signafaide.tools";
313
+ /** `signafaide.tool/<tool name>`: the agent has this tool and calls to it are recorded. */
314
+ declare const TOOL_PREFIX = "signafaide.tool/";
315
+ /** `signafaide.tool_unrecorded/<tool name>`: the agent has this tool and calls to it are not recorded. */
316
+ declare const TOOL_UNRECORDED_PREFIX = "signafaide.tool_unrecorded/";
317
+ /** The longest tool name a declaration holds as it is, in characters (Unicode code points): an
318
+ * envelope name is at most 128 and the longer prefix takes 27. */
319
+ declare const MAX_DECLARED_NAME = 101;
320
+ /** A tool's name as a declaration holds it: the name itself when it has at most 101 characters,
321
+ * else its first 92 characters, `~`, and the first 8 hex characters of sha256(UTF-8 tool name).
322
+ * A name that is already short maps to itself. */
323
+ declare function declaredName(tool: string): string;
324
+ /** The envelope name of the note that declares `tool`. */
325
+ declare function toolNoteName(tool: string, recorded: boolean): string;
326
+ /** What a record says about the agent's tool list, from its kind and name alone: `"header"`,
327
+ * a tool, or null when it is not a tool-declaration note. */
328
+ declare function toolNote(kind: unknown, name: unknown): "header" | {
329
+ name: string;
330
+ recorded: boolean;
331
+ } | null;
332
+ interface DeclaredTool {
333
+ name: string;
334
+ recorded: boolean;
335
+ }
336
+ /** What one agent's records show about its tools (§7 tool coverage). */
337
+ interface Coverage {
338
+ /** The tools of the agent's current declaration, in name order; empty when there is none. */
339
+ declared: DeclaredTool[];
340
+ /** The names of decision records that are not in the current declaration, in name order. */
341
+ undeclared_recorded: string[];
342
+ /** The seq of the `signafaide.tools` note that starts the current declaration, or null. */
343
+ declared_at_seq: number | null;
344
+ }
345
+ /** The coverage of one agent's records, given in seq order. The current declaration is the one
346
+ * started by the last `signafaide.tools` note; tool notes before the first such note belong to a
347
+ * declaration the records do not hold and are ignored. Inside a declaration a later note for the
348
+ * same tool replaces an earlier one. A `decision` counts as a recorded tool unless its name
349
+ * starts with `signafaide.`; it is compared by its declared name. */
350
+ declare function coverage(records: Iterable<{
351
+ seq: number;
352
+ kind: unknown;
353
+ name: unknown;
354
+ }>): Coverage;
355
+ /** The coverage of `agent` in words (§7), one line each: what was declared, then the recorded
356
+ * tools the declaration does not list. */
357
+ declare function coverageLines(agent: string, c: Coverage): string[];
358
+
359
+ declare const MAX_PROOF_BYTES = 65536;
360
+ declare const MAX_NODES = 1024;
361
+ type Attestation = ({
362
+ kind: "bitcoin";
363
+ height: number;
364
+ } | {
365
+ kind: "pending";
366
+ uri: string;
367
+ } | {
368
+ kind: "unknown";
369
+ tag: string;
370
+ }) & {
371
+ msg: Uint8Array;
372
+ };
373
+ /** Digest and attestations of a detached proof. Throws on a malformed proof. */
374
+ declare function parseOts(data: Uint8Array): {
375
+ digest: string;
376
+ attestations: Attestation[];
377
+ };
378
+ /** Digest and lowest attested block height (null while pending). Throws for a
379
+ * malformed proof or one with no attestation at all. */
380
+ declare function otsStatus(data: Uint8Array): {
381
+ digest: string;
382
+ height: number | null;
383
+ };
384
+ /** (height, Merkle root hex in header byte order) of each block attestation. */
385
+ declare function blockAttestations(data: Uint8Array): [number, string][];
386
+
387
+ /**
388
+ * Public-timestamp confirmation for §7 check 11: the raw 80-byte header of a block from an
389
+ * Esplora-style explorer API (`GET {base}/block-height/{h}`, then `GET {base}/block/{hash}/header`).
390
+ * mempool.space and blockstream.info serve this API. The header is trusted only when it hashes to
391
+ * that block hash and meets its own proof of work (`checkHeader`). Uses built-in fetch; no
392
+ * redirects. An explorer over plain http is never asked: anyone on the path could answer.
393
+ */
394
+ declare const DEFAULT_TIMESTAMP_URL = "https://mempool.space/api";
395
+ /** A block as an explorer serves it: its hash (display hex) and raw header (hex). */
396
+ interface BlockHeader {
397
+ hash: string;
398
+ header: string;
399
+ }
400
+ /** height -> [fetched, the block header | why it could not be fetched] */
401
+ type BlockRoots = Map<number, [true, BlockHeader] | [false, string]>;
402
+ /** The target of a compact `nBits`; null when it is negative, zero or overflows. */
403
+ declare function target(bits: number): bigint | null;
404
+ /** The Merkle root (header byte order, hex) of a header that hashes to `hash` (display hex) and
405
+ * meets its own proof of work. Throws saying why the header is not trusted. */
406
+ declare function checkHeader(hash: string, headerHex: string): string;
407
+ /** The hash and raw header of block `height` as the explorer serves them. Throws with the reason. */
408
+ declare function blockHeader(base: string, height: number, timeoutMs?: number): Promise<BlockHeader>;
409
+ /** Look up every height; a failure is kept with its reason (the check then says NOT CHECKED).
410
+ * A plain-http explorer is not asked. */
411
+ declare function lookupTimestamps(base: string, heights: number[]): Promise<BlockRoots>;
412
+ /** The `bitcoin` object of a test vector: height -> {hash, header}. */
413
+ declare function rootsFromVector(m: Record<string, unknown>): BlockRoots;
414
+
415
+ /**
416
+ * The §7 verifier, in pure TypeScript (runs in Node, browsers and edge runtimes).
417
+ * Same check ids, statuses, text and JSON as the reference verifier (`signafaide-verify`).
418
+ *
419
+ * The drand BLS12-381 signature (check 9) is verified only when a `drand` verifier is
420
+ * passed; `loadDrandVerifier()` builds one from `@noble/curves` (a dependency).
421
+ * Without it the check is "not checked", never "pass".
422
+ */
423
+
424
+ declare const CHECKS: readonly [readonly ["schema", "Envelope schema"], readonly ["record_hash", "Record hashes"], readonly ["record_sig", "Client signatures"], readonly ["chain", "Agent chains"], readonly ["merkle", "Merkle proofs"], readonly ["batch_sig", "Batch hashes and signatures"], readonly ["batch_chain", "Batch chain"], readonly ["head_sig", "Signed heads"], readonly ["drand", "drand rounds"], readonly ["anchor", "Anchors"], readonly ["ots", "OpenTimestamps"], readonly ["content", "Content hashes"]];
425
+ /** Check 3 without independent agent keys (§7). */
426
+ declare const 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";
427
+ /** The highest drand round whose publication time is a protocol time (year 9999 at most). */
428
+ declare const MAX_ROUND: number;
429
+ /** A batch is anchored within this time of its server_time (§4); an older batch no anchor covers is "not timestamped". */
430
+ declare const ANCHOR_WINDOW_MS = 7200000;
431
+ /** Where keys came from, when not from an independent source (§7 result line). */
432
+ type KeySource = "independent" | "taken from the pack" | "fetched over plain http";
433
+ interface PackFilter {
434
+ agents: string[] | null;
435
+ from: string | null;
436
+ to: string | null;
437
+ }
438
+ type CheckId = (typeof CHECKS)[number][0];
439
+ /** `pending`: ran, found nothing wrong, and waits for an anchor or public timestamp not confirmed yet (§7); never counted as passed. */
440
+ type Status = "pass" | "fail" | "not_checked" | "pending";
441
+ interface Failure {
442
+ item: string;
443
+ message: string;
444
+ }
445
+ interface Check {
446
+ n: number;
447
+ id: CheckId;
448
+ title: string;
449
+ status: Status;
450
+ checked: number;
451
+ detail: string;
452
+ value?: string;
453
+ failures: Failure[];
454
+ }
455
+ interface KeyDoc {
456
+ key_id: string;
457
+ public_key: string;
458
+ valid_from?: string | null;
459
+ valid_to?: string | null;
460
+ agent?: string;
461
+ }
462
+ interface DrandBeacon {
463
+ chain: string;
464
+ round: number;
465
+ randomness: string;
466
+ signature: string;
467
+ }
468
+ /** Verifies a quicknet beacon's BLS signature over its round. */
469
+ type DrandVerifier = (beacon: DrandBeacon) => boolean;
470
+ /** No independent service keys and `trustEmbeddedKeys` not set: an input error (exit 2). */
471
+ declare class KeysRequired extends Error {
472
+ name: string;
473
+ }
474
+ /** What a pack covers (§7 result line): `export of every agent, all records, generated <t>`. */
475
+ declare function scopeText(filter: PackFilter, generated: string): string;
476
+ /** How many checks passed, failed, are pending and were not checked (§7 result line). */
477
+ declare function countsText(checks: {
478
+ id: string;
479
+ status: Status;
480
+ }[], nothingCosigned?: boolean): string;
481
+ /** `PASS` or `FAIL`; on PASS, why keys were not independent; then how many checks ran and what
482
+ * the pack covers (§7). */
483
+ declare function resultLine(result: string, service?: KeySource, agent?: KeySource, byService?: boolean, scope?: string | null, counts?: string): string;
484
+ /** A record's content held locally (`--content`), with its salt when the file has one. */
485
+ interface LocalContent {
486
+ content: Record<string, unknown>;
487
+ salt: string | undefined;
488
+ }
489
+ interface ReportScope {
490
+ generated: string;
491
+ filter: PackFilter;
492
+ sources: [KeySource, KeySource];
493
+ byService: boolean;
494
+ }
495
+ declare class Report {
496
+ readonly checks: Check[];
497
+ readonly project: string | null;
498
+ readonly env: string | null;
499
+ readonly records: number;
500
+ readonly warnings: string[];
501
+ readonly notes: string[];
502
+ readonly independent: {
503
+ service_keys: boolean;
504
+ agent_keys: boolean;
505
+ };
506
+ readonly scope: ReportScope | null;
507
+ /** Lines shown after the checks, before the result line: for local state, what still needs
508
+ * the witness (co-signature, public timestamp). */
509
+ readonly after: string[];
510
+ /** Local state with no record co-signed yet: the result line says so (§7). */
511
+ nothingCosigned: boolean;
512
+ /** Local state: chains that cannot ship (refused by the witness, or wedged here), as
513
+ * `<chain>: <reason>`. Any makes the result BLOCKED, which always fails (§7). */
514
+ readonly blocked: string[];
515
+ /** Local state with --fail-on-pending-send: records not co-signed yet, which then fail. */
516
+ notSent: number;
517
+ /** The project's name from the signed export statement, when the pack has one. */
518
+ projectName: string | null;
519
+ /** Tool coverage per agent (§7; per chain for local state): the agent's current tool
520
+ * declaration and the recorded tools it does not list. A note, never a failure. */
521
+ coverage: Record<string, Coverage>;
522
+ constructor(checks: Check[], project?: string | null, env?: string | null, records?: number, warnings?: string[], notes?: string[], independent?: {
523
+ service_keys: boolean;
524
+ agent_keys: boolean;
525
+ }, scope?: ReportScope | null);
526
+ /** e.g. `PASS: export of every agent, all records, generated <t>`, or
527
+ * `PASS (agent keys taken from the pack, not independent): export of ...` (§7). */
528
+ get resultLine(): string;
529
+ get ok(): boolean;
530
+ get result(): "PASS" | "FAIL" | "BLOCKED";
531
+ check(id: CheckId): Check;
532
+ firstFailed(): CheckId | null;
533
+ /** Tool coverage in words (§7), agent by agent: what each agent declared, then the recorded
534
+ * tools its declaration does not list. */
535
+ coverageLines(): string[];
536
+ lines(): string[];
537
+ toString(): string;
538
+ toJSON(): object;
539
+ }
540
+ /** Service keys from `{"keys": [...]}` (the well-known document) or a bare list. */
541
+ declare function parseServiceKeys(doc: unknown): KeyDoc[];
542
+ /** The customer's agent-key manifest (§2.5); every entry's self-signature is checked. */
543
+ declare function parseAgentKeys(doc: unknown): AgentKeyEntry[];
544
+ interface SignedHeadDoc {
545
+ head: {
546
+ project: string;
547
+ env: string;
548
+ agent: string;
549
+ seq: number;
550
+ record_hash: string;
551
+ server_time: string;
552
+ };
553
+ head_sig: string;
554
+ }
555
+ /** The client's head file (§8 `head.json`) as a list of signed heads. */
556
+ declare function parseHeadFile(doc: unknown): SignedHeadDoc[];
557
+ /** record_hash -> content from JSON lines, or a JSON array, of `{envelope, content}` (§7). */
558
+ declare function contentFromText(text: string, into?: Map<string, LocalContent>): Map<string, LocalContent>;
559
+ /** The block heights the pack's public timestamps claim, to look up before verifying. */
560
+ declare function timestampHeights(pack: any): number[];
561
+ interface PackOptions {
562
+ trustEmbeddedKeys?: boolean;
563
+ /** The customer's agent-key manifest entries (`--agent-keys`, §2.5). */
564
+ agentKeys?: AgentKeyEntry[] | {
565
+ agent: string;
566
+ key_id: string;
567
+ public_key: string;
568
+ valid_from: string;
569
+ }[] | null;
570
+ trustEmbeddedAgentKeys?: boolean;
571
+ /** The client's head file (`--head`). */
572
+ heads?: SignedHeadDoc[] | null;
573
+ /** Block headers looked up for the public timestamps; null/undefined: offline. */
574
+ timestamps?: BlockRoots | null;
575
+ /** record_hash -> content, e.g. from the local chain. */
576
+ contents?: Map<string, LocalContent>;
577
+ drand?: DrandVerifier | null;
578
+ /** The service keys came over plain http from this machine (warned; not independent). */
579
+ serviceKeysPlainHttp?: boolean;
580
+ /** The agent keys came over plain http from this machine (warned; not independent). */
581
+ agentKeysPlainHttp?: boolean;
582
+ /** The service checks its own pack with its own key registry (never independent). */
583
+ byService?: boolean;
584
+ }
585
+ /** `records_hash` (§7): sha256(JCS(list)) of every record's `receipt.record_hash`, in pack order. */
586
+ declare function recordsHash(records: any[]): string;
587
+ /** The export statement the service signs (§7, domain `export`). */
588
+ declare function exportStatement(pack: any): object;
589
+ /** True for `http://localhost` and loopback addresses: the only hosts plain http is accepted from. */
590
+ declare function isLoopbackUrl(url: string): boolean;
591
+ /** Verify a pack. Throws `KeysRequired` when `serviceKeys` is null and embedded keys are not
592
+ * trusted. Parse pack text with `parseJson` (not JSON.parse) so number literals are judged as written. */
593
+ declare function verifyPack(pack: any, serviceKeys: KeyDoc[] | null, opts?: PackOptions): Report;
594
+ interface LocalInput {
595
+ records: any[];
596
+ /** The chain id (`<project>/<env>/<agent>`) of each record. */
597
+ chainIds: string[];
598
+ agentKeys: KeyDoc[];
599
+ /** Newest signed head per chain id. */
600
+ heads: Record<string, {
601
+ head: any;
602
+ head_sig: string;
603
+ }>;
604
+ /** Drafts waiting for the project (§8): not a reason for "no records". */
605
+ drafts?: number;
606
+ /** Per chain id, the witness's signed head the chain continues after (§8: a chain this
607
+ * machine started on an empty disk). */
608
+ bases?: Record<string, {
609
+ head: any;
610
+ head_sig: string;
611
+ stop?: any;
612
+ }>;
613
+ }
614
+ /** Verify local state chain by chain. Unshipped records are fine; a chain must be
615
+ * complete from seq 1 and reach the newest signed head. */
616
+ declare function verifyLocalState(local: LocalInput, serviceKeys: KeyDoc[], drand?: DrandVerifier | null, serviceKeysPlainHttp?: boolean): Report;
617
+ /** `keys_hash` (§4): service keys with `valid_from <= created`, as `{key_id, public_key, valid_from}`,
618
+ * ordered by `valid_from` then `key_id`. */
619
+ declare function serviceKeysHash(keys: KeyDoc[], createdMs: number): string;
620
+ /** §3.1: why `serverMs` does not fit drand round `round`, or null. */
621
+ declare function checkRoundTime(serverMs: number, round: number): string | null;
622
+ /** A drand quicknet verifier from `@noble/curves`, or null when that dependency is missing
623
+ * (a pruned install); the drand check then says `DRAND_NOT_CHECKED`. */
624
+ declare function loadDrandVerifier(): Promise<DrandVerifier | null>;
625
+
626
+ /**
627
+ * The `X-Signafaide-Client` header and usage events.
628
+ *
629
+ * Sent: the SDK name and version, the runtime and its version, the framework adapter
630
+ * in use, the coding agent that set the project up, and how the request was made
631
+ * (sdk, cli or mcp). CLI `verify` and `export` also send one event naming the command.
632
+ * Never sent: content, file paths, hostnames or user names.
633
+ * `SIGNAFAIDE_TELEMETRY=0` drops the adapter, the agent, the strict flag and the events.
634
+ */
635
+ declare const HEADER = "X-Signafaide-Client";
636
+ type Env = Record<string, string | undefined>;
637
+ /** The coding agent whose environment this is, or `unknown`. */
638
+ declare function detectAgent(env?: Env): string;
639
+ declare function telemetryEnabled(env?: Env): boolean;
640
+ /** `signafaide-js/<v> (node <v>; adapter=<a>; agent=<a>; via=<v>[; strict=1])`; with
641
+ * telemetry off, adapter, agent and strict are left out. */
642
+ declare function clientHeader(opts?: {
643
+ via?: string;
644
+ adapter?: string;
645
+ agent?: string;
646
+ strict?: boolean;
647
+ env?: Env;
648
+ }): string;
649
+
650
+ declare const VERSION = "0.3.0";
651
+
652
+ export { checkHeader as $, type AgentKeyEntry as A, type BlockHeader as B, Client as C, DEFAULT_TIMESTAMP_URL as D, type Envelope as E, TOOL_UNRECORDED_PREFIX as F, agentName as G, HEADER as H, assertPortableNumbers as I, JsonNumber as J, type Kind as K, type LocalContent as L, MAX_DECLARED_NAME as M, NOT_INDEPENDENT as N, auditPath as O, type PackFilter as P, b64url as Q, type Receipt as R, SigningKey as S, TOOLS as T, b64urlDecode as U, VERSION as V, blockAttestations as W, blockHeader as X, canonicalBytes as Y, canonicalize as Z, checkContent as _, type SignedRecord as a, checkRoundTime as a0, clampTime as a1, clientHeader as a2, contentFromText as a3, contentHash as a4, countsText as a5, coverage as a6, coverageLines as a7, declaredName as a8, detectAgent as a9, recordHash as aA, recordsHash as aB, registration as aC, resultLine as aD, rootsFromVector as aE, scopeText as aF, serviceKeysHash as aG, sha256Hex as aH, signDraft as aI, signedMessage as aJ, target as aK, telemetryEnabled as aL, timestampHeights as aM, toolNote as aN, toolNoteName as aO, validProjectId as aP, validTime as aQ, verifyInclusion as aR, verifyLocalState as aS, verifyPack as aT, verifySig as aU, wireRecord as aV, withWallTime as aW, draftErrors as aa, envelopeErrors as ab, exportStatement as ac, fetchJson as ad, hashJson as ae, isLoopbackUrl as af, keyEntryErrors as ag, keyId as ah, leafHash as ai, loadDrandVerifier as aj, lookupTimestamps as ak, makeDraft as al, makeRecord as am, manifestEntry as an, manifestErrors as ao, merkleRoot as ap, newSalt as aq, now as ar, otsStatus as as, parseAgentKeys as at, parseHeadFile as au, parseJson as av, parseOts as aw, parseServiceKeys as ax, receiptErrors as ay, recordErrors as az, SignafaideError as b, Report as c, type KeyDoc as d, AGENT_RE as e, ANCHOR_WINDOW_MS as f, type BlockRoots as g, CHECKS as h, type Check as i, type CheckId as j, type Coverage as k, DEFAULT_URL as l, type DeclaredTool as m, type Domain as n, type DrandBeacon as o, type DrandVerifier as p, type KeySource as q, KeysRequired as r, MAX_NODES as s, MAX_PROOF_BYTES as t, MAX_ROUND as u, type PackOptions as v, type RecordFields as w, type SignedHeadDoc as x, type Status as y, TOOL_PREFIX as z };