signafaide 0.0.0-stage → 0.2.1
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/README.md +239 -2
- package/dist/chunk-4C2JEYAU.js +2766 -0
- package/dist/chunk-MBXHRQ2J.js +1919 -0
- package/dist/chunk-S2RSID5F.js +189 -0
- package/dist/cli.js +793 -0
- package/dist/core-BMfko_ex.d.cts +590 -0
- package/dist/core-BMfko_ex.d.ts +590 -0
- package/dist/core.cjs +2613 -0
- package/dist/core.d.cts +1 -0
- package/dist/core.d.ts +1 -0
- package/dist/core.js +164 -0
- package/dist/http-YWG535HP.js +16 -0
- package/dist/index.cjs +5450 -0
- package/dist/index.d.cts +898 -0
- package/dist/index.d.ts +898 -0
- package/dist/index.js +230 -0
- package/package.json +84 -4
|
@@ -0,0 +1,590 @@
|
|
|
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
|
+
declare const MAX_PROOF_BYTES = 65536;
|
|
304
|
+
declare const MAX_NODES = 1024;
|
|
305
|
+
type Attestation = ({
|
|
306
|
+
kind: "bitcoin";
|
|
307
|
+
height: number;
|
|
308
|
+
} | {
|
|
309
|
+
kind: "pending";
|
|
310
|
+
uri: string;
|
|
311
|
+
} | {
|
|
312
|
+
kind: "unknown";
|
|
313
|
+
tag: string;
|
|
314
|
+
}) & {
|
|
315
|
+
msg: Uint8Array;
|
|
316
|
+
};
|
|
317
|
+
/** Digest and attestations of a detached proof. Throws on a malformed proof. */
|
|
318
|
+
declare function parseOts(data: Uint8Array): {
|
|
319
|
+
digest: string;
|
|
320
|
+
attestations: Attestation[];
|
|
321
|
+
};
|
|
322
|
+
/** Digest and lowest attested block height (null while pending). Throws for a
|
|
323
|
+
* malformed proof or one with no attestation at all. */
|
|
324
|
+
declare function otsStatus(data: Uint8Array): {
|
|
325
|
+
digest: string;
|
|
326
|
+
height: number | null;
|
|
327
|
+
};
|
|
328
|
+
/** (height, Merkle root hex in header byte order) of each block attestation. */
|
|
329
|
+
declare function blockAttestations(data: Uint8Array): [number, string][];
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Public-timestamp confirmation for §7 check 11: the raw 80-byte header of a block from an
|
|
333
|
+
* Esplora-style explorer API (`GET {base}/block-height/{h}`, then `GET {base}/block/{hash}/header`).
|
|
334
|
+
* mempool.space and blockstream.info serve this API. The header is trusted only when it hashes to
|
|
335
|
+
* that block hash and meets its own proof of work (`checkHeader`). Uses built-in fetch; no
|
|
336
|
+
* redirects. An explorer over plain http is never asked: anyone on the path could answer.
|
|
337
|
+
*/
|
|
338
|
+
declare const DEFAULT_TIMESTAMP_URL = "https://mempool.space/api";
|
|
339
|
+
/** A block as an explorer serves it: its hash (display hex) and raw header (hex). */
|
|
340
|
+
interface BlockHeader {
|
|
341
|
+
hash: string;
|
|
342
|
+
header: string;
|
|
343
|
+
}
|
|
344
|
+
/** height -> [fetched, the block header | why it could not be fetched] */
|
|
345
|
+
type BlockRoots = Map<number, [true, BlockHeader] | [false, string]>;
|
|
346
|
+
/** The target of a compact `nBits`; null when it is negative, zero or overflows. */
|
|
347
|
+
declare function target(bits: number): bigint | null;
|
|
348
|
+
/** The Merkle root (header byte order, hex) of a header that hashes to `hash` (display hex) and
|
|
349
|
+
* meets its own proof of work. Throws saying why the header is not trusted. */
|
|
350
|
+
declare function checkHeader(hash: string, headerHex: string): string;
|
|
351
|
+
/** The hash and raw header of block `height` as the explorer serves them. Throws with the reason. */
|
|
352
|
+
declare function blockHeader(base: string, height: number, timeoutMs?: number): Promise<BlockHeader>;
|
|
353
|
+
/** Look up every height; a failure is kept with its reason (the check then says NOT CHECKED).
|
|
354
|
+
* A plain-http explorer is not asked. */
|
|
355
|
+
declare function lookupTimestamps(base: string, heights: number[]): Promise<BlockRoots>;
|
|
356
|
+
/** The `bitcoin` object of a test vector: height -> {hash, header}. */
|
|
357
|
+
declare function rootsFromVector(m: Record<string, unknown>): BlockRoots;
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* The §7 verifier, in pure TypeScript (runs in Node, browsers and edge runtimes).
|
|
361
|
+
* Same check ids, statuses, text and JSON as the reference verifier (`signafaide-verify`).
|
|
362
|
+
*
|
|
363
|
+
* The drand BLS12-381 signature (check 9) is verified only when a `drand` verifier is
|
|
364
|
+
* passed; `loadDrandVerifier()` builds one from `@noble/curves` (a dependency).
|
|
365
|
+
* Without it the check is "not checked", never "pass".
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
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"]];
|
|
369
|
+
/** Check 3 without independent agent keys (§7). */
|
|
370
|
+
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";
|
|
371
|
+
/** The highest drand round whose publication time is a protocol time (year 9999 at most). */
|
|
372
|
+
declare const MAX_ROUND: number;
|
|
373
|
+
/** A batch is anchored within this time of its server_time (§4); an older batch no anchor covers is "not timestamped". */
|
|
374
|
+
declare const ANCHOR_WINDOW_MS = 7200000;
|
|
375
|
+
/** Where keys came from, when not from an independent source (§7 result line). */
|
|
376
|
+
type KeySource = "independent" | "taken from the pack" | "fetched over plain http";
|
|
377
|
+
interface PackFilter {
|
|
378
|
+
agents: string[] | null;
|
|
379
|
+
from: string | null;
|
|
380
|
+
to: string | null;
|
|
381
|
+
}
|
|
382
|
+
type CheckId = (typeof CHECKS)[number][0];
|
|
383
|
+
/** `pending`: ran, found nothing wrong, and waits for an anchor or public timestamp not confirmed yet (§7); never counted as passed. */
|
|
384
|
+
type Status = "pass" | "fail" | "not_checked" | "pending";
|
|
385
|
+
interface Failure {
|
|
386
|
+
item: string;
|
|
387
|
+
message: string;
|
|
388
|
+
}
|
|
389
|
+
interface Check {
|
|
390
|
+
n: number;
|
|
391
|
+
id: CheckId;
|
|
392
|
+
title: string;
|
|
393
|
+
status: Status;
|
|
394
|
+
checked: number;
|
|
395
|
+
detail: string;
|
|
396
|
+
value?: string;
|
|
397
|
+
failures: Failure[];
|
|
398
|
+
}
|
|
399
|
+
interface KeyDoc {
|
|
400
|
+
key_id: string;
|
|
401
|
+
public_key: string;
|
|
402
|
+
valid_from?: string | null;
|
|
403
|
+
valid_to?: string | null;
|
|
404
|
+
agent?: string;
|
|
405
|
+
}
|
|
406
|
+
interface DrandBeacon {
|
|
407
|
+
chain: string;
|
|
408
|
+
round: number;
|
|
409
|
+
randomness: string;
|
|
410
|
+
signature: string;
|
|
411
|
+
}
|
|
412
|
+
/** Verifies a quicknet beacon's BLS signature over its round. */
|
|
413
|
+
type DrandVerifier = (beacon: DrandBeacon) => boolean;
|
|
414
|
+
/** No independent service keys and `trustEmbeddedKeys` not set: an input error (exit 2). */
|
|
415
|
+
declare class KeysRequired extends Error {
|
|
416
|
+
name: string;
|
|
417
|
+
}
|
|
418
|
+
/** What a pack covers (§7 result line): `export of every agent, all records, generated <t>`. */
|
|
419
|
+
declare function scopeText(filter: PackFilter, generated: string): string;
|
|
420
|
+
/** How many checks passed, failed, are pending and were not checked (§7 result line). */
|
|
421
|
+
declare function countsText(checks: {
|
|
422
|
+
id: string;
|
|
423
|
+
status: Status;
|
|
424
|
+
}[], nothingCosigned?: boolean): string;
|
|
425
|
+
/** `PASS` or `FAIL`; on PASS, why keys were not independent; then how many checks ran and what
|
|
426
|
+
* the pack covers (§7). */
|
|
427
|
+
declare function resultLine(result: string, service?: KeySource, agent?: KeySource, byService?: boolean, scope?: string | null, counts?: string): string;
|
|
428
|
+
/** A record's content held locally (`--content`), with its salt when the file has one. */
|
|
429
|
+
interface LocalContent {
|
|
430
|
+
content: Record<string, unknown>;
|
|
431
|
+
salt: string | undefined;
|
|
432
|
+
}
|
|
433
|
+
interface ReportScope {
|
|
434
|
+
generated: string;
|
|
435
|
+
filter: PackFilter;
|
|
436
|
+
sources: [KeySource, KeySource];
|
|
437
|
+
byService: boolean;
|
|
438
|
+
}
|
|
439
|
+
declare class Report {
|
|
440
|
+
readonly checks: Check[];
|
|
441
|
+
readonly project: string | null;
|
|
442
|
+
readonly env: string | null;
|
|
443
|
+
readonly records: number;
|
|
444
|
+
readonly warnings: string[];
|
|
445
|
+
readonly notes: string[];
|
|
446
|
+
readonly independent: {
|
|
447
|
+
service_keys: boolean;
|
|
448
|
+
agent_keys: boolean;
|
|
449
|
+
};
|
|
450
|
+
readonly scope: ReportScope | null;
|
|
451
|
+
/** Lines shown after the checks, before the result line: for local state, what still needs
|
|
452
|
+
* the witness (co-signature, public timestamp). */
|
|
453
|
+
readonly after: string[];
|
|
454
|
+
/** Local state with no record co-signed yet: the result line says so (§7). */
|
|
455
|
+
nothingCosigned: boolean;
|
|
456
|
+
/** Local state: chains that cannot ship (refused by the witness, or wedged here), as
|
|
457
|
+
* `<chain>: <reason>`. Any makes the result BLOCKED, which always fails (§7). */
|
|
458
|
+
readonly blocked: string[];
|
|
459
|
+
/** Local state with --fail-on-pending-send: records not co-signed yet, which then fail. */
|
|
460
|
+
notSent: number;
|
|
461
|
+
/** The project's name from the signed export statement, when the pack has one. */
|
|
462
|
+
projectName: string | null;
|
|
463
|
+
constructor(checks: Check[], project?: string | null, env?: string | null, records?: number, warnings?: string[], notes?: string[], independent?: {
|
|
464
|
+
service_keys: boolean;
|
|
465
|
+
agent_keys: boolean;
|
|
466
|
+
}, scope?: ReportScope | null);
|
|
467
|
+
/** e.g. `PASS: export of every agent, all records, generated <t>`, or
|
|
468
|
+
* `PASS (agent keys taken from the pack, not independent): export of ...` (§7). */
|
|
469
|
+
get resultLine(): string;
|
|
470
|
+
get ok(): boolean;
|
|
471
|
+
get result(): "PASS" | "FAIL" | "BLOCKED";
|
|
472
|
+
check(id: CheckId): Check;
|
|
473
|
+
firstFailed(): CheckId | null;
|
|
474
|
+
lines(): string[];
|
|
475
|
+
toString(): string;
|
|
476
|
+
toJSON(): object;
|
|
477
|
+
}
|
|
478
|
+
/** Service keys from `{"keys": [...]}` (the well-known document) or a bare list. */
|
|
479
|
+
declare function parseServiceKeys(doc: unknown): KeyDoc[];
|
|
480
|
+
/** The customer's agent-key manifest (§2.5); every entry's self-signature is checked. */
|
|
481
|
+
declare function parseAgentKeys(doc: unknown): AgentKeyEntry[];
|
|
482
|
+
interface SignedHeadDoc {
|
|
483
|
+
head: {
|
|
484
|
+
project: string;
|
|
485
|
+
env: string;
|
|
486
|
+
agent: string;
|
|
487
|
+
seq: number;
|
|
488
|
+
record_hash: string;
|
|
489
|
+
server_time: string;
|
|
490
|
+
};
|
|
491
|
+
head_sig: string;
|
|
492
|
+
}
|
|
493
|
+
/** The client's head file (§8 `head.json`) as a list of signed heads. */
|
|
494
|
+
declare function parseHeadFile(doc: unknown): SignedHeadDoc[];
|
|
495
|
+
/** record_hash -> content from JSON lines, or a JSON array, of `{envelope, content}` (§7). */
|
|
496
|
+
declare function contentFromText(text: string, into?: Map<string, LocalContent>): Map<string, LocalContent>;
|
|
497
|
+
/** The block heights the pack's public timestamps claim, to look up before verifying. */
|
|
498
|
+
declare function timestampHeights(pack: any): number[];
|
|
499
|
+
interface PackOptions {
|
|
500
|
+
trustEmbeddedKeys?: boolean;
|
|
501
|
+
/** The customer's agent-key manifest entries (`--agent-keys`, §2.5). */
|
|
502
|
+
agentKeys?: AgentKeyEntry[] | {
|
|
503
|
+
agent: string;
|
|
504
|
+
key_id: string;
|
|
505
|
+
public_key: string;
|
|
506
|
+
valid_from: string;
|
|
507
|
+
}[] | null;
|
|
508
|
+
trustEmbeddedAgentKeys?: boolean;
|
|
509
|
+
/** The client's head file (`--head`). */
|
|
510
|
+
heads?: SignedHeadDoc[] | null;
|
|
511
|
+
/** Block headers looked up for the public timestamps; null/undefined: offline. */
|
|
512
|
+
timestamps?: BlockRoots | null;
|
|
513
|
+
/** record_hash -> content, e.g. from the local chain. */
|
|
514
|
+
contents?: Map<string, LocalContent>;
|
|
515
|
+
drand?: DrandVerifier | null;
|
|
516
|
+
/** The service keys came over plain http from this machine (warned; not independent). */
|
|
517
|
+
serviceKeysPlainHttp?: boolean;
|
|
518
|
+
/** The agent keys came over plain http from this machine (warned; not independent). */
|
|
519
|
+
agentKeysPlainHttp?: boolean;
|
|
520
|
+
/** The service checks its own pack with its own key registry (never independent). */
|
|
521
|
+
byService?: boolean;
|
|
522
|
+
}
|
|
523
|
+
/** `records_hash` (§7): sha256(JCS(list)) of every record's `receipt.record_hash`, in pack order. */
|
|
524
|
+
declare function recordsHash(records: any[]): string;
|
|
525
|
+
/** The export statement the service signs (§7, domain `export`). */
|
|
526
|
+
declare function exportStatement(pack: any): object;
|
|
527
|
+
/** True for `http://localhost` and loopback addresses: the only hosts plain http is accepted from. */
|
|
528
|
+
declare function isLoopbackUrl(url: string): boolean;
|
|
529
|
+
/** Verify a pack. Throws `KeysRequired` when `serviceKeys` is null and embedded keys are not
|
|
530
|
+
* trusted. Parse pack text with `parseJson` (not JSON.parse) so number literals are judged as written. */
|
|
531
|
+
declare function verifyPack(pack: any, serviceKeys: KeyDoc[] | null, opts?: PackOptions): Report;
|
|
532
|
+
interface LocalInput {
|
|
533
|
+
records: any[];
|
|
534
|
+
/** The chain id (`<project>/<env>/<agent>`) of each record. */
|
|
535
|
+
chainIds: string[];
|
|
536
|
+
agentKeys: KeyDoc[];
|
|
537
|
+
/** Newest signed head per chain id. */
|
|
538
|
+
heads: Record<string, {
|
|
539
|
+
head: any;
|
|
540
|
+
head_sig: string;
|
|
541
|
+
}>;
|
|
542
|
+
/** Drafts waiting for the project (§8): not a reason for "no records". */
|
|
543
|
+
drafts?: number;
|
|
544
|
+
/** Per chain id, the witness's signed head the chain continues after (§8: a chain this
|
|
545
|
+
* machine started on an empty disk). */
|
|
546
|
+
bases?: Record<string, {
|
|
547
|
+
head: any;
|
|
548
|
+
head_sig: string;
|
|
549
|
+
stop?: any;
|
|
550
|
+
}>;
|
|
551
|
+
}
|
|
552
|
+
/** Verify local state chain by chain. Unshipped records are fine; a chain must be
|
|
553
|
+
* complete from seq 1 and reach the newest signed head. */
|
|
554
|
+
declare function verifyLocalState(local: LocalInput, serviceKeys: KeyDoc[], drand?: DrandVerifier | null, serviceKeysPlainHttp?: boolean): Report;
|
|
555
|
+
/** `keys_hash` (§4): service keys with `valid_from <= created`, as `{key_id, public_key, valid_from}`,
|
|
556
|
+
* ordered by `valid_from` then `key_id`. */
|
|
557
|
+
declare function serviceKeysHash(keys: KeyDoc[], createdMs: number): string;
|
|
558
|
+
/** §3.1: why `serverMs` does not fit drand round `round`, or null. */
|
|
559
|
+
declare function checkRoundTime(serverMs: number, round: number): string | null;
|
|
560
|
+
/** A drand quicknet verifier from `@noble/curves`, or null when that dependency is missing
|
|
561
|
+
* (a pruned install); the drand check then says `DRAND_NOT_CHECKED`. */
|
|
562
|
+
declare function loadDrandVerifier(): Promise<DrandVerifier | null>;
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* The `X-Signafaide-Client` header and usage events.
|
|
566
|
+
*
|
|
567
|
+
* Sent: the SDK name and version, the runtime and its version, the framework adapter
|
|
568
|
+
* in use, the coding agent that set the project up, and how the request was made
|
|
569
|
+
* (sdk, cli or mcp). CLI `verify` and `export` also send one event naming the command.
|
|
570
|
+
* Never sent: content, file paths, hostnames or user names.
|
|
571
|
+
* `SIGNAFAIDE_TELEMETRY=0` drops the adapter, the agent, the strict flag and the events.
|
|
572
|
+
*/
|
|
573
|
+
declare const HEADER = "X-Signafaide-Client";
|
|
574
|
+
type Env = Record<string, string | undefined>;
|
|
575
|
+
/** The coding agent whose environment this is, or `unknown`. */
|
|
576
|
+
declare function detectAgent(env?: Env): string;
|
|
577
|
+
declare function telemetryEnabled(env?: Env): boolean;
|
|
578
|
+
/** `signafaide-js/<v> (node <v>; adapter=<a>; agent=<a>; via=<v>[; strict=1])`; with
|
|
579
|
+
* telemetry off, adapter, agent and strict are left out. */
|
|
580
|
+
declare function clientHeader(opts?: {
|
|
581
|
+
via?: string;
|
|
582
|
+
adapter?: string;
|
|
583
|
+
agent?: string;
|
|
584
|
+
strict?: boolean;
|
|
585
|
+
env?: Env;
|
|
586
|
+
}): string;
|
|
587
|
+
|
|
588
|
+
declare const VERSION = "0.2.1";
|
|
589
|
+
|
|
590
|
+
export { countsText as $, type AgentKeyEntry as A, type BlockHeader as B, Client as C, DEFAULT_TIMESTAMP_URL as D, type Envelope as E, b64urlDecode as F, blockAttestations as G, HEADER as H, blockHeader as I, JsonNumber as J, type Kind as K, type LocalContent as L, MAX_NODES as M, NOT_INDEPENDENT as N, canonicalBytes as O, type PackFilter as P, canonicalize as Q, type Receipt as R, SigningKey as S, checkContent as T, checkHeader as U, VERSION as V, checkRoundTime as W, clampTime as X, clientHeader as Y, contentFromText as Z, contentHash as _, type SignedRecord as a, detectAgent as a0, draftErrors as a1, envelopeErrors as a2, exportStatement as a3, fetchJson as a4, hashJson as a5, isLoopbackUrl as a6, keyEntryErrors as a7, keyId as a8, leafHash as a9, signedMessage as aA, target as aB, telemetryEnabled as aC, timestampHeights as aD, validProjectId as aE, validTime as aF, verifyInclusion as aG, verifyLocalState as aH, verifyPack as aI, verifySig as aJ, wireRecord as aK, withWallTime as aL, loadDrandVerifier as aa, lookupTimestamps as ab, makeDraft as ac, makeRecord as ad, manifestEntry as ae, manifestErrors as af, merkleRoot as ag, newSalt as ah, now as ai, otsStatus as aj, parseAgentKeys as ak, parseHeadFile as al, parseJson as am, parseOts as an, parseServiceKeys as ao, receiptErrors as ap, recordErrors as aq, recordHash as ar, recordsHash as as, registration as at, resultLine as au, rootsFromVector as av, scopeText as aw, serviceKeysHash as ax, sha256Hex as ay, signDraft 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, DEFAULT_URL as k, type Domain as l, type DrandBeacon as m, type DrandVerifier as n, type KeySource as o, KeysRequired as p, MAX_PROOF_BYTES as q, MAX_ROUND as r, type PackOptions as s, type RecordFields as t, type SignedHeadDoc as u, type Status as v, agentName as w, assertPortableNumbers as x, auditPath as y, b64url as z };
|