verdifax 1.2.0 → 1.4.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 +121 -3
- package/dist/index.d.mts +415 -4
- package/dist/index.d.ts +415 -4
- package/dist/index.js +554 -57
- package/dist/index.mjs +552 -56
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
VerdifaxClient: () => VerdifaxClient,
|
|
35
35
|
VerdifaxError: () => VerdifaxError,
|
|
36
36
|
VerdifaxJob: () => VerdifaxJob,
|
|
37
|
+
approverKeyProof: () => approverKeyProof,
|
|
37
38
|
captureEnvironment: () => captureEnvironment,
|
|
38
39
|
cresReceiptHash: () => cresReceiptHash,
|
|
39
40
|
dcaeClosureHash: () => dcaeClosureHash,
|
|
@@ -48,6 +49,7 @@ __export(index_exports, {
|
|
|
48
49
|
requestKeyRegistrationBody: () => requestKeyRegistrationBody,
|
|
49
50
|
requestSignaturePreimage: () => requestSignaturePreimage,
|
|
50
51
|
requestSignaturePreimageV2: () => requestSignaturePreimageV2,
|
|
52
|
+
signOffPreimage: () => signOffPreimage,
|
|
51
53
|
signRequest: () => signRequest,
|
|
52
54
|
signRequestV2: () => signRequestV2,
|
|
53
55
|
validateHex64: () => validateHex64,
|
|
@@ -389,7 +391,10 @@ var JOB_FORM = "verdifax.job.v1";
|
|
|
389
391
|
var REQUEST_KEY_PROOF_FORM = "verdifax.request-key.v1";
|
|
390
392
|
var REQUEST_KEY_CHANGE_FORM = "verdifax.request-key-change.v1";
|
|
391
393
|
var POLICY_ACTIVATION_FORM = "verdifax.policy-activation.v1";
|
|
394
|
+
var SIGNOFF_FORM = "verdifax.signoff.v1";
|
|
395
|
+
var APPROVER_KEY_PROOF_FORM = "verdifax.approver-key.v1";
|
|
392
396
|
var SIGNING_KEY_BYTES = 32;
|
|
397
|
+
var SPKI_ED25519_HEADER = Buffer.from("302a300506032b6570032100", "hex");
|
|
393
398
|
var PKCS8_ED25519_HEADER = Buffer.from(
|
|
394
399
|
"302e020100300506032b657004220420",
|
|
395
400
|
"hex"
|
|
@@ -573,6 +578,71 @@ function policyActivationApproval(signingKey, fields, options = {}) {
|
|
|
573
578
|
nonce
|
|
574
579
|
};
|
|
575
580
|
}
|
|
581
|
+
function signOffPreimage(fields) {
|
|
582
|
+
const { target } = fields;
|
|
583
|
+
return Buffer.from(
|
|
584
|
+
[
|
|
585
|
+
SIGNOFF_FORM,
|
|
586
|
+
fields.chain_id,
|
|
587
|
+
fields.previous_signoff_hash,
|
|
588
|
+
target.kind,
|
|
589
|
+
String(target.run_id),
|
|
590
|
+
target.digest,
|
|
591
|
+
target.job_id ?? "",
|
|
592
|
+
fields.decision,
|
|
593
|
+
fields.note_hash,
|
|
594
|
+
fields.signed_at,
|
|
595
|
+
fields.nonce
|
|
596
|
+
].join("\0"),
|
|
597
|
+
"utf8"
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
function signOffSignature(approverKey, fields, options = {}) {
|
|
601
|
+
const key = loadSigningKey(approverKey);
|
|
602
|
+
const signed_at = options.signedAt ?? signedAtNow();
|
|
603
|
+
const nonce = options.nonce ?? (0, import_node_crypto.randomBytes)(16).toString("hex");
|
|
604
|
+
const preimage = signOffPreimage({ ...fields, signed_at, nonce });
|
|
605
|
+
return {
|
|
606
|
+
approver_key: rawPublicKey((0, import_node_crypto.createPublicKey)(key)).toString("base64"),
|
|
607
|
+
signature: (0, import_node_crypto.sign)(null, preimage, key).toString("base64"),
|
|
608
|
+
signed_at,
|
|
609
|
+
nonce
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function approverKeyProof(approverKey) {
|
|
613
|
+
const key = loadSigningKey(approverKey);
|
|
614
|
+
const public_key = rawPublicKey((0, import_node_crypto.createPublicKey)(key)).toString("base64");
|
|
615
|
+
const proof = (0, import_node_crypto.sign)(
|
|
616
|
+
null,
|
|
617
|
+
Buffer.from(`${APPROVER_KEY_PROOF_FORM}\0${public_key}`, "utf8"),
|
|
618
|
+
key
|
|
619
|
+
).toString("base64");
|
|
620
|
+
return { public_key, proof };
|
|
621
|
+
}
|
|
622
|
+
function approverKeyProofVerifies(proof) {
|
|
623
|
+
if (typeof proof?.public_key !== "string" || typeof proof?.proof !== "string") {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
const raw = Buffer.from(proof.public_key, "base64");
|
|
627
|
+
const signature = Buffer.from(proof.proof, "base64");
|
|
628
|
+
if (raw.length !== 32 || raw.toString("base64") !== proof.public_key || signature.length !== 64 || signature.toString("base64") !== proof.proof) {
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
const publicKey = (0, import_node_crypto.createPublicKey)({
|
|
632
|
+
key: Buffer.concat([SPKI_ED25519_HEADER, raw]),
|
|
633
|
+
format: "der",
|
|
634
|
+
type: "spki"
|
|
635
|
+
});
|
|
636
|
+
return (0, import_node_crypto.verify)(
|
|
637
|
+
null,
|
|
638
|
+
Buffer.from(`${APPROVER_KEY_PROOF_FORM}\0${proof.public_key}`, "utf8"),
|
|
639
|
+
publicKey,
|
|
640
|
+
signature
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
function publicKeyOf(key) {
|
|
644
|
+
return rawPublicKey((0, import_node_crypto.createPublicKey)(key)).toString("base64");
|
|
645
|
+
}
|
|
576
646
|
|
|
577
647
|
// src/job.ts
|
|
578
648
|
var STEP_OPTIONS = {
|
|
@@ -674,9 +744,10 @@ var VerdifaxJob = class {
|
|
|
674
744
|
};
|
|
675
745
|
|
|
676
746
|
// src/version.ts
|
|
677
|
-
var SDK_VERSION = "1.
|
|
747
|
+
var SDK_VERSION = "1.4.0";
|
|
678
748
|
|
|
679
749
|
// src/client.ts
|
|
750
|
+
var import_node_crypto2 = require("crypto");
|
|
680
751
|
var DEFAULT_BASE_URL = "https://api.verdifax.com";
|
|
681
752
|
var DEFAULT_TIMEOUT_MS = 9e4;
|
|
682
753
|
var DEFAULT_MAX_RETRIES = 3;
|
|
@@ -685,6 +756,16 @@ var SDK_USER_AGENT = `verdifax-sdk-node/${SDK_VERSION}`;
|
|
|
685
756
|
var HEADER_API_KEY = "X-Verdifax-Key";
|
|
686
757
|
var REQUEST_KEY_PATH = "/keys/request-key";
|
|
687
758
|
var POLICIES_PATH = "/policies";
|
|
759
|
+
var ACCOUNT_PATH = "/account";
|
|
760
|
+
var ACCOUNT_REQUEST_KEY_PATH = "/account/request-key";
|
|
761
|
+
var ACCOUNT_POLICIES_PATH = "/account/policies";
|
|
762
|
+
var ACCOUNT_CHAIN_ID = /^account-[1-9][0-9]*$/;
|
|
763
|
+
var SIGNOFFS_PATH = "/signoffs";
|
|
764
|
+
var ACCOUNT_SIGNOFFS_PATH = "/account/signoffs";
|
|
765
|
+
var APPROVERS_PATH = "/account/approvers";
|
|
766
|
+
var SIGNOFF_TARGET_KINDS = /* @__PURE__ */ new Set(["record", "receipt", "job"]);
|
|
767
|
+
var SIGNOFF_DECISIONS = /* @__PURE__ */ new Set(["approve", "reject"]);
|
|
768
|
+
var MAX_APPROVER_NAME_BYTES = 200;
|
|
688
769
|
var HEADER_EXPORT_TOTAL = "X-Verdifax-Total";
|
|
689
770
|
var HEADER_EXPORT_TRUNCATED = "X-Verdifax-Truncated";
|
|
690
771
|
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
@@ -870,15 +951,18 @@ var VerdifaxClient = class {
|
|
|
870
951
|
* that the client holds the private key. From then on the server
|
|
871
952
|
* refuses a run on this API key without a request signature. */
|
|
872
953
|
async registerRequestKey() {
|
|
954
|
+
return this.registerRequestKeyAt(REQUEST_KEY_PATH, "registerRequestKey");
|
|
955
|
+
}
|
|
956
|
+
async registerRequestKeyAt(path, method) {
|
|
873
957
|
this.requireAPIKey();
|
|
874
958
|
if (!this.signingKey) {
|
|
875
959
|
throw new VerdifaxError(
|
|
876
|
-
|
|
960
|
+
`verdifax: ${method} needs the signingKey client option`
|
|
877
961
|
);
|
|
878
962
|
}
|
|
879
963
|
return this.request(
|
|
880
964
|
"POST",
|
|
881
|
-
|
|
965
|
+
path,
|
|
882
966
|
requestKeyRegistrationBody(this.signingKey)
|
|
883
967
|
);
|
|
884
968
|
}
|
|
@@ -917,10 +1001,13 @@ var VerdifaxClient = class {
|
|
|
917
1001
|
* unsigned requests, because the server refuses a signed run on an API
|
|
918
1002
|
* key with no registered key. If the call fails it keeps the key. */
|
|
919
1003
|
async clearRequestKey() {
|
|
1004
|
+
await this.clearRequestKeyAt(REQUEST_KEY_PATH);
|
|
1005
|
+
}
|
|
1006
|
+
async clearRequestKeyAt(path) {
|
|
920
1007
|
this.requireAPIKey();
|
|
921
1008
|
await this.request(
|
|
922
1009
|
"DELETE",
|
|
923
|
-
|
|
1010
|
+
path,
|
|
924
1011
|
this.signingKey ? { change: requestKeyChangeApproval(this.signingKey, "") } : void 0
|
|
925
1012
|
);
|
|
926
1013
|
this.signingKey = void 0;
|
|
@@ -932,13 +1019,7 @@ var VerdifaxClient = class {
|
|
|
932
1019
|
* not a string: serializing an object here would change the hash.
|
|
933
1020
|
* Verdifax does not write, review or approve a customer's policy. */
|
|
934
1021
|
async uploadPolicy(document) {
|
|
935
|
-
this.
|
|
936
|
-
if (typeof document !== "string") {
|
|
937
|
-
throw new VerdifaxError(
|
|
938
|
-
"verdifax: document must be the policy's exact text, as a string"
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
return this.request("POST", POLICIES_PATH, { document });
|
|
1022
|
+
return this.uploadPolicyAt(POLICIES_PATH, document);
|
|
942
1023
|
}
|
|
943
1024
|
/** This key's stored documents, with the chain its changes belong to,
|
|
944
1025
|
* the newest change in it (64 zeros when there is none yet) and the
|
|
@@ -949,44 +1030,13 @@ var VerdifaxClient = class {
|
|
|
949
1030
|
}
|
|
950
1031
|
/** One stored document, with its exact text as `document`. */
|
|
951
1032
|
async getPolicy(documentHash) {
|
|
952
|
-
this.
|
|
953
|
-
validateHex64(documentHash, "documentHash");
|
|
954
|
-
return this.request(
|
|
955
|
-
"GET",
|
|
956
|
-
`${POLICIES_PATH}/${documentHash}`
|
|
957
|
-
);
|
|
1033
|
+
return this.getPolicyAt(POLICIES_PATH, documentHash);
|
|
958
1034
|
}
|
|
959
1035
|
/** Shows which rule would fire on sample inputs, for a document given as
|
|
960
1036
|
* text or one already stored, named by its hash: exactly one of the two.
|
|
961
1037
|
* It seals nothing. */
|
|
962
1038
|
async testPolicy(req) {
|
|
963
|
-
this.
|
|
964
|
-
const { inputs, document, documentHash } = req ?? {};
|
|
965
|
-
if (document === void 0 === (documentHash === void 0)) {
|
|
966
|
-
throw new VerdifaxError(
|
|
967
|
-
"verdifax: testPolicy needs exactly one of document or documentHash"
|
|
968
|
-
);
|
|
969
|
-
}
|
|
970
|
-
if (typeof inputs !== "object" || inputs === null || Array.isArray(inputs)) {
|
|
971
|
-
throw new VerdifaxError("verdifax: testPolicy needs inputs as an object");
|
|
972
|
-
}
|
|
973
|
-
const body = { inputs };
|
|
974
|
-
if (document !== void 0) {
|
|
975
|
-
if (typeof document !== "string") {
|
|
976
|
-
throw new VerdifaxError(
|
|
977
|
-
"verdifax: document must be the policy's exact text, as a string"
|
|
978
|
-
);
|
|
979
|
-
}
|
|
980
|
-
body.document = document;
|
|
981
|
-
} else {
|
|
982
|
-
validateHex64(documentHash, "documentHash");
|
|
983
|
-
body.document_hash = documentHash;
|
|
984
|
-
}
|
|
985
|
-
return this.request(
|
|
986
|
-
"POST",
|
|
987
|
-
`${POLICIES_PATH}/test`,
|
|
988
|
-
body
|
|
989
|
-
);
|
|
1039
|
+
return this.testPolicyAt(POLICIES_PATH, req, "testPolicy");
|
|
990
1040
|
}
|
|
991
1041
|
/** This key's policy changes, oldest first, as the list the server sends.
|
|
992
1042
|
* Save it as JSON, for example with
|
|
@@ -1012,7 +1062,54 @@ var VerdifaxClient = class {
|
|
|
1012
1062
|
async activatePolicy(documentHash) {
|
|
1013
1063
|
this.requireAPIKey();
|
|
1014
1064
|
validateHex64(documentHash, "documentHash");
|
|
1015
|
-
|
|
1065
|
+
return this.activatePolicyAt(
|
|
1066
|
+
POLICIES_PATH,
|
|
1067
|
+
documentHash,
|
|
1068
|
+
await this.listPolicies()
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
1071
|
+
async uploadPolicyAt(base, document) {
|
|
1072
|
+
this.requireAPIKey();
|
|
1073
|
+
if (typeof document !== "string") {
|
|
1074
|
+
throw new VerdifaxError(
|
|
1075
|
+
"verdifax: document must be the policy's exact text, as a string"
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
return this.request("POST", base, { document });
|
|
1079
|
+
}
|
|
1080
|
+
async getPolicyAt(base, documentHash) {
|
|
1081
|
+
this.requireAPIKey();
|
|
1082
|
+
validateHex64(documentHash, "documentHash");
|
|
1083
|
+
return this.request("GET", `${base}/${documentHash}`);
|
|
1084
|
+
}
|
|
1085
|
+
async testPolicyAt(base, req, method) {
|
|
1086
|
+
this.requireAPIKey();
|
|
1087
|
+
const { inputs, document, documentHash } = req ?? {};
|
|
1088
|
+
if (document === void 0 === (documentHash === void 0)) {
|
|
1089
|
+
throw new VerdifaxError(
|
|
1090
|
+
`verdifax: ${method} needs exactly one of document or documentHash`
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
if (typeof inputs !== "object" || inputs === null || Array.isArray(inputs)) {
|
|
1094
|
+
throw new VerdifaxError(`verdifax: ${method} needs inputs as an object`);
|
|
1095
|
+
}
|
|
1096
|
+
const body = { inputs };
|
|
1097
|
+
if (document !== void 0) {
|
|
1098
|
+
if (typeof document !== "string") {
|
|
1099
|
+
throw new VerdifaxError(
|
|
1100
|
+
"verdifax: document must be the policy's exact text, as a string"
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
body.document = document;
|
|
1104
|
+
} else {
|
|
1105
|
+
validateHex64(documentHash, "documentHash");
|
|
1106
|
+
body.document_hash = documentHash;
|
|
1107
|
+
}
|
|
1108
|
+
return this.request("POST", `${base}/test`, body);
|
|
1109
|
+
}
|
|
1110
|
+
/** Posts the change that follows the newest one in `chain`, signed by
|
|
1111
|
+
* the request key when the client has one. */
|
|
1112
|
+
async activatePolicyAt(base, documentHash, chain) {
|
|
1016
1113
|
const previous = chain?.newest_activation_hash;
|
|
1017
1114
|
if (typeof previous !== "string") {
|
|
1018
1115
|
throw new VerdifaxError(
|
|
@@ -1034,11 +1131,315 @@ var VerdifaxClient = class {
|
|
|
1034
1131
|
}
|
|
1035
1132
|
const out = await this.request(
|
|
1036
1133
|
"POST",
|
|
1037
|
-
`${
|
|
1134
|
+
`${base}/${documentHash}/activate`,
|
|
1038
1135
|
body
|
|
1039
1136
|
);
|
|
1040
1137
|
return out.activation;
|
|
1041
1138
|
}
|
|
1139
|
+
// ── Account, with its owner key (auth) ───────────────────────
|
|
1140
|
+
//
|
|
1141
|
+
// An account is a company on Verdifax. Every key belongs to one account
|
|
1142
|
+
// for life. These methods need the account's owner key, which manages the
|
|
1143
|
+
// account and cannot run anything: the server refuses it on every other
|
|
1144
|
+
// route. The account number is a label Verdifax assigned, not a verified
|
|
1145
|
+
// company identity.
|
|
1146
|
+
/** The owner's account: its number, name, tier, key counts and the
|
|
1147
|
+
* chain of its policy changes. */
|
|
1148
|
+
async getAccount() {
|
|
1149
|
+
this.requireAPIKey();
|
|
1150
|
+
return this.request("GET", ACCOUNT_PATH);
|
|
1151
|
+
}
|
|
1152
|
+
/** Creates a member key in the account. It takes the account's tier and
|
|
1153
|
+
* stays in the account for life. The secret is in the result this once.
|
|
1154
|
+
* On a trial account the key needs a request key before it can run. */
|
|
1155
|
+
async createAccountKey(name, options = {}) {
|
|
1156
|
+
this.requireAPIKey();
|
|
1157
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
1158
|
+
throw new VerdifaxError(
|
|
1159
|
+
"verdifax: name is required for createAccountKey"
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
const body = { name };
|
|
1163
|
+
if (options.expiresAt !== void 0) body.expires_at = options.expiresAt;
|
|
1164
|
+
return this.request(
|
|
1165
|
+
"POST",
|
|
1166
|
+
`${ACCOUNT_PATH}/keys`,
|
|
1167
|
+
body
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
/** Every key in the account, owner keys included, without secrets. */
|
|
1171
|
+
async listAccountKeys() {
|
|
1172
|
+
this.requireAPIKey();
|
|
1173
|
+
const out = await this.request(
|
|
1174
|
+
"GET",
|
|
1175
|
+
`${ACCOUNT_PATH}/keys`
|
|
1176
|
+
);
|
|
1177
|
+
return { ...out, keys: out?.keys ?? [] };
|
|
1178
|
+
}
|
|
1179
|
+
/** Revokes one of the account's member keys. A key in another account,
|
|
1180
|
+
* or an owner key, throws {@link NotFoundError}. */
|
|
1181
|
+
async revokeAccountKey(keyId) {
|
|
1182
|
+
this.requireAPIKey();
|
|
1183
|
+
requirePositiveId(keyId, "keyId");
|
|
1184
|
+
return this.request(
|
|
1185
|
+
"DELETE",
|
|
1186
|
+
`${ACCOUNT_PATH}/keys/${keyId}`
|
|
1187
|
+
);
|
|
1188
|
+
}
|
|
1189
|
+
/** Registers this client's public key as the owner key's request key,
|
|
1190
|
+
* which must sign every change to the account's policy. It works as
|
|
1191
|
+
* {@link registerRequestKey} does. */
|
|
1192
|
+
async registerAccountRequestKey() {
|
|
1193
|
+
return this.registerRequestKeyAt(
|
|
1194
|
+
ACCOUNT_REQUEST_KEY_PATH,
|
|
1195
|
+
"registerAccountRequestKey"
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
/** Clears the owner key's request key, as {@link clearRequestKey} does
|
|
1199
|
+
* for a key's own. With none registered the account's policy cannot be
|
|
1200
|
+
* changed. A trial owner key's request key cannot be cleared. */
|
|
1201
|
+
async clearAccountRequestKey() {
|
|
1202
|
+
await this.clearRequestKeyAt(ACCOUNT_REQUEST_KEY_PATH);
|
|
1203
|
+
}
|
|
1204
|
+
/** The runs of every key in the account, paged as {@link listRuns}. */
|
|
1205
|
+
async listAccountRuns(params = {}) {
|
|
1206
|
+
return this.listRunsAt(`${ACCOUNT_PATH}/runs`, params);
|
|
1207
|
+
}
|
|
1208
|
+
/** The runs of every key in the account in a period, as
|
|
1209
|
+
* {@link exportRuns} lists a key's, with the account's number, its
|
|
1210
|
+
* policy documents and its chain of changes. The account's view covers
|
|
1211
|
+
* the runs its keys made through Verdifax, not runs made elsewhere. */
|
|
1212
|
+
async exportAccountRuns(params = {}) {
|
|
1213
|
+
this.requireAPIKey();
|
|
1214
|
+
return this.request(
|
|
1215
|
+
"GET",
|
|
1216
|
+
this.exportPath(`${ACCOUNT_PATH}/runs/export`, params, "json")
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
/** The same rows as {@link exportAccountRuns} as raw CSV text, checked
|
|
1220
|
+
* as {@link exportRunsCSV} checks a key's. */
|
|
1221
|
+
async exportAccountRunsCSV(params = {}) {
|
|
1222
|
+
return this.exportCSVAt(`${ACCOUNT_PATH}/runs/export`, params);
|
|
1223
|
+
}
|
|
1224
|
+
/** Stores an account policy document, as {@link uploadPolicy} stores a
|
|
1225
|
+
* key's. Every key in the account must pass the account's policy on top
|
|
1226
|
+
* of its own. Verdifax does not write, review or approve it. */
|
|
1227
|
+
async uploadAccountPolicy(document) {
|
|
1228
|
+
return this.uploadPolicyAt(ACCOUNT_POLICIES_PATH, document);
|
|
1229
|
+
}
|
|
1230
|
+
/** The account's stored documents, its chain `account-N`, the newest
|
|
1231
|
+
* change in it and the document in force. */
|
|
1232
|
+
async listAccountPolicies() {
|
|
1233
|
+
this.requireAPIKey();
|
|
1234
|
+
return this.request("GET", ACCOUNT_POLICIES_PATH);
|
|
1235
|
+
}
|
|
1236
|
+
/** One of the account's documents, with its exact text. */
|
|
1237
|
+
async getAccountPolicy(documentHash) {
|
|
1238
|
+
return this.getPolicyAt(ACCOUNT_POLICIES_PATH, documentHash);
|
|
1239
|
+
}
|
|
1240
|
+
/** {@link testPolicy} for the account's documents. It seals nothing. */
|
|
1241
|
+
async testAccountPolicy(req) {
|
|
1242
|
+
return this.testPolicyAt(ACCOUNT_POLICIES_PATH, req, "testAccountPolicy");
|
|
1243
|
+
}
|
|
1244
|
+
/** The account's policy changes, oldest first, in the form
|
|
1245
|
+
* `verdifax-verify --policy-history FILE` reads. */
|
|
1246
|
+
async getAccountPolicyHistory() {
|
|
1247
|
+
this.requireAPIKey();
|
|
1248
|
+
return this.request(
|
|
1249
|
+
"GET",
|
|
1250
|
+
`${ACCOUNT_POLICIES_PATH}/history`
|
|
1251
|
+
);
|
|
1252
|
+
}
|
|
1253
|
+
/** Makes a stored document the account's policy in force, as
|
|
1254
|
+
* {@link activatePolicy} does for a key's. It reads
|
|
1255
|
+
* {@link listAccountPolicies} for the chain and its newest change, and
|
|
1256
|
+
* the owner key's request key signs the change over that chain,
|
|
1257
|
+
* `account-N`. The server refuses an unsigned account change, so this
|
|
1258
|
+
* needs the `signingKey` option and refuses to send without it. */
|
|
1259
|
+
async activateAccountPolicy(documentHash) {
|
|
1260
|
+
this.requireAPIKey();
|
|
1261
|
+
validateHex64(documentHash, "documentHash");
|
|
1262
|
+
if (!this.signingKey) {
|
|
1263
|
+
throw new VerdifaxError(
|
|
1264
|
+
"verdifax: activateAccountPolicy needs the owner key's request key in the signingKey client option, because the server refuses an unsigned account policy change"
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
const chain = await this.listAccountPolicies();
|
|
1268
|
+
if (typeof chain?.chain_id !== "string" || !ACCOUNT_CHAIN_ID.test(chain.chain_id)) {
|
|
1269
|
+
throw new VerdifaxError(
|
|
1270
|
+
"verdifax: the server did not report an account chain_id (account-N), so the change cannot be signed"
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
return this.activatePolicyAt(ACCOUNT_POLICIES_PATH, documentHash, chain);
|
|
1274
|
+
}
|
|
1275
|
+
// ── Sign-off ─────────────────────────────────────────────────
|
|
1276
|
+
//
|
|
1277
|
+
// An account's owner registers approver keys. An approver signs a
|
|
1278
|
+
// decision, approve or reject, on one exact result on their own machine,
|
|
1279
|
+
// and any key in the account, or the owner key with asOwner, submits it.
|
|
1280
|
+
// A sign-off shows that the holder of a registered approver key signed
|
|
1281
|
+
// that decision on that exact result and that Verdifax logged it on the
|
|
1282
|
+
// public log. It does not show the review was careful, the result right,
|
|
1283
|
+
// or who the person is, and it gates nothing on its own.
|
|
1284
|
+
/** Registers an approver key to the owner's account. `approver` is the
|
|
1285
|
+
* approver's signing key, from which the proof is made on this machine,
|
|
1286
|
+
* or the `{ public_key, proof }` that {@link approverKeyProof} made on
|
|
1287
|
+
* the approver's own machine, so the owner need not hold the private
|
|
1288
|
+
* key. A proof that does not verify is refused before sending. The
|
|
1289
|
+
* server refuses a key that is a request key in the same account.
|
|
1290
|
+
* `name` is a label, not an identity. */
|
|
1291
|
+
async registerApprover(name, approver) {
|
|
1292
|
+
this.requireAPIKey();
|
|
1293
|
+
if (typeof name !== "string" || name.trim() === "" || Buffer.byteLength(name.trim(), "utf8") > MAX_APPROVER_NAME_BYTES) {
|
|
1294
|
+
throw new VerdifaxError(
|
|
1295
|
+
"verdifax: registerApprover needs a name of at most 200 bytes"
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
const body = approver instanceof Uint8Array || approver instanceof import_node_crypto2.KeyObject ? approverKeyProof(approver) : { public_key: approver?.public_key, proof: approver?.proof };
|
|
1299
|
+
if (!approverKeyProofVerifies(body)) {
|
|
1300
|
+
throw new VerdifaxError(
|
|
1301
|
+
"verdifax: registerApprover needs the approver's signing key, or a public_key and proof from approverKeyProof that verify"
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
if (this.signingKey && publicKeyOf(this.signingKey) === body.public_key) {
|
|
1305
|
+
throw new VerdifaxError(
|
|
1306
|
+
"verdifax: an approver key must not be a request key in the same account, and this is the client's signingKey"
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
return this.request("POST", APPROVERS_PATH, {
|
|
1310
|
+
public_key: body.public_key,
|
|
1311
|
+
name,
|
|
1312
|
+
proof: body.proof
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
/** The account's approver keys, revoked ones included. */
|
|
1316
|
+
async listApprovers() {
|
|
1317
|
+
this.requireAPIKey();
|
|
1318
|
+
const out = await this.request("GET", APPROVERS_PATH);
|
|
1319
|
+
return { ...out, approvers: out?.approvers ?? [] };
|
|
1320
|
+
}
|
|
1321
|
+
/** Revokes one of the account's approver keys. Its earlier sign-offs
|
|
1322
|
+
* stay on the record. A key that is not an active approver in the
|
|
1323
|
+
* account throws {@link NotFoundError}. */
|
|
1324
|
+
async revokeApprover(approverId) {
|
|
1325
|
+
this.requireAPIKey();
|
|
1326
|
+
requirePositiveId(approverId, "approverId");
|
|
1327
|
+
return this.request(
|
|
1328
|
+
"DELETE",
|
|
1329
|
+
`${APPROVERS_PATH}/${approverId}`
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
/** What an approver signs against: the account's chain `account-N` and
|
|
1333
|
+
* its newest sign-off (64 zeros when there is none yet). With
|
|
1334
|
+
* `asOwner` it reads the owner key's route. */
|
|
1335
|
+
async getNewestSignOff(options = {}) {
|
|
1336
|
+
this.requireAPIKey();
|
|
1337
|
+
return this.request(
|
|
1338
|
+
"GET",
|
|
1339
|
+
`${signOffsBase(options, "getNewestSignOff")}/newest`
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
/** Signs an approver's decision, approve or reject, on one exact result
|
|
1343
|
+
* and submits it. It first reads {@link getNewestSignOff} for the
|
|
1344
|
+
* account's chain and its newest sign-off, then signs over them with
|
|
1345
|
+
* `approverKey` on this machine. The approver's private key is never
|
|
1346
|
+
* sent.
|
|
1347
|
+
*
|
|
1348
|
+
* `target.digest` is what the result's public-log entry covers: for a
|
|
1349
|
+
* record, the `bundle_anchor.anchored_digest` of the bundle
|
|
1350
|
+
* {@link getRunArtifacts} returns; for a receipt, its hash; for a job,
|
|
1351
|
+
* the final step's record digest, read as a record's, with `job_id`.
|
|
1352
|
+
*
|
|
1353
|
+
* `note` is hashed here and only its SHA-256 is sent and signed: the
|
|
1354
|
+
* text never reaches Verdifax. With `asOwner` it uses the owner key's
|
|
1355
|
+
* routes. Bad input is refused before anything is sent.
|
|
1356
|
+
*
|
|
1357
|
+
* The record the server returns must carry the signature,
|
|
1358
|
+
* approver_key, decision, previous_signoff_hash and target that were
|
|
1359
|
+
* sent, or this throws {@link VerdifaxError}.
|
|
1360
|
+
*
|
|
1361
|
+
* A later sign-off on the same result supersedes this one, and neither
|
|
1362
|
+
* is deleted. An HTTP 409 means the chain moved or the target is not
|
|
1363
|
+
* this account's result: it throws the usual {@link APIError} and is
|
|
1364
|
+
* not retried. */
|
|
1365
|
+
async signOff(target, decision, approverKey, options = {}) {
|
|
1366
|
+
this.requireAPIKey();
|
|
1367
|
+
const base = signOffsBase(options, "signOff");
|
|
1368
|
+
const exact = signOffTargetOf(target);
|
|
1369
|
+
if (!SIGNOFF_DECISIONS.has(decision)) {
|
|
1370
|
+
throw new VerdifaxError(
|
|
1371
|
+
"verdifax: signOff decision must be approve or reject"
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
const note = options?.note;
|
|
1375
|
+
if (note !== void 0 && (typeof note !== "string" || note === "")) {
|
|
1376
|
+
throw new VerdifaxError(
|
|
1377
|
+
"verdifax: signOff note must be non-empty text when given"
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
const key = loadSigningKey(approverKey);
|
|
1381
|
+
const note_hash = note === void 0 ? "" : sha256Hex(note);
|
|
1382
|
+
const newest = await this.getNewestSignOff(options);
|
|
1383
|
+
if (typeof newest?.chain_id !== "string" || !ACCOUNT_CHAIN_ID.test(newest.chain_id)) {
|
|
1384
|
+
throw new VerdifaxError(
|
|
1385
|
+
"verdifax: the server did not report an account chain_id (account-N), so the sign-off cannot be signed"
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
const previous = newest.newest_signoff_hash;
|
|
1389
|
+
if (typeof previous !== "string" || !/^[0-9a-f]{64}$/.test(previous)) {
|
|
1390
|
+
throw new VerdifaxError(
|
|
1391
|
+
"verdifax: the server did not report newest_signoff_hash, so the sign-off cannot name the one it follows"
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
const signed = signOffSignature(key, {
|
|
1395
|
+
chain_id: newest.chain_id,
|
|
1396
|
+
previous_signoff_hash: previous,
|
|
1397
|
+
target: exact,
|
|
1398
|
+
decision,
|
|
1399
|
+
note_hash
|
|
1400
|
+
});
|
|
1401
|
+
const body = {
|
|
1402
|
+
previous_signoff_hash: previous,
|
|
1403
|
+
target: exact,
|
|
1404
|
+
decision,
|
|
1405
|
+
...note_hash ? { note_hash } : {},
|
|
1406
|
+
...signed
|
|
1407
|
+
};
|
|
1408
|
+
const out = await this.request(
|
|
1409
|
+
"POST",
|
|
1410
|
+
base,
|
|
1411
|
+
body
|
|
1412
|
+
);
|
|
1413
|
+
const record = out?.signoff;
|
|
1414
|
+
if (!recordMatchesSent(record, exact, decision, previous, signed)) {
|
|
1415
|
+
throw new VerdifaxError(
|
|
1416
|
+
"verdifax: the sign-off the server returned does not carry the signature, approver_key, decision, previous_signoff_hash and target that were sent"
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
return record;
|
|
1420
|
+
}
|
|
1421
|
+
/** The sign-offs on one run, oldest first, in the form
|
|
1422
|
+
* `verdifax-verify --signoff FILE` reads: for a member key, a run it
|
|
1423
|
+
* made; with `asOwner`, any run of the account. The latest is the
|
|
1424
|
+
* decision in force. */
|
|
1425
|
+
async getSignOffs(runId, options = {}) {
|
|
1426
|
+
this.requireAPIKey();
|
|
1427
|
+
requirePositiveId(runId, "runId");
|
|
1428
|
+
const base = asOwnerOf(options, "getSignOffs") ? `${ACCOUNT_PATH}/runs` : "/runs";
|
|
1429
|
+
return await this.request(
|
|
1430
|
+
"GET",
|
|
1431
|
+
`${base}/${runId}/signoffs`
|
|
1432
|
+
) ?? [];
|
|
1433
|
+
}
|
|
1434
|
+
/** The account's whole chain of sign-offs, oldest first, in the form
|
|
1435
|
+
* `verdifax-verify --signoff-history FILE` reads. Needs the owner key. */
|
|
1436
|
+
async getSignOffHistory() {
|
|
1437
|
+
this.requireAPIKey();
|
|
1438
|
+
return await this.request(
|
|
1439
|
+
"GET",
|
|
1440
|
+
`${ACCOUNT_SIGNOFFS_PATH}/history`
|
|
1441
|
+
) ?? [];
|
|
1442
|
+
}
|
|
1042
1443
|
// ── Runs ──────────────────────────────────────────────────────
|
|
1043
1444
|
async getRun(runId) {
|
|
1044
1445
|
this.requireAPIKey();
|
|
@@ -1051,6 +1452,9 @@ var VerdifaxClient = class {
|
|
|
1051
1452
|
return this.request("GET", `/runs/${runId}/verify`);
|
|
1052
1453
|
}
|
|
1053
1454
|
async listRuns(params = {}) {
|
|
1455
|
+
return this.listRunsAt("/runs", params);
|
|
1456
|
+
}
|
|
1457
|
+
async listRunsAt(base, params) {
|
|
1054
1458
|
this.requireAPIKey();
|
|
1055
1459
|
const q = new URLSearchParams();
|
|
1056
1460
|
if (params.limit && params.limit > 0) {
|
|
@@ -1060,7 +1464,7 @@ var VerdifaxClient = class {
|
|
|
1060
1464
|
q.set("offset", String(params.offset));
|
|
1061
1465
|
}
|
|
1062
1466
|
const qs = q.toString();
|
|
1063
|
-
const path = qs ?
|
|
1467
|
+
const path = qs ? `${base}?${qs}` : base;
|
|
1064
1468
|
const out = await this.request("GET", path);
|
|
1065
1469
|
return { runs: out.runs ?? [], total: out.total ?? 0 };
|
|
1066
1470
|
}
|
|
@@ -1098,7 +1502,10 @@ var VerdifaxClient = class {
|
|
|
1098
1502
|
* call that never reached Verdifax cannot appear in it. */
|
|
1099
1503
|
async exportRuns(params = {}) {
|
|
1100
1504
|
this.requireAPIKey();
|
|
1101
|
-
return this.request(
|
|
1505
|
+
return this.request(
|
|
1506
|
+
"GET",
|
|
1507
|
+
this.exportPath("/runs/export", params, "json")
|
|
1508
|
+
);
|
|
1102
1509
|
}
|
|
1103
1510
|
/** The same rows as {@link exportRuns}: the raw CSV text with `total`
|
|
1104
1511
|
* and `truncated` from the response headers. Check `truncated`: when
|
|
@@ -1106,9 +1513,12 @@ var VerdifaxClient = class {
|
|
|
1106
1513
|
* does not send those headers, so that a cut-off export cannot pass
|
|
1107
1514
|
* for a complete one. */
|
|
1108
1515
|
async exportRunsCSV(params = {}) {
|
|
1516
|
+
return this.exportCSVAt("/runs/export", params);
|
|
1517
|
+
}
|
|
1518
|
+
async exportCSVAt(base, params) {
|
|
1109
1519
|
this.requireAPIKey();
|
|
1110
1520
|
const { text, headers } = await this.requestRaw(
|
|
1111
|
-
this.exportPath(params, "csv")
|
|
1521
|
+
this.exportPath(base, params, "csv")
|
|
1112
1522
|
);
|
|
1113
1523
|
const total = Number.parseInt(headers.get(HEADER_EXPORT_TOTAL) ?? "", 10);
|
|
1114
1524
|
const truncated = headers.get(HEADER_EXPORT_TRUNCATED);
|
|
@@ -1119,13 +1529,13 @@ var VerdifaxClient = class {
|
|
|
1119
1529
|
}
|
|
1120
1530
|
return { csv: text, total, truncated: truncated === "true" };
|
|
1121
1531
|
}
|
|
1122
|
-
exportPath(params, format) {
|
|
1532
|
+
exportPath(base, params, format) {
|
|
1123
1533
|
const q = new URLSearchParams();
|
|
1124
1534
|
if (params.from) q.set("from", params.from);
|
|
1125
1535
|
if (params.to) q.set("to", params.to);
|
|
1126
1536
|
if (params.job_id) q.set("job_id", params.job_id);
|
|
1127
1537
|
q.set("format", format);
|
|
1128
|
-
return
|
|
1538
|
+
return `${base}?${q.toString()}`;
|
|
1129
1539
|
}
|
|
1130
1540
|
// ── Attestation receipts (auth) ──────────────────────────────
|
|
1131
1541
|
async listAttestations(params = {}) {
|
|
@@ -1263,14 +1673,48 @@ var VerdifaxClient = class {
|
|
|
1263
1673
|
return this.request("POST", "/dcae/verify", bundle);
|
|
1264
1674
|
}
|
|
1265
1675
|
// ── Admin: API keys (auth) ───────────────────────────────────
|
|
1266
|
-
|
|
1676
|
+
/** Creates a customer key. With `accountId` the key joins that existing
|
|
1677
|
+
* account; without it the key gets an account of its own. A key never
|
|
1678
|
+
* changes account. */
|
|
1679
|
+
async adminCreateKey(name, options = {}) {
|
|
1267
1680
|
this.requireAPIKey();
|
|
1268
1681
|
if (!name) {
|
|
1269
1682
|
throw new VerdifaxError("verdifax: name is required for adminCreateKey");
|
|
1270
1683
|
}
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1684
|
+
const body = { name };
|
|
1685
|
+
if (options.accountId !== void 0) {
|
|
1686
|
+
requirePositiveId(options.accountId, "accountId");
|
|
1687
|
+
body.account_id = options.accountId;
|
|
1688
|
+
}
|
|
1689
|
+
return this.request("POST", "/admin/keys", body);
|
|
1690
|
+
}
|
|
1691
|
+
/** Creates an account and its first owner key, whose secret is in the
|
|
1692
|
+
* result this once. The owner key creates the member keys. The account
|
|
1693
|
+
* number is a label Verdifax assigned, not a verified company identity. */
|
|
1694
|
+
async adminCreateAccount(name, options = {}) {
|
|
1695
|
+
this.requireAPIKey();
|
|
1696
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
1697
|
+
throw new VerdifaxError(
|
|
1698
|
+
"verdifax: name is required for adminCreateAccount"
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
const body = { name };
|
|
1702
|
+
if (options.tier !== void 0) body.tier = options.tier;
|
|
1703
|
+
if (options.expiresAt !== void 0) body.expires_at = options.expiresAt;
|
|
1704
|
+
return this.request(
|
|
1705
|
+
"POST",
|
|
1706
|
+
"/admin/accounts",
|
|
1707
|
+
body
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
/** Every account, with its key counts. */
|
|
1711
|
+
async adminListAccounts() {
|
|
1712
|
+
this.requireAPIKey();
|
|
1713
|
+
const out = await this.request(
|
|
1714
|
+
"GET",
|
|
1715
|
+
"/admin/accounts"
|
|
1716
|
+
);
|
|
1717
|
+
return out?.accounts ?? [];
|
|
1274
1718
|
}
|
|
1275
1719
|
async adminListKeys() {
|
|
1276
1720
|
this.requireAPIKey();
|
|
@@ -1449,6 +1893,57 @@ var VerdifaxClient = class {
|
|
|
1449
1893
|
return this.signingKey ? signRequestV2(this.signingKey, req) : withExecuteFlags(req);
|
|
1450
1894
|
}
|
|
1451
1895
|
};
|
|
1896
|
+
function signOffsBase(options, method) {
|
|
1897
|
+
return asOwnerOf(options, method) ? ACCOUNT_SIGNOFFS_PATH : SIGNOFFS_PATH;
|
|
1898
|
+
}
|
|
1899
|
+
function asOwnerOf(options, method) {
|
|
1900
|
+
const asOwner = options?.asOwner;
|
|
1901
|
+
if (asOwner !== void 0 && typeof asOwner !== "boolean") {
|
|
1902
|
+
throw new VerdifaxError(`verdifax: ${method} asOwner must be true or false`);
|
|
1903
|
+
}
|
|
1904
|
+
return asOwner === true;
|
|
1905
|
+
}
|
|
1906
|
+
function signOffTargetOf(target) {
|
|
1907
|
+
if (typeof target !== "object" || target === null || Array.isArray(target)) {
|
|
1908
|
+
throw new VerdifaxError("verdifax: signOff needs a target object");
|
|
1909
|
+
}
|
|
1910
|
+
for (const name of Object.keys(target)) {
|
|
1911
|
+
if (!["kind", "run_id", "digest", "job_id"].includes(name)) {
|
|
1912
|
+
throw new VerdifaxError(`verdifax: signOff target has an unknown field: ${name}`);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
if (!SIGNOFF_TARGET_KINDS.has(target.kind)) {
|
|
1916
|
+
throw new VerdifaxError(
|
|
1917
|
+
"verdifax: signOff target kind must be record, receipt or job"
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1920
|
+
requirePositiveId(target.run_id, "target.run_id");
|
|
1921
|
+
validateHex64(target.digest, "target.digest");
|
|
1922
|
+
if (target.kind === "job") {
|
|
1923
|
+
validateJobName(target.job_id, "target.job_id");
|
|
1924
|
+
return {
|
|
1925
|
+
kind: target.kind,
|
|
1926
|
+
run_id: target.run_id,
|
|
1927
|
+
digest: target.digest,
|
|
1928
|
+
job_id: target.job_id
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
if (target.job_id !== void 0) {
|
|
1932
|
+
throw new VerdifaxError(
|
|
1933
|
+
"verdifax: signOff target job_id is for a job target only"
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
return { kind: target.kind, run_id: target.run_id, digest: target.digest };
|
|
1937
|
+
}
|
|
1938
|
+
function recordMatchesSent(record, target, decision, previous, signed) {
|
|
1939
|
+
const got = record?.target;
|
|
1940
|
+
return typeof record === "object" && record !== null && record.signature === signed.signature && record.approver_key === signed.approver_key && record.decision === decision && record.previous_signoff_hash === previous && typeof got === "object" && got !== null && got.kind === target.kind && got.run_id === target.run_id && got.digest === target.digest && (got.job_id ?? "") === (target.job_id ?? "");
|
|
1941
|
+
}
|
|
1942
|
+
function requirePositiveId(id, name) {
|
|
1943
|
+
if (!Number.isSafeInteger(id) || id <= 0) {
|
|
1944
|
+
throw new VerdifaxError(`verdifax: ${name} must be a whole number > 0`);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1452
1947
|
function withSignedText(result, sent) {
|
|
1453
1948
|
return "signed_request" in sent ? { ...result, signed_request: sent.signed_request } : result;
|
|
1454
1949
|
}
|
|
@@ -1574,13 +2069,13 @@ var RUN_STATUSES = [
|
|
|
1574
2069
|
];
|
|
1575
2070
|
|
|
1576
2071
|
// src/section0.ts
|
|
1577
|
-
var
|
|
2072
|
+
var import_node_crypto3 = require("crypto");
|
|
1578
2073
|
var DSE_FORMULA_VERSION = "dse.tcu.dispatch.v1";
|
|
1579
2074
|
var DCAE_PREIMAGE_VERSION = "verdifax.dcae.v1";
|
|
1580
2075
|
var CRES_PREIMAGE_VERSION = "verdifax.cres.v1";
|
|
1581
2076
|
var CRES_ENGINE_VERSION = "verdifax-cres/1.0.0";
|
|
1582
2077
|
function sha256Hex2(preimage) {
|
|
1583
|
-
return (0,
|
|
2078
|
+
return (0, import_node_crypto3.createHash)("sha256").update(Buffer.from(preimage, "utf8")).digest("hex");
|
|
1584
2079
|
}
|
|
1585
2080
|
function requireHex64(value, name) {
|
|
1586
2081
|
try {
|
|
@@ -1650,6 +2145,7 @@ function cresReceiptHash(fields) {
|
|
|
1650
2145
|
VerdifaxClient,
|
|
1651
2146
|
VerdifaxError,
|
|
1652
2147
|
VerdifaxJob,
|
|
2148
|
+
approverKeyProof,
|
|
1653
2149
|
captureEnvironment,
|
|
1654
2150
|
cresReceiptHash,
|
|
1655
2151
|
dcaeClosureHash,
|
|
@@ -1664,6 +2160,7 @@ function cresReceiptHash(fields) {
|
|
|
1664
2160
|
requestKeyRegistrationBody,
|
|
1665
2161
|
requestSignaturePreimage,
|
|
1666
2162
|
requestSignaturePreimageV2,
|
|
2163
|
+
signOffPreimage,
|
|
1667
2164
|
signRequest,
|
|
1668
2165
|
signRequestV2,
|
|
1669
2166
|
validateHex64,
|