verdifax 1.4.0 → 1.5.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/README.md CHANGED
@@ -112,6 +112,15 @@ if (v.verified) {
112
112
  | `client.getNewestSignOff({ asOwner? })` | `GET /signoffs/newest`, or `GET /account/signoffs/newest` with `asOwner` | required, or owner key |
113
113
  | `client.signOff(target, decision, approverKey, { note?, asOwner? })` | `POST /signoffs`, or `POST /account/signoffs` with `asOwner` | required, or owner key |
114
114
  | `client.getSignOffs(runId, { asOwner? })` | `GET /runs/{id}/signoffs`, or `GET /account/runs/{id}/signoffs` with `asOwner` | required, or owner key |
115
+ | `client.getSignals({ after?, limit?, wait?, asOwner? })` | `GET /signals`, or `GET /account/signals` with `asOwner` | feed key, or owner key |
116
+ | `client.followSignals({ after?, wait?, asOwner? })` | `GET /signals` page after page, or `GET /account/signals` with `asOwner` | feed key, or owner key |
117
+ | `client.createFeedKey(name, { expiresAt? })` | `POST /account/feed-keys` | owner key |
118
+ | `client.revokeFeedKey(id)` | `DELETE /account/feed-keys/{id}` | owner key |
119
+ | `client.createWebhook(url, { types? })` | `POST /account/webhooks` | owner key |
120
+ | `client.listWebhooks()` | `GET /account/webhooks` | owner key |
121
+ | `client.deleteWebhook(id)` | `DELETE /account/webhooks/{id}` | owner key |
122
+ | `client.listWebhookDeliveries(id, { limit? })` | `GET /account/webhooks/{id}/deliveries` | owner key |
123
+ | `client.getFailureReceipt(id)` | `GET /runs/{id}/failure-receipt` | required |
115
124
  | `client.adminCreateKey(name, { accountId? })` | `POST /admin/keys` | admin |
116
125
  | `client.adminListKeys()` | `GET /admin/keys` | admin |
117
126
  | `client.adminRevokeKey(id)` | `DELETE /admin/keys/{id}` | admin |
@@ -122,7 +131,7 @@ if (v.verified) {
122
131
 
123
132
  `client.job(jobId)` returns a [job helper](#jobs) and makes no request until a step runs.
124
133
 
125
- These functions make no request: `captureEnvironment`, `formatProviderPayload`, `signRequest`, `signRequestV2`, `requestKeyRegistrationBody`, `requestKeyChangeApproval`, `requestKeyChangePreimage`, `policyActivationApproval`, `policyActivationPreimage`, `signOffPreimage`, `approverKeyProof`, `requestSignaturePreimage`, `requestSignaturePreimageV2`, `generateRequestKeyPair`, `validateHex64`, `validateJobName`, `validateRouteId`, `dseDispatchHash`, `dcaeClosureHash` and `cresReceiptHash`. `RUN_STATUSES` lists every run status the server uses, and `EXECUTE_FIELDS` lists every top-level key a run request may hold.
134
+ These functions make no request: `captureEnvironment`, `formatProviderPayload`, `signRequest`, `signRequestV2`, `requestKeyRegistrationBody`, `requestKeyChangeApproval`, `requestKeyChangePreimage`, `policyActivationApproval`, `policyActivationPreimage`, `signOffPreimage`, `approverKeyProof`, `verifyWebhook`, `signalEventHash`, `requestSignaturePreimage`, `requestSignaturePreimageV2`, `generateRequestKeyPair`, `validateHex64`, `validateJobName`, `validateRouteId`, `dseDispatchHash`, `dcaeClosureHash` and `cresReceiptHash`. `RUN_STATUSES` lists every run status the server uses, and `EXECUTE_FIELDS` lists every top-level key a run request may hold.
126
135
 
127
136
  `getRunArtifacts`, `getPublicBundle` and `getPublicBundleByToken` return the exact JSON text the server sent, so you can save it and check it with `verdifax-verify`.
128
137
 
@@ -350,6 +359,72 @@ Verdifax did not review the result.
350
359
 
351
360
  Since v0.22.0, the verifier reads sign-offs with `--signoff` and `--signoff-history`.
352
361
 
362
+ ## Signal feed
363
+
364
+ The signal feed is every control signal Verdifax recorded for the account, in order: refused, halted and failed runs, policy changes, sign-offs, approver keys registered and revoked, and keys created and revoked. Each event is a CloudEvent that carries its sealed entry, its place in the feed and the hash of the event before it. The feed cannot show a signal Verdifax never recorded.
365
+
366
+ A feed key reads the feed and nothing else. The account's owner creates one with `createFeedKey` and revokes it with `revokeFeedKey`, and the owner key reads the same feed with `asOwner: true`.
367
+
368
+ ```typescript
369
+ import { writeFileSync } from "node:fs";
370
+ import { SignalFeedError, VerdifaxClient } from "verdifax";
371
+
372
+ const owner = new VerdifaxClient({ apiKey: process.env.VERDIFAX_OWNER_KEY! });
373
+ const { secret } = await owner.createFeedKey("grc reader"); // shown this once
374
+
375
+ const feed = new VerdifaxClient({ apiKey: secret });
376
+ let after = 0; // the last sequence your system stored
377
+ try {
378
+ for await (const event of feed.followSignals({ after })) {
379
+ console.log(event.data.sequence, event.type, event.data.reason ?? "");
380
+ after = event.data.sequence;
381
+ }
382
+ } catch (err) {
383
+ if (!(err instanceof SignalFeedError)) throw err;
384
+ after = err.lastSequence; // follow again from here and nothing is lost
385
+ }
386
+
387
+ // Save a page and check it with verdifax-verify --signals signals.json.
388
+ writeFileSync("signals.json", JSON.stringify(await feed.getSignals({ limit: 500 })));
389
+ ```
390
+
391
+ `followSignals` reads page after page and starts each read at the previous page's `next_after`, so it never skips an event, and it refuses a page that would skip or go back. When there is no new event, each read waits up to `wait` seconds (default 25) on the server. A failed read is retried like any GET. If it still fails, `followSignals` throws `SignalFeedError`, whose `lastSequence` is the last sequence it handed out and whose `cause` is the error the read threw. It runs until you stop it with `break` or `return`. `getSignals` reads one page: `after` is the last sequence read (default 0), `limit` is 1 to 500 and `wait` is 0 to 25 seconds. The timeout of a signals call is at least its wait plus 30 seconds, whatever the client's `timeoutMs`. Bad options are refused before anything is sent.
392
+
393
+ The owner key subscribes an https address with `createWebhook(url, { types })`, at most ten per account. `types` are the kinds to deliver, each once; empty or left out takes every kind. The secret, `whsec_` and base64, is in the result this once. Each delivery is signed under the Standard Webhooks scheme. Check it with `verifyWebhook` before you use it, passing the body exactly as received:
394
+
395
+ ```typescript
396
+ import http from "node:http";
397
+ import { verifyWebhook } from "verdifax";
398
+
399
+ http
400
+ .createServer((req, res) => {
401
+ const chunks: Buffer[] = [];
402
+ req.on("data", (chunk) => chunks.push(chunk));
403
+ req.on("end", () => {
404
+ try {
405
+ const event = verifyWebhook(req.headers, Buffer.concat(chunks), process.env.VERDIFAX_WEBHOOK_SECRET!);
406
+ console.log(event.data.sequence, event.type);
407
+ res.statusCode = 204;
408
+ } catch (err) {
409
+ console.error((err as Error).message); // names the check that failed, never the secret
410
+ res.statusCode = 400;
411
+ }
412
+ res.end();
413
+ });
414
+ })
415
+ .listen(8080);
416
+ ```
417
+
418
+ `verifyWebhook` reads the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers without regard to case. It refuses a timestamp more than `toleranceSeconds` (default 300) from `now` in either direction, accepts the delivery when any one `v1` signature matches, compares in constant time and returns the parsed CloudEvent. It throws `VerdifaxError` naming the check that failed, and never puts the secret in a message.
419
+
420
+ A webhook delivery is a copy: the pull feed and the public log are the record. A delivery can fail or arrive more than once, so store `data.sequence` and read the feed from the last one you stored. `listWebhookDeliveries(id, { limit })` shows each delivery's status and attempts, newest first. `deleteWebhook` ends a subscription, and its events stay in the feed.
421
+
422
+ `getFailureReceipt(runId)` returns a failed run's sealed failure receipt, in the form `verdifax-verify` reads. A run that failed before failure receipts were sealed has none, and the call throws `NotFoundError`.
423
+
424
+ `signalEventHash(kind, entryHash, { account, sequence, previousSignalHash })` recomputes an event's `event_hash`, the hash the next event names. It makes no request.
425
+
426
+ `verdifax-verify --signals FILE` checks a saved feed: each entry as its own kind, that the place each entry seals is the event's place, and that the numbers and links run without a gap.
427
+
353
428
  ## Evidence export
354
429
 
355
430
  `exportRuns` lists the runs the server holds for your key in a period, failures included, with up to 10,000 rows per export. `exportRunsCSV` returns the same rows as raw CSV text.
package/dist/index.d.mts CHANGED
@@ -881,6 +881,173 @@ interface ApproverRevocation {
881
881
  id: number;
882
882
  revoked: boolean;
883
883
  }
884
+ /** The kind of a control signal. Each is also a CloudEvents type,
885
+ * `com.verdifax.` and the kind. */
886
+ type SignalKind = "run.refused" | "run.halted" | "run.failed" | "policy.changed" | "signoff.recorded" | "approver.registered" | "approver.revoked" | "key.created" | "key.revoked";
887
+ /** The Verdifax event inside a {@link SignalEvent}, verdifax.signal.v1. */
888
+ interface SignalEventData {
889
+ /** `verdifax.signal.v1`. */
890
+ form: string;
891
+ kind: SignalKind | (string & {});
892
+ /** The account, `account-N`. */
893
+ account: string;
894
+ /** The event's place in the account's feed, from 1. */
895
+ sequence: number;
896
+ /** The previous event's `event_hash`, or 64 zeros for the account's
897
+ * first event. */
898
+ previous_signal_hash: Hex64;
899
+ /** {@link signalEventHash} of the kind, the entry's hash and the place. */
900
+ event_hash: Hex64;
901
+ run_id?: number;
902
+ api_key_id?: number;
903
+ /** A short reason a reader can route on, for a refused, halted or
904
+ * failed run. */
905
+ reason?: string;
906
+ /** The sealed entry the event names, in the exact form
907
+ * `verdifax-verify` reads. */
908
+ entry: Record<string, unknown>;
909
+ }
910
+ /** One control signal, as a CloudEvents 1.0 envelope. */
911
+ interface SignalEvent {
912
+ specversion: string;
913
+ /** `account-N/SEQUENCE`. */
914
+ id: string;
915
+ source: string;
916
+ /** `com.verdifax.` and the kind. */
917
+ type: string;
918
+ time: string;
919
+ datacontenttype: string;
920
+ data: SignalEventData;
921
+ }
922
+ /** One page of the feed. Returned by {@link VerdifaxClient.getSignals}. */
923
+ interface SignalPage {
924
+ /** The events after the place asked for, oldest first. */
925
+ events: SignalEvent[];
926
+ /** The place to read from next: the last event's sequence, or the
927
+ * place asked for when the page is empty. */
928
+ next_after: number;
929
+ /** The newest sequence in the account's feed, 0 when it is empty. */
930
+ newest: number;
931
+ }
932
+ /** Options for {@link VerdifaxClient.getSignals}. */
933
+ interface GetSignalsOptions {
934
+ /** The last sequence already read. 0, the default, reads from the start. */
935
+ after?: number;
936
+ /** 1 to 500 events. The server's default is 100. */
937
+ limit?: number;
938
+ /** 0 to 25 seconds to hold the request open when there is no event yet.
939
+ * Defaults to 0. */
940
+ wait?: number;
941
+ /** Read GET /account/signals with the owner key instead of GET /signals
942
+ * with a feed key. */
943
+ asOwner?: boolean;
944
+ }
945
+ /** Options for {@link VerdifaxClient.followSignals}. */
946
+ interface FollowSignalsOptions {
947
+ /** The last sequence already read. 0, the default, reads from the start. */
948
+ after?: number;
949
+ /** 0 to 25 seconds each read waits for the next event. Defaults to 25. */
950
+ wait?: number;
951
+ /** Read GET /account/signals with the owner key instead of GET /signals
952
+ * with a feed key. */
953
+ asOwner?: boolean;
954
+ }
955
+ /** Options for {@link VerdifaxClient.createFeedKey}. */
956
+ interface CreateFeedKeyOptions {
957
+ /** RFC 3339 or YYYY-MM-DD. */
958
+ expiresAt?: string;
959
+ }
960
+ /** A new feed key with its secret, which is shown this once. A feed key
961
+ * reads the account's signal feed and nothing else. Returned by
962
+ * {@link VerdifaxClient.createFeedKey}. */
963
+ type CreatedFeedKey = CreatedAccountKey;
964
+ /** Returned by {@link VerdifaxClient.revokeFeedKey}. */
965
+ interface FeedKeyRevocation {
966
+ ok: boolean;
967
+ id: number;
968
+ revoked: boolean;
969
+ }
970
+ /** Options for {@link VerdifaxClient.createWebhook}. */
971
+ interface CreateWebhookOptions {
972
+ /** The kinds to deliver, each once. Empty or left out takes every kind. */
973
+ types?: SignalKind[];
974
+ }
975
+ /** A webhook subscription, without its secret. */
976
+ interface Webhook {
977
+ id: number;
978
+ url: string;
979
+ /** The kinds it delivers. Empty means every kind. */
980
+ types: SignalKind[];
981
+ created_at: string;
982
+ }
983
+ /** A new webhook subscription with its secret, `whsec_` and base64,
984
+ * which is shown this once. Check each delivery with it using
985
+ * {@link verifyWebhook}. Returned by {@link VerdifaxClient.createWebhook}. */
986
+ interface CreatedWebhook extends Webhook {
987
+ ok: boolean;
988
+ secret: string;
989
+ hint: string;
990
+ }
991
+ /** Returned by {@link VerdifaxClient.listWebhooks}. */
992
+ interface WebhookList {
993
+ ok: boolean;
994
+ webhooks: Webhook[];
995
+ }
996
+ /** Returned by {@link VerdifaxClient.deleteWebhook}. */
997
+ interface WebhookDeletion {
998
+ ok: boolean;
999
+ id: number;
1000
+ deleted: boolean;
1001
+ }
1002
+ /** Options for {@link VerdifaxClient.listWebhookDeliveries}. */
1003
+ interface ListWebhookDeliveriesOptions {
1004
+ /** 1 to 500 deliveries. The server's default is 50. */
1005
+ limit?: number;
1006
+ }
1007
+ /** One delivery of one event to a webhook. A delivery is a copy: the pull
1008
+ * feed and the public log are the record. */
1009
+ interface WebhookDelivery {
1010
+ /** The event's place in the account's feed. */
1011
+ sequence: number;
1012
+ /** `pending`, `delivered` or `failed`. */
1013
+ status: string;
1014
+ attempts: number;
1015
+ last_status_code: number;
1016
+ last_error: string;
1017
+ created_at: string;
1018
+ delivered_at?: string;
1019
+ next_attempt_at?: string;
1020
+ }
1021
+ /** Returned by {@link VerdifaxClient.listWebhookDeliveries}: the newest
1022
+ * deliveries first. */
1023
+ interface WebhookDeliveryList {
1024
+ ok: boolean;
1025
+ webhook_id: number;
1026
+ deliveries: WebhookDelivery[];
1027
+ }
1028
+ /** The headers of a webhook delivery: a fetch `Headers`, or an object such
1029
+ * as Node's `req.headers`. Names are read without regard to case. */
1030
+ type WebhookHeaders = {
1031
+ get(name: string): string | null;
1032
+ } | Record<string, string | string[] | undefined>;
1033
+ /** Options for {@link verifyWebhook}. */
1034
+ interface VerifyWebhookOptions {
1035
+ /** How far, in seconds, `webhook-timestamp` may be from `now` in either
1036
+ * direction. Defaults to 300. */
1037
+ toleranceSeconds?: number;
1038
+ /** The time to check the timestamp against. Defaults to the current
1039
+ * time. */
1040
+ now?: Date;
1041
+ }
1042
+ /** An event's place in its account's feed, for {@link signalEventHash}. */
1043
+ interface SignalEventHashFields {
1044
+ /** The account, `account-N`. */
1045
+ account: string;
1046
+ /** The event's sequence, a whole number. */
1047
+ sequence: number;
1048
+ /** The previous event's hash, or 64 zeros for the account's first. */
1049
+ previousSignalHash: string;
1050
+ }
884
1051
 
885
1052
  /**
886
1053
  * The job helper: runs the steps of one job and keeps the map of step ids
@@ -1002,6 +1169,15 @@ declare const MAX_BATCH_ROWS = 1000;
1002
1169
  * | `getNewestSignOff(options)` | GET /signoffs/newest, or GET /account/signoffs/newest with asOwner | required |
1003
1170
  * | `getSignOffs(runId, options)` | GET /runs/{id}/signoffs, or GET /account/runs/{id}/signoffs with asOwner | required |
1004
1171
  * | `getSignOffHistory()` | GET /account/signoffs/history | owner key |
1172
+ * | `getSignals(options)` | GET /signals, or GET /account/signals with asOwner | feed key, or owner key |
1173
+ * | `followSignals(options)` | GET /signals page after page, or GET /account/signals with asOwner | feed key, or owner key |
1174
+ * | `createFeedKey(name, options)` | POST /account/feed-keys | owner key |
1175
+ * | `revokeFeedKey(id)` | DELETE /account/feed-keys/{id} | owner key |
1176
+ * | `createWebhook(url, options)` | POST /account/webhooks | owner key |
1177
+ * | `listWebhooks()` | GET /account/webhooks | owner key |
1178
+ * | `deleteWebhook(id)` | DELETE /account/webhooks/{id} | owner key |
1179
+ * | `listWebhookDeliveries(id, options)` | GET /account/webhooks/{id}/deliveries | owner key |
1180
+ * | `getFailureReceipt(id)` | GET /runs/{id}/failure-receipt | required |
1005
1181
  * | `job(jobId)` | none, returns a {@link VerdifaxJob} | none |
1006
1182
  *
1007
1183
  * With the `signingKey` option set, every path to POST /execute carries
@@ -1220,6 +1396,44 @@ declare class VerdifaxClient {
1220
1396
  /** The account's whole chain of sign-offs, oldest first, in the form
1221
1397
  * `verdifax-verify --signoff-history FILE` reads. Needs the owner key. */
1222
1398
  getSignOffHistory(): Promise<SignOffRecord[]>;
1399
+ /** One page of the account's feed: the events after `after`, oldest
1400
+ * first, with `next_after` to read from next. With `wait` the server
1401
+ * holds the request open up to that many seconds for the next event, and
1402
+ * this call's timeout is at least the wait plus 30 seconds. With
1403
+ * `asOwner` it reads GET /account/signals with the owner key. Bad
1404
+ * options are refused before sending. Save the events and check them
1405
+ * with `verdifax-verify --signals`. */
1406
+ getSignals(options?: GetSignalsOptions): Promise<SignalPage>;
1407
+ /** Follows the feed from `after`, yielding each event once, in order,
1408
+ * until you stop with `break` or `return`. Each read starts at the page
1409
+ * before's `next_after`, waits up to `wait` seconds (default 25) when
1410
+ * there is no event, and is retried like any GET. A read that still
1411
+ * fails throws {@link SignalFeedError}, whose `lastSequence` is the last
1412
+ * sequence yielded: follow again from there and nothing is lost. Bad
1413
+ * options are refused here, before anything is sent. */
1414
+ followSignals(options?: FollowSignalsOptions): AsyncGenerator<SignalEvent, void, undefined>;
1415
+ /** Creates a feed key in the owner's account. A feed key reads the
1416
+ * account's signal feed, with {@link getSignals} and
1417
+ * {@link followSignals}, and nothing else. The secret is in the result
1418
+ * this once. */
1419
+ createFeedKey(name: string, options?: CreateFeedKeyOptions): Promise<CreatedFeedKey>;
1420
+ /** Revokes one of the account's feed keys. An id that is not a feed key
1421
+ * of the account throws {@link NotFoundError}. */
1422
+ revokeFeedKey(id: number): Promise<FeedKeyRevocation>;
1423
+ /** Subscribes an https address to the account's signals. `types` are the
1424
+ * kinds to deliver, each once; empty or left out takes every kind. The
1425
+ * secret, `whsec_` and base64, is in the result this once: check each
1426
+ * delivery with {@link verifyWebhook}. A delivery is a copy: the pull
1427
+ * feed and the public log are the record. The server checks the address
1428
+ * too, and refuses an eleventh subscription with HTTP 409. */
1429
+ createWebhook(url: string, options?: CreateWebhookOptions): Promise<CreatedWebhook>;
1430
+ /** The account's webhook subscriptions, without their secrets. */
1431
+ listWebhooks(): Promise<WebhookList>;
1432
+ /** Ends a webhook subscription. Its events stay in the pull feed. */
1433
+ deleteWebhook(id: number): Promise<WebhookDeletion>;
1434
+ /** A webhook's newest deliveries first, up to `limit` (1 to 500; the
1435
+ * server's default is 50). */
1436
+ listWebhookDeliveries(id: number, options?: ListWebhookDeliveriesOptions): Promise<WebhookDeliveryList>;
1223
1437
  getRun(runId: number): Promise<Run>;
1224
1438
  verifyRun(runId: number): Promise<VerifyResponse>;
1225
1439
  listRuns(params?: ListRunsParams): Promise<{
@@ -1256,6 +1470,10 @@ declare class VerdifaxClient {
1256
1470
  getAivpT4HaltReceipt(runId: number): Promise<Record<string, unknown>>;
1257
1471
  getAttestationGapReceipt(runId: number): Promise<Record<string, unknown>>;
1258
1472
  getCESAuditRecord(runId: number): Promise<Record<string, unknown>>;
1473
+ /** A failed run's sealed failure receipt, in the form `verdifax-verify`
1474
+ * reads. A run that failed before failure receipts were sealed has none
1475
+ * and throws {@link NotFoundError}. */
1476
+ getFailureReceipt(runId: number): Promise<Record<string, unknown>>;
1259
1477
  /** The audit report for a run, as JSON. */
1260
1478
  getRunReport(runId: number): Promise<Record<string, unknown>>;
1261
1479
  /** The run's sealed audit bundle as the exact JSON text the server
@@ -1556,6 +1774,48 @@ declare function signOffPreimage(fields: {
1556
1774
  * never holds the approver's private key. */
1557
1775
  declare function approverKeyProof(approverKey: Uint8Array | KeyObject): ApproverKeyProof;
1558
1776
 
1777
+ /**
1778
+ * The signal feed: every control signal Verdifax recorded for an account,
1779
+ * in order, each with its sealed entry. The feed cannot show a signal
1780
+ * Verdifax never recorded.
1781
+ *
1782
+ * A feed key reads the feed and nothing else. The owner key reads the same
1783
+ * feed on its /account route and manages feed keys and webhooks. A webhook
1784
+ * delivery is a copy: the pull feed and the public log are the record.
1785
+ *
1786
+ * The request building, the checks made before sending, the follow loop,
1787
+ * webhook verification and the event hash live here; the client's methods
1788
+ * are thin wrappers over them.
1789
+ */
1790
+
1791
+ /**
1792
+ * Checks one webhook delivery under the Standard Webhooks scheme and
1793
+ * returns its CloudEvent. Pass the headers as received, the body as the
1794
+ * exact bytes received (a Buffer, a Uint8Array or a string, never parsed
1795
+ * JSON) and the subscription's `whsec_` secret.
1796
+ *
1797
+ * The signed content is `webhook-id`, a dot, `webhook-timestamp`, a dot and
1798
+ * the body. `webhook-signature` is a space-separated list of `v1,<base64>`
1799
+ * entries, and any one may match; each is compared in constant time. A
1800
+ * timestamp more than `toleranceSeconds` (default 300) from `now` in either
1801
+ * direction is refused. Header names are read without regard to case. The
1802
+ * body must be a JSON object whose top-level `id` is a string equal to
1803
+ * `webhook-id`.
1804
+ *
1805
+ * Throws {@link VerdifaxError} naming the check that failed. The secret is
1806
+ * never placed in an error message. A delivery is a copy: the pull feed and
1807
+ * the public log are the record.
1808
+ */
1809
+ declare function verifyWebhook(headers: WebhookHeaders, body: Uint8Array | string, secret: string, options?: VerifyWebhookOptions): SignalEvent;
1810
+ /**
1811
+ * The hash the next event in an account's feed names, matching the
1812
+ * orchestrator's SignalEventHash: hex SHA-256 of `verdifax.signal.v1`, the
1813
+ * kind, the hash of the event's entry, the account, the decimal sequence
1814
+ * and the previous event's hash, joined by single zero bytes. It makes no
1815
+ * request.
1816
+ */
1817
+ declare function signalEventHash(kind: string, entryHash: string, fields: SignalEventHashFields): string;
1818
+
1559
1819
  /**
1560
1820
  * Input-validation helpers, mirrors the Python SDK's
1561
1821
  * `_validation.py` and the Go SDK's `validation.go` for
@@ -1639,5 +1899,17 @@ declare class APIKeyRequiredError extends VerdifaxError {
1639
1899
  readonly name: string;
1640
1900
  constructor();
1641
1901
  }
1902
+ /** Thrown by {@link VerdifaxClient.followSignals} when a read fails or a
1903
+ * page does not follow on from the last one. `lastSequence` is the last
1904
+ * sequence the generator handed out, so the feed is never lost: follow
1905
+ * again with `after: lastSequence`. The error the read threw, after the
1906
+ * client's normal retries, is `cause`. */
1907
+ declare class SignalFeedError extends VerdifaxError {
1908
+ readonly lastSequence: number;
1909
+ readonly name: string;
1910
+ constructor(lastSequence: number, reason: string, options?: {
1911
+ cause?: unknown;
1912
+ });
1913
+ }
1642
1914
 
1643
- export { APIError, APIKeyRequiredError, type Account, type AccountInfo, type AccountKey, type AccountKeyList, type AccountKeyRevocation, type AccountRunsExport, type AdminAPIKey, type AdminCreateAccountOptions, type AdminCreateAccountResponse, type AdminCreateKeyOptions, type AdminCreateKeyResponse, type AdminEraseRequest, type Approver, type ApproverKeyProof, type ApproverList, type ApproverRegistration, type ApproverRevocation, type AsOwnerOptions, type AttestRequest, type AttestationList, AuthError, CRES_ENGINE_VERSION, CRES_PREIMAGE_VERSION, type ClientConfig, type CreateAccountKeyOptions, type CreatedAccountKey, type DCAEProofBundle, type DCAEVerdict, DCAE_PREIMAGE_VERSION, DSE_FORMULA_VERSION, type DeletionReceipt, type DeterminismDiff, type DeterminismRun, EXECUTE_FIELDS, type ExecuteBatchResponse, type ExecuteBatchResult, type ExecuteRequest, type ExecuteResponse, type ExecutionManifest, type ExportRow, type ExportRunsParams, type FormulaEra, type Health, type Hex64, type JobLink, type JobStepOptions, type KeyTier, type ListAttestationsParams, type ListRunsParams, MAX_BATCH_ROWS, type NewestSignOff, NotFoundError, type PDFFormat, type Policy, type PolicyActivation, type PolicyActivationSignature, type PolicyActorType, type PolicyClassification, type PolicyContext, type PolicyList, type PolicyObjectType, type PolicyTestRequest, type PolicyTestResult, type PolicyWithDocument, RUN_STATUSES, type ReproducibilityContext, type RequestKeyChange, type RequestKeyPair, type RequestKeyRegistration, type RequestKeyRegistrationBody, type RequestSignature, type RequestSignatureV2, type Run, type RunStats, type RunStatus, type RunSummary, type RunsExport, type RunsExportCSV, type SearchRunsParams, type SignOffDecision, type SignOffOptions, type SignOffRecord, type SignOffSignature, type SignOffTarget, type SignOffTargetKind, type SignedRequestBody, VerdifaxClient, VerdifaxError, VerdifaxJob, type VerdifaxVersion, type VerifyDeterminismResponse, type VerifyResponse, approverKeyProof, captureEnvironment, cresReceiptHash, dcaeClosureHash, dseDispatchHash, formatProviderPayload, fromEnv, generateRequestKeyPair, policyActivationApproval, policyActivationPreimage, requestKeyChangeApproval, requestKeyChangePreimage, requestKeyRegistrationBody, requestSignaturePreimage, requestSignaturePreimageV2, signOffPreimage, signRequest, signRequestV2, validateHex64, validateJobName, validateRouteId };
1915
+ export { APIError, APIKeyRequiredError, type Account, type AccountInfo, type AccountKey, type AccountKeyList, type AccountKeyRevocation, type AccountRunsExport, type AdminAPIKey, type AdminCreateAccountOptions, type AdminCreateAccountResponse, type AdminCreateKeyOptions, type AdminCreateKeyResponse, type AdminEraseRequest, type Approver, type ApproverKeyProof, type ApproverList, type ApproverRegistration, type ApproverRevocation, type AsOwnerOptions, type AttestRequest, type AttestationList, AuthError, CRES_ENGINE_VERSION, CRES_PREIMAGE_VERSION, type ClientConfig, type CreateAccountKeyOptions, type CreateFeedKeyOptions, type CreateWebhookOptions, type CreatedAccountKey, type CreatedFeedKey, type CreatedWebhook, type DCAEProofBundle, type DCAEVerdict, DCAE_PREIMAGE_VERSION, DSE_FORMULA_VERSION, type DeletionReceipt, type DeterminismDiff, type DeterminismRun, EXECUTE_FIELDS, type ExecuteBatchResponse, type ExecuteBatchResult, type ExecuteRequest, type ExecuteResponse, type ExecutionManifest, type ExportRow, type ExportRunsParams, type FeedKeyRevocation, type FollowSignalsOptions, type FormulaEra, type GetSignalsOptions, type Health, type Hex64, type JobLink, type JobStepOptions, type KeyTier, type ListAttestationsParams, type ListRunsParams, type ListWebhookDeliveriesOptions, MAX_BATCH_ROWS, type NewestSignOff, NotFoundError, type PDFFormat, type Policy, type PolicyActivation, type PolicyActivationSignature, type PolicyActorType, type PolicyClassification, type PolicyContext, type PolicyList, type PolicyObjectType, type PolicyTestRequest, type PolicyTestResult, type PolicyWithDocument, RUN_STATUSES, type ReproducibilityContext, type RequestKeyChange, type RequestKeyPair, type RequestKeyRegistration, type RequestKeyRegistrationBody, type RequestSignature, type RequestSignatureV2, type Run, type RunStats, type RunStatus, type RunSummary, type RunsExport, type RunsExportCSV, type SearchRunsParams, type SignOffDecision, type SignOffOptions, type SignOffRecord, type SignOffSignature, type SignOffTarget, type SignOffTargetKind, type SignalEvent, type SignalEventData, type SignalEventHashFields, SignalFeedError, type SignalKind, type SignalPage, type SignedRequestBody, VerdifaxClient, VerdifaxError, VerdifaxJob, type VerdifaxVersion, type VerifyDeterminismResponse, type VerifyResponse, type VerifyWebhookOptions, type Webhook, type WebhookDeletion, type WebhookDelivery, type WebhookDeliveryList, type WebhookHeaders, type WebhookList, approverKeyProof, captureEnvironment, cresReceiptHash, dcaeClosureHash, dseDispatchHash, formatProviderPayload, fromEnv, generateRequestKeyPair, policyActivationApproval, policyActivationPreimage, requestKeyChangeApproval, requestKeyChangePreimage, requestKeyRegistrationBody, requestSignaturePreimage, requestSignaturePreimageV2, signOffPreimage, signRequest, signRequestV2, signalEventHash, validateHex64, validateJobName, validateRouteId, verifyWebhook };