@opendatalabs/vana-sdk 3.20.1 → 3.22.0-pr.207.0a5d07b

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.
Files changed (69) hide show
  1. package/README.md +64 -0
  2. package/dist/crypto/envelope/job.cjs +263 -0
  3. package/dist/crypto/envelope/job.cjs.map +1 -0
  4. package/dist/crypto/envelope/job.d.ts +122 -0
  5. package/dist/crypto/envelope/job.js +240 -0
  6. package/dist/crypto/envelope/job.js.map +1 -0
  7. package/dist/direct/access-request-client.cjs +100 -2
  8. package/dist/direct/access-request-client.cjs.map +1 -1
  9. package/dist/direct/access-request-client.d.ts +23 -1
  10. package/dist/direct/access-request-client.js +98 -1
  11. package/dist/direct/access-request-client.js.map +1 -1
  12. package/dist/direct/controller.cjs +4 -0
  13. package/dist/direct/controller.cjs.map +1 -1
  14. package/dist/direct/controller.d.ts +10 -1
  15. package/dist/direct/controller.js +6 -1
  16. package/dist/direct/controller.js.map +1 -1
  17. package/dist/direct/types.cjs.map +1 -1
  18. package/dist/direct/types.d.ts +45 -0
  19. package/dist/direct/types.js.map +1 -1
  20. package/dist/errors.cjs +77 -0
  21. package/dist/errors.cjs.map +1 -1
  22. package/dist/errors.d.ts +123 -0
  23. package/dist/errors.js +67 -0
  24. package/dist/errors.js.map +1 -1
  25. package/dist/index.browser.d.ts +5 -1
  26. package/dist/index.browser.js +721 -24
  27. package/dist/index.browser.js.map +4 -4
  28. package/dist/index.node.cjs +1309 -107
  29. package/dist/index.node.cjs.map +4 -4
  30. package/dist/index.node.d.ts +6 -1
  31. package/dist/index.node.js +1261 -97
  32. package/dist/index.node.js.map +4 -4
  33. package/dist/protocol/derivative-questions.cjs +22 -0
  34. package/dist/protocol/derivative-questions.cjs.map +1 -1
  35. package/dist/protocol/derivative-questions.d.ts +53 -0
  36. package/dist/protocol/derivative-questions.js +19 -0
  37. package/dist/protocol/derivative-questions.js.map +1 -1
  38. package/dist/protocol/derivative-status.cjs +209 -0
  39. package/dist/protocol/derivative-status.cjs.map +1 -0
  40. package/dist/protocol/derivative-status.d.ts +196 -0
  41. package/dist/protocol/derivative-status.js +190 -0
  42. package/dist/protocol/derivative-status.js.map +1 -0
  43. package/dist/protocol/derivative-status.test.d.ts +1 -0
  44. package/dist/protocol/identity.cjs +219 -0
  45. package/dist/protocol/identity.cjs.map +1 -0
  46. package/dist/protocol/identity.d.ts +193 -0
  47. package/dist/protocol/identity.js +183 -0
  48. package/dist/protocol/identity.js.map +1 -0
  49. package/dist/protocol/identity.test.d.ts +1 -0
  50. package/dist/protocol/identity.vector.test.d.ts +1 -0
  51. package/dist/protocol/jobs-client.cjs +493 -0
  52. package/dist/protocol/jobs-client.cjs.map +1 -0
  53. package/dist/protocol/jobs-client.d.ts +167 -0
  54. package/dist/protocol/jobs-client.js +498 -0
  55. package/dist/protocol/jobs-client.js.map +1 -0
  56. package/dist/protocol/jobs-client.test.d.ts +1 -0
  57. package/dist/protocol/jobs.cjs +67 -0
  58. package/dist/protocol/jobs.cjs.map +1 -0
  59. package/dist/protocol/jobs.d.ts +185 -0
  60. package/dist/protocol/jobs.js +33 -0
  61. package/dist/protocol/jobs.js.map +1 -0
  62. package/dist/protocol/jobs.test.d.ts +1 -0
  63. package/dist/server.cjs +4 -2
  64. package/dist/server.cjs.map +1 -1
  65. package/dist/server.d.ts +2 -2
  66. package/dist/server.js +4 -2
  67. package/dist/server.js.map +1 -1
  68. package/dist/tests/mock-personal-server.d.ts +9 -0
  69. package/package.json +7 -1
package/README.md CHANGED
@@ -621,6 +621,70 @@ These helpers require `personal-server-ts` main `d91124d` or later, which is
621
621
  where the query-in-the-signed-uri rule, the `nonce` claim, the 404 for an
622
622
  unknown id and the full-view `recompute` answer landed.
623
623
 
624
+ ### Watching a derived scope as the reader
625
+
626
+ The helpers above are the builder's: every one of them needs a write session,
627
+ which an app holding only a bare read entry on the derived scope cannot open.
628
+ That reader sees `GET /v1/data/<derivedScope>` answer 404 whether the compute
629
+ is running, retrying, or finished failing.
630
+
631
+ `getDerivativeStatus` is the reader's view of the same question. It
632
+ authenticates like a data read — a live grant covering the derived scope, or
633
+ the owner — and nothing is charged, so a priced grant raises no 402 here.
634
+
635
+ ```typescript
636
+ import {
637
+ getDerivativeStatus,
638
+ waitForDerivativeStatus,
639
+ } from "@opendatalabs/vana-sdk";
640
+
641
+ const status = await getDerivativeStatus({
642
+ personalServerUrl: "https://ps.example.com",
643
+ derivedScope: "coach.weekly",
644
+ grantId,
645
+ signer,
646
+ });
647
+ // { derivedScope, status, lastComputedAt, derivedVersion,
648
+ // derivedCollectedAt, errorCode, retryAfterSeconds }
649
+
650
+ const settled = await waitForDerivativeStatus({
651
+ personalServerUrl: "https://ps.example.com",
652
+ derivedScope: "coach.weekly",
653
+ grantId,
654
+ signer,
655
+ timeoutMs: 60_000,
656
+ });
657
+ ```
658
+
659
+ The view is lifecycle only: the question text, the source scopes, the question
660
+ id, the registrar and the server's raw `error` string stay owner-only.
661
+ `errorCode` is a closed vocabulary — `inference_unavailable`,
662
+ `source_missing`, `grant_invalid`, `internal` — and is `null` unless `status`
663
+ is `failed`.
664
+
665
+ `retryAfterSeconds` is what separates a failure that is still being worked on
666
+ from one that is over: `inference_unavailable` is the one transient class, and
667
+ the Personal Server retries it on its own schedule. `waitForDerivativeStatus`
668
+ returns as soon as the scope is `ready` or has failed with no retry pending,
669
+ keeps waiting through a retrying failure, and takes the server's
670
+ `retryAfterSeconds` as the cadence in place of `pollIntervalMs`, longer or
671
+ shorter — it is when the next compute actually happens, so asking sooner sees
672
+ nothing new and asking later sits on an answer that already exists. Once the
673
+ remaining budget cannot cover the next cadence it raises the timeout rather
674
+ than spending one more request that cannot carry new data. `signal` aborts
675
+ the wait and the request in flight with it. A failed status is returned, not thrown; branch
676
+ on `errorCode`. `isDerivativeStatusSettled` is the same predicate, exported
677
+ for callers that poll on their own.
678
+
679
+ When several questions write the same derived scope, the most optimistic true
680
+ state answers (`ready`, then `stale`, then `pending`, then `failed`), because
681
+ serving data is registration-agnostic: a duplicate that never wrote anything
682
+ must not report away an answer the scope has.
683
+
684
+ The status route needs a Personal Server that ships it; an older one answers
685
+ 404 for the route itself, which arrives as `DerivativeQuestionNotFoundError`
686
+ — the same error as a covered scope with no question behind it.
687
+
624
688
  ## Networks
625
689
 
626
690
  | Network | Chain ID | RPC URL |
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var job_exports = {};
20
+ __export(job_exports, {
21
+ JobEnvelopeError: () => JobEnvelopeError,
22
+ canonicalJobRequestBytes: () => canonicalJobRequestBytes,
23
+ openJobRequest: () => openJobRequest,
24
+ openJobResult: () => openJobResult,
25
+ sealJobRequest: () => sealJobRequest,
26
+ sealJobResult: () => sealJobResult
27
+ });
28
+ module.exports = __toCommonJS(job_exports);
29
+ var import_sha2 = require("@noble/hashes/sha2");
30
+ var import_viem = require("viem");
31
+ var import_interface = require("../ecies/interface");
32
+ var import_jobs = require("../../protocol/jobs");
33
+ var import_encoding = require("../../utils/encoding");
34
+ const textDecoder = new TextDecoder();
35
+ const textEncoder = new TextEncoder();
36
+ class JobEnvelopeError extends Error {
37
+ constructor(message) {
38
+ super(message);
39
+ this.name = "JobEnvelopeError";
40
+ }
41
+ }
42
+ function sortJsonKeys(value) {
43
+ if (Array.isArray(value)) {
44
+ return value.map(sortJsonKeys);
45
+ }
46
+ if (value !== null && typeof value === "object") {
47
+ return Object.fromEntries(
48
+ Object.keys(value).sort().map((key) => [
49
+ key,
50
+ sortJsonKeys(value[key])
51
+ ])
52
+ );
53
+ }
54
+ return value;
55
+ }
56
+ function canonicalJsonBytes(value) {
57
+ return textEncoder.encode(JSON.stringify(sortJsonKeys(value)));
58
+ }
59
+ function canonicalJobRequestBytes(request) {
60
+ validateJobRequest(request);
61
+ return canonicalJsonBytes(request);
62
+ }
63
+ function requestPlaintext(envelope) {
64
+ validateRequestEnvelope(envelope);
65
+ return canonicalJsonBytes(envelope);
66
+ }
67
+ function resultPlaintext(result) {
68
+ return textEncoder.encode(
69
+ JSON.stringify({
70
+ v: result.v,
71
+ jobId: result.jobId,
72
+ scope: result.scope,
73
+ version: result.version,
74
+ contentType: result.contentType,
75
+ body: result.body
76
+ })
77
+ );
78
+ }
79
+ function encryptedBytesToBase64(encrypted) {
80
+ return (0, import_encoding.toBase64)((0, import_viem.fromHex)(`0x${(0, import_interface.serializeECIES)(encrypted)}`, "bytes"));
81
+ }
82
+ function base64ToEncrypted(ciphertext) {
83
+ return (0, import_interface.deserializeECIES)((0, import_viem.toHex)((0, import_encoding.fromBase64)(ciphertext)));
84
+ }
85
+ function parseObject(plaintext, kind) {
86
+ try {
87
+ const value = JSON.parse(textDecoder.decode(plaintext));
88
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
89
+ throw new JobEnvelopeError(`Invalid ${kind}: expected an object`);
90
+ }
91
+ return value;
92
+ } catch (error) {
93
+ if (error instanceof JobEnvelopeError) throw error;
94
+ throw new JobEnvelopeError(`Invalid ${kind}: malformed JSON`);
95
+ }
96
+ }
97
+ function isPlainObject(value) {
98
+ if (value === null || typeof value !== "object") return false;
99
+ const prototype = Object.getPrototypeOf(value);
100
+ return prototype === Object.prototype || prototype === null;
101
+ }
102
+ function requirePlainObject(value, field, kind) {
103
+ if (!isPlainObject(value)) {
104
+ throw new JobEnvelopeError(`Invalid ${kind}: invalid ${field}`);
105
+ }
106
+ }
107
+ function requireExactKeys(value, expectedKeys, kind) {
108
+ for (const key of expectedKeys) {
109
+ if (!Object.hasOwn(value, key) || value[key] === void 0) {
110
+ throw new JobEnvelopeError(`Invalid ${kind}: missing ${key}`);
111
+ }
112
+ }
113
+ const expected = new Set(expectedKeys);
114
+ for (const key of Reflect.ownKeys(value)) {
115
+ if (!expected.has(key)) {
116
+ throw new JobEnvelopeError(
117
+ `Invalid ${kind}: unknown field ${String(key)}`
118
+ );
119
+ }
120
+ }
121
+ }
122
+ function requireString(value, field, kind) {
123
+ if (typeof value !== "string" || value.length === 0) {
124
+ throw new JobEnvelopeError(`Invalid ${kind}: missing ${field}`);
125
+ }
126
+ }
127
+ function validateJobRequest(value) {
128
+ requirePlainObject(value, "request", "job request");
129
+ requireExactKeys(
130
+ value,
131
+ [
132
+ "v",
133
+ "jobId",
134
+ "owner",
135
+ "builder",
136
+ "builderPublicKey",
137
+ "grantId",
138
+ "scope",
139
+ "operation",
140
+ "pinnedVersion",
141
+ "deadline"
142
+ ],
143
+ "job request"
144
+ );
145
+ if (value.v !== import_jobs.JOB_PROTOCOL_VERSION) {
146
+ throw new JobEnvelopeError(
147
+ `Unsupported job request version: ${String(value.v)}`
148
+ );
149
+ }
150
+ requireString(value.jobId, "jobId", "job request");
151
+ if (typeof value.owner !== "string" || !(0, import_viem.isAddress)(value.owner)) {
152
+ throw new JobEnvelopeError("Invalid job request: invalid owner");
153
+ }
154
+ if (typeof value.builder !== "string" || !(0, import_viem.isAddress)(value.builder)) {
155
+ throw new JobEnvelopeError("Invalid job request: invalid builder");
156
+ }
157
+ if (typeof value.builderPublicKey !== "string" || !(0, import_viem.isHex)(value.builderPublicKey)) {
158
+ throw new JobEnvelopeError("Invalid job request: invalid builderPublicKey");
159
+ }
160
+ if (typeof value.grantId !== "string" || !(0, import_viem.isHex)(value.grantId)) {
161
+ throw new JobEnvelopeError("Invalid job request: invalid grantId");
162
+ }
163
+ requireString(value.scope, "scope", "job request");
164
+ if (!import_jobs.JOB_OPERATIONS.includes(value.operation)) {
165
+ throw new JobEnvelopeError("Invalid job request: invalid operation");
166
+ }
167
+ if (value.pinnedVersion !== null && typeof value.pinnedVersion !== "string") {
168
+ throw new JobEnvelopeError("Invalid job request: missing pinnedVersion");
169
+ }
170
+ if (typeof value.deadline !== "string" || !Number.isFinite(Date.parse(value.deadline))) {
171
+ throw new JobEnvelopeError("Invalid job request: invalid deadline");
172
+ }
173
+ return value;
174
+ }
175
+ function validateRequestEnvelope(value) {
176
+ requirePlainObject(value, "envelope", "job request envelope");
177
+ requireExactKeys(value, ["request", "auth"], "job request envelope");
178
+ validateJobRequest(value.request);
179
+ requireString(value.auth, "auth", "job request envelope");
180
+ return value;
181
+ }
182
+ function validateResult(value) {
183
+ requirePlainObject(value, "result", "job result");
184
+ requireExactKeys(
185
+ value,
186
+ ["v", "jobId", "scope", "version", "contentType", "body"],
187
+ "job result"
188
+ );
189
+ if (value.v !== import_jobs.JOB_PROTOCOL_VERSION) {
190
+ throw new JobEnvelopeError(
191
+ `Unsupported job result version: ${String(value.v)}`
192
+ );
193
+ }
194
+ for (const field of ["jobId", "scope", "contentType"]) {
195
+ requireString(value[field], field, "job result");
196
+ }
197
+ if (typeof value.body !== "string") {
198
+ throw new JobEnvelopeError("Invalid job result: missing body");
199
+ }
200
+ if (value.version !== null && typeof value.version !== "string") {
201
+ throw new JobEnvelopeError("Invalid job result: missing version");
202
+ }
203
+ return value;
204
+ }
205
+ async function sealJobRequest(envelope, enclavePublicKey, ecies) {
206
+ const encrypted = await ecies.encrypt(
207
+ (0, import_viem.fromHex)(enclavePublicKey, "bytes"),
208
+ requestPlaintext(envelope)
209
+ );
210
+ return encryptedBytesToBase64(encrypted);
211
+ }
212
+ async function openJobRequest(ciphertext, privateKey, ecies) {
213
+ const plaintext = await ecies.decrypt(
214
+ privateKey,
215
+ base64ToEncrypted(ciphertext)
216
+ );
217
+ return validateRequestEnvelope(
218
+ parseObject(plaintext, "job request envelope")
219
+ );
220
+ }
221
+ async function sealJobResult(result, builderPublicKey, ecies) {
222
+ validateResult(result);
223
+ const encrypted = await ecies.encrypt(
224
+ (0, import_viem.fromHex)(builderPublicKey, "bytes"),
225
+ resultPlaintext(result)
226
+ );
227
+ const ciphertext = encryptedBytesToBase64(encrypted);
228
+ const bytes = (0, import_encoding.fromBase64)(ciphertext);
229
+ return { ciphertext, hash: (0, import_viem.bytesToHex)((0, import_sha2.sha256)(bytes)), size: bytes.length };
230
+ }
231
+ async function openJobResult(ciphertext, builderPrivateKey, ecies, expect) {
232
+ const plaintext = await ecies.decrypt(
233
+ (0, import_viem.fromHex)(builderPrivateKey, "bytes"),
234
+ base64ToEncrypted(ciphertext)
235
+ );
236
+ const result = validateResult(parseObject(plaintext, "job result"));
237
+ if (result.jobId !== expect.jobId) {
238
+ throw new JobEnvelopeError(
239
+ `Job result ID ${result.jobId} does not match expected job ID ${expect.jobId}`
240
+ );
241
+ }
242
+ if (expect.scope !== void 0 && result.scope !== expect.scope) {
243
+ throw new JobEnvelopeError(
244
+ `Job result scope ${result.scope} does not match expected scope ${expect.scope}`
245
+ );
246
+ }
247
+ if (expect.version !== void 0 && result.version !== expect.version) {
248
+ throw new JobEnvelopeError(
249
+ `Job result version ${String(result.version)} does not match expected version ${String(expect.version)}`
250
+ );
251
+ }
252
+ return result;
253
+ }
254
+ // Annotate the CommonJS export names for ESM import in node:
255
+ 0 && (module.exports = {
256
+ JobEnvelopeError,
257
+ canonicalJobRequestBytes,
258
+ openJobRequest,
259
+ openJobResult,
260
+ sealJobRequest,
261
+ sealJobResult
262
+ });
263
+ //# sourceMappingURL=job.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/crypto/envelope/job.ts"],"sourcesContent":["/**\n * ECIES envelopes for encrypted job requests and results.\n *\n * Request plaintext is UTF-8 JSON with object keys sorted recursively and\n * array order preserved. Result properties follow their interface order. The\n * wire ciphertext is base64 of `iv || ephemPub || ct || mac`, as specified by\n * the ECIES provider interface. The Gateway hashes raw ciphertext bytes, not\n * plaintext.\n * The builder verifies the decrypted result's job ID, scope, and version\n * bindings. The PS worker verifies\n * `auth.bodyHash === sha256(canonicalJobRequestBytes(request))`; the Gateway\n * never sees the plaintext. Flow: personal-server-ts\n * `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Cryptography\n */\n\nimport { sha256 } from \"@noble/hashes/sha2\";\nimport { bytesToHex, fromHex, isAddress, isHex, toHex, type Hex } from \"viem\";\nimport {\n deserializeECIES,\n serializeECIES,\n type ECIESProvider,\n} from \"../ecies/interface\";\nimport {\n JOB_OPERATIONS,\n JOB_PROTOCOL_VERSION,\n type JobOperation,\n type JobRequest,\n type JobRequestEnvelope,\n type JobResult,\n} from \"../../protocol/jobs\";\nimport { fromBase64, toBase64 } from \"../../utils/encoding\";\n\nconst textDecoder = new TextDecoder();\nconst textEncoder = new TextEncoder();\n\n/** A job envelope or result did not match the jobs protocol. */\nexport class JobEnvelopeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"JobEnvelopeError\";\n }\n}\n\nfunction sortJsonKeys(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortJsonKeys);\n }\n if (value !== null && typeof value === \"object\") {\n return Object.fromEntries(\n Object.keys(value)\n .sort()\n .map((key) => [\n key,\n sortJsonKeys((value as Record<string, unknown>)[key]),\n ]),\n );\n }\n return value;\n}\n\nfunction canonicalJsonBytes(value: unknown): Uint8Array {\n return textEncoder.encode(JSON.stringify(sortJsonKeys(value)));\n}\n\n/**\n * Returns the canonical UTF-8 JSON bytes committed to by the auth body hash.\n *\n * @param request - Job request to validate and serialize canonically.\n * @returns Recursively key-sorted, whitespace-free UTF-8 JSON bytes.\n * @throws {JobEnvelopeError} If the request does not match the protocol schema.\n */\nexport function canonicalJobRequestBytes(request: JobRequest): Uint8Array {\n validateJobRequest(request);\n return canonicalJsonBytes(request);\n}\n\nfunction requestPlaintext(envelope: JobRequestEnvelope): Uint8Array {\n validateRequestEnvelope(envelope);\n return canonicalJsonBytes(envelope);\n}\n\nfunction resultPlaintext(result: JobResult): Uint8Array {\n return textEncoder.encode(\n JSON.stringify({\n v: result.v,\n jobId: result.jobId,\n scope: result.scope,\n version: result.version,\n contentType: result.contentType,\n body: result.body,\n }),\n );\n}\n\nfunction encryptedBytesToBase64(\n encrypted: Awaited<ReturnType<ECIESProvider[\"encrypt\"]>>,\n): string {\n return toBase64(fromHex(`0x${serializeECIES(encrypted)}`, \"bytes\"));\n}\n\nfunction base64ToEncrypted(ciphertext: string) {\n return deserializeECIES(toHex(fromBase64(ciphertext)));\n}\n\nfunction parseObject(\n plaintext: Uint8Array,\n kind: string,\n): Record<string, unknown> {\n try {\n const value: unknown = JSON.parse(textDecoder.decode(plaintext));\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new JobEnvelopeError(`Invalid ${kind}: expected an object`);\n }\n return value as Record<string, unknown>;\n } catch (error) {\n if (error instanceof JobEnvelopeError) throw error;\n throw new JobEnvelopeError(`Invalid ${kind}: malformed JSON`);\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction requirePlainObject(\n value: unknown,\n field: string,\n kind: string,\n): asserts value is Record<string, unknown> {\n if (!isPlainObject(value)) {\n throw new JobEnvelopeError(`Invalid ${kind}: invalid ${field}`);\n }\n}\n\nfunction requireExactKeys(\n value: Record<string, unknown>,\n expectedKeys: readonly string[],\n kind: string,\n): void {\n for (const key of expectedKeys) {\n if (!Object.hasOwn(value, key) || value[key] === undefined) {\n throw new JobEnvelopeError(`Invalid ${kind}: missing ${key}`);\n }\n }\n const expected = new Set<PropertyKey>(expectedKeys);\n for (const key of Reflect.ownKeys(value)) {\n if (!expected.has(key)) {\n throw new JobEnvelopeError(\n `Invalid ${kind}: unknown field ${String(key)}`,\n );\n }\n }\n}\n\nfunction requireString(\n value: unknown,\n field: string,\n kind: string,\n): asserts value is string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new JobEnvelopeError(`Invalid ${kind}: missing ${field}`);\n }\n}\n\nfunction validateJobRequest(value: unknown): JobRequest {\n requirePlainObject(value, \"request\", \"job request\");\n requireExactKeys(\n value,\n [\n \"v\",\n \"jobId\",\n \"owner\",\n \"builder\",\n \"builderPublicKey\",\n \"grantId\",\n \"scope\",\n \"operation\",\n \"pinnedVersion\",\n \"deadline\",\n ],\n \"job request\",\n );\n if (value.v !== JOB_PROTOCOL_VERSION) {\n throw new JobEnvelopeError(\n `Unsupported job request version: ${String(value.v)}`,\n );\n }\n requireString(value.jobId, \"jobId\", \"job request\");\n if (typeof value.owner !== \"string\" || !isAddress(value.owner)) {\n throw new JobEnvelopeError(\"Invalid job request: invalid owner\");\n }\n if (typeof value.builder !== \"string\" || !isAddress(value.builder)) {\n throw new JobEnvelopeError(\"Invalid job request: invalid builder\");\n }\n if (\n typeof value.builderPublicKey !== \"string\" ||\n !isHex(value.builderPublicKey)\n ) {\n throw new JobEnvelopeError(\"Invalid job request: invalid builderPublicKey\");\n }\n if (typeof value.grantId !== \"string\" || !isHex(value.grantId)) {\n throw new JobEnvelopeError(\"Invalid job request: invalid grantId\");\n }\n requireString(value.scope, \"scope\", \"job request\");\n if (!JOB_OPERATIONS.includes(value.operation as JobOperation)) {\n throw new JobEnvelopeError(\"Invalid job request: invalid operation\");\n }\n if (value.pinnedVersion !== null && typeof value.pinnedVersion !== \"string\") {\n throw new JobEnvelopeError(\"Invalid job request: missing pinnedVersion\");\n }\n if (\n typeof value.deadline !== \"string\" ||\n !Number.isFinite(Date.parse(value.deadline))\n ) {\n throw new JobEnvelopeError(\"Invalid job request: invalid deadline\");\n }\n return value as unknown as JobRequest;\n}\n\nfunction validateRequestEnvelope(value: unknown): JobRequestEnvelope {\n requirePlainObject(value, \"envelope\", \"job request envelope\");\n requireExactKeys(value, [\"request\", \"auth\"], \"job request envelope\");\n validateJobRequest(value.request);\n requireString(value.auth, \"auth\", \"job request envelope\");\n return value as unknown as JobRequestEnvelope;\n}\n\nfunction validateResult(value: unknown): JobResult {\n requirePlainObject(value, \"result\", \"job result\");\n requireExactKeys(\n value,\n [\"v\", \"jobId\", \"scope\", \"version\", \"contentType\", \"body\"],\n \"job result\",\n );\n if (value.v !== JOB_PROTOCOL_VERSION) {\n throw new JobEnvelopeError(\n `Unsupported job result version: ${String(value.v)}`,\n );\n }\n for (const field of [\"jobId\", \"scope\", \"contentType\"]) {\n requireString(value[field], field, \"job result\");\n }\n if (typeof value.body !== \"string\") {\n throw new JobEnvelopeError(\"Invalid job result: missing body\");\n }\n if (value.version !== null && typeof value.version !== \"string\") {\n throw new JobEnvelopeError(\"Invalid job result: missing version\");\n }\n return value as unknown as JobResult;\n}\n\n/**\n * Encrypts a validated job request envelope for a Personal Server enclave.\n *\n * The PS worker verifies\n * `auth.bodyHash === sha256(canonicalJobRequestBytes(request))`; the Gateway\n * never sees the plaintext.\n *\n * @param envelope - Request and builder Web3Signed authorization to encrypt.\n * @param enclavePublicKey - Public key returned by `GET /v1/identity?owner=`.\n * @param ecies - Injected ECIES implementation.\n * @returns Base64 ciphertext encoded as `iv || ephemPub || ct || mac`.\n * @throws {JobEnvelopeError} If the envelope or request is invalid.\n * @throws If ECIES encryption fails or the enclave public key is invalid.\n *\n * @example\n * ```ts\n * const identity = await fetch(`/v1/identity?owner=${owner}`).then((response) =>\n * response.json(),\n * );\n * const requestCiphertext = await sealJobRequest(\n * requestEnvelope,\n * identity.publicKey,\n * ecies,\n * );\n * await fetch(\"/v1/jobs\", {\n * method: \"POST\",\n * body: JSON.stringify({ ...submission, requestCiphertext }),\n * });\n * ```\n */\nexport async function sealJobRequest(\n envelope: JobRequestEnvelope,\n enclavePublicKey: Hex,\n ecies: ECIESProvider,\n): Promise<string> {\n const encrypted = await ecies.encrypt(\n fromHex(enclavePublicKey, \"bytes\"),\n requestPlaintext(envelope),\n );\n return encryptedBytesToBase64(encrypted);\n}\n\n/**\n * Decrypts and validates a job request envelope inside the enclave.\n *\n * @param ciphertext - Base64 `iv || ephemPub || ct || mac` ciphertext.\n * @param privateKey - Enclave key bytes, supplied as `Uint8Array` so the agent can zero them after use.\n * @param ecies - Injected ECIES implementation.\n * @returns The validated request envelope exactly as parsed from plaintext.\n * @throws {JobEnvelopeError} If plaintext is malformed or fails schema validation.\n * @throws If ciphertext decoding or ECIES decryption fails.\n */\nexport async function openJobRequest(\n ciphertext: string,\n privateKey: Uint8Array,\n ecies: ECIESProvider,\n): Promise<JobRequestEnvelope> {\n const plaintext = await ecies.decrypt(\n privateKey,\n base64ToEncrypted(ciphertext),\n );\n return validateRequestEnvelope(\n parseObject(plaintext, \"job request envelope\"),\n );\n}\n\n/**\n * Encrypts a validated job result for its builder and describes the ciphertext.\n *\n * `hash` is lowercase `0x` SHA-256 of decoded ciphertext bytes, not the\n * `sha256:` prefix used by Web3Signed `bodyHash`. `size` is that decoded byte\n * length. They equal the Gateway's `resultHash` and `resultSize`.\n *\n * @param result - Job result to validate and encrypt.\n * @param builderPublicKey - Builder wallet public key recorded in the request.\n * @param ecies - Injected ECIES implementation.\n * @returns Base64 ciphertext plus its Gateway-compatible hash and size.\n * @throws {JobEnvelopeError} If the result does not match the protocol schema.\n * @throws If ECIES encryption fails or the builder public key is invalid.\n */\nexport async function sealJobResult(\n result: JobResult,\n builderPublicKey: Hex,\n ecies: ECIESProvider,\n): Promise<{ ciphertext: string; hash: Hex; size: number }> {\n validateResult(result);\n const encrypted = await ecies.encrypt(\n fromHex(builderPublicKey, \"bytes\"),\n resultPlaintext(result),\n );\n const ciphertext = encryptedBytesToBase64(encrypted);\n const bytes = fromBase64(ciphertext);\n return { ciphertext, hash: bytesToHex(sha256(bytes)), size: bytes.length };\n}\n\n/**\n * Decrypts a job result and verifies its builder-visible protocol bindings.\n *\n * The builder private key is a `Hex` wallet key. For `expect.version`,\n * `undefined` skips the check while `null` requires a null result version.\n *\n * @param ciphertext - Base64 `iv || ephemPub || ct || mac` ciphertext.\n * @param builderPrivateKey - Builder's wallet private key as hex.\n * @param ecies - Injected ECIES implementation.\n * @param expect - Required job ID and optional scope and version bindings.\n * @returns The validated result when every supplied binding matches.\n * @throws {JobEnvelopeError} If plaintext is malformed, invalid, or a binding differs.\n * @throws If ciphertext decoding or ECIES decryption fails.\n *\n * @example\n * ```ts\n * const result = await openJobResult(\n * status.resultCiphertext,\n * key,\n * ecies,\n * { jobId, scope },\n * );\n * const body = fromBase64(result.body);\n * ```\n */\nexport async function openJobResult(\n ciphertext: string,\n builderPrivateKey: Hex,\n ecies: ECIESProvider,\n expect: { jobId: string; scope?: string; version?: string | null },\n): Promise<JobResult> {\n const plaintext = await ecies.decrypt(\n fromHex(builderPrivateKey, \"bytes\"),\n base64ToEncrypted(ciphertext),\n );\n const result = validateResult(parseObject(plaintext, \"job result\"));\n if (result.jobId !== expect.jobId) {\n throw new JobEnvelopeError(\n `Job result ID ${result.jobId} does not match expected job ID ${expect.jobId}`,\n );\n }\n if (expect.scope !== undefined && result.scope !== expect.scope) {\n throw new JobEnvelopeError(\n `Job result scope ${result.scope} does not match expected scope ${expect.scope}`,\n );\n }\n if (expect.version !== undefined && result.version !== expect.version) {\n throw new JobEnvelopeError(\n `Job result version ${String(result.version)} does not match expected version ${String(expect.version)}`,\n );\n }\n return result;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * ECIES envelopes for encrypted job requests and results.
3
+ *
4
+ * Request plaintext is UTF-8 JSON with object keys sorted recursively and
5
+ * array order preserved. Result properties follow their interface order. The
6
+ * wire ciphertext is base64 of `iv || ephemPub || ct || mac`, as specified by
7
+ * the ECIES provider interface. The Gateway hashes raw ciphertext bytes, not
8
+ * plaintext.
9
+ * The builder verifies the decrypted result's job ID, scope, and version
10
+ * bindings. The PS worker verifies
11
+ * `auth.bodyHash === sha256(canonicalJobRequestBytes(request))`; the Gateway
12
+ * never sees the plaintext. Flow: personal-server-ts
13
+ * `docs/260903-jobs-contract.md`, section 1.
14
+ *
15
+ * @category Cryptography
16
+ */
17
+ import { type Hex } from "viem";
18
+ import { type ECIESProvider } from "../ecies/interface.js";
19
+ import { type JobRequest, type JobRequestEnvelope, type JobResult } from "../../protocol/jobs.js";
20
+ /** A job envelope or result did not match the jobs protocol. */
21
+ export declare class JobEnvelopeError extends Error {
22
+ constructor(message: string);
23
+ }
24
+ /**
25
+ * Returns the canonical UTF-8 JSON bytes committed to by the auth body hash.
26
+ *
27
+ * @param request - Job request to validate and serialize canonically.
28
+ * @returns Recursively key-sorted, whitespace-free UTF-8 JSON bytes.
29
+ * @throws {JobEnvelopeError} If the request does not match the protocol schema.
30
+ */
31
+ export declare function canonicalJobRequestBytes(request: JobRequest): Uint8Array;
32
+ /**
33
+ * Encrypts a validated job request envelope for a Personal Server enclave.
34
+ *
35
+ * The PS worker verifies
36
+ * `auth.bodyHash === sha256(canonicalJobRequestBytes(request))`; the Gateway
37
+ * never sees the plaintext.
38
+ *
39
+ * @param envelope - Request and builder Web3Signed authorization to encrypt.
40
+ * @param enclavePublicKey - Public key returned by `GET /v1/identity?owner=`.
41
+ * @param ecies - Injected ECIES implementation.
42
+ * @returns Base64 ciphertext encoded as `iv || ephemPub || ct || mac`.
43
+ * @throws {JobEnvelopeError} If the envelope or request is invalid.
44
+ * @throws If ECIES encryption fails or the enclave public key is invalid.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const identity = await fetch(`/v1/identity?owner=${owner}`).then((response) =>
49
+ * response.json(),
50
+ * );
51
+ * const requestCiphertext = await sealJobRequest(
52
+ * requestEnvelope,
53
+ * identity.publicKey,
54
+ * ecies,
55
+ * );
56
+ * await fetch("/v1/jobs", {
57
+ * method: "POST",
58
+ * body: JSON.stringify({ ...submission, requestCiphertext }),
59
+ * });
60
+ * ```
61
+ */
62
+ export declare function sealJobRequest(envelope: JobRequestEnvelope, enclavePublicKey: Hex, ecies: ECIESProvider): Promise<string>;
63
+ /**
64
+ * Decrypts and validates a job request envelope inside the enclave.
65
+ *
66
+ * @param ciphertext - Base64 `iv || ephemPub || ct || mac` ciphertext.
67
+ * @param privateKey - Enclave key bytes, supplied as `Uint8Array` so the agent can zero them after use.
68
+ * @param ecies - Injected ECIES implementation.
69
+ * @returns The validated request envelope exactly as parsed from plaintext.
70
+ * @throws {JobEnvelopeError} If plaintext is malformed or fails schema validation.
71
+ * @throws If ciphertext decoding or ECIES decryption fails.
72
+ */
73
+ export declare function openJobRequest(ciphertext: string, privateKey: Uint8Array, ecies: ECIESProvider): Promise<JobRequestEnvelope>;
74
+ /**
75
+ * Encrypts a validated job result for its builder and describes the ciphertext.
76
+ *
77
+ * `hash` is lowercase `0x` SHA-256 of decoded ciphertext bytes, not the
78
+ * `sha256:` prefix used by Web3Signed `bodyHash`. `size` is that decoded byte
79
+ * length. They equal the Gateway's `resultHash` and `resultSize`.
80
+ *
81
+ * @param result - Job result to validate and encrypt.
82
+ * @param builderPublicKey - Builder wallet public key recorded in the request.
83
+ * @param ecies - Injected ECIES implementation.
84
+ * @returns Base64 ciphertext plus its Gateway-compatible hash and size.
85
+ * @throws {JobEnvelopeError} If the result does not match the protocol schema.
86
+ * @throws If ECIES encryption fails or the builder public key is invalid.
87
+ */
88
+ export declare function sealJobResult(result: JobResult, builderPublicKey: Hex, ecies: ECIESProvider): Promise<{
89
+ ciphertext: string;
90
+ hash: Hex;
91
+ size: number;
92
+ }>;
93
+ /**
94
+ * Decrypts a job result and verifies its builder-visible protocol bindings.
95
+ *
96
+ * The builder private key is a `Hex` wallet key. For `expect.version`,
97
+ * `undefined` skips the check while `null` requires a null result version.
98
+ *
99
+ * @param ciphertext - Base64 `iv || ephemPub || ct || mac` ciphertext.
100
+ * @param builderPrivateKey - Builder's wallet private key as hex.
101
+ * @param ecies - Injected ECIES implementation.
102
+ * @param expect - Required job ID and optional scope and version bindings.
103
+ * @returns The validated result when every supplied binding matches.
104
+ * @throws {JobEnvelopeError} If plaintext is malformed, invalid, or a binding differs.
105
+ * @throws If ciphertext decoding or ECIES decryption fails.
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * const result = await openJobResult(
110
+ * status.resultCiphertext,
111
+ * key,
112
+ * ecies,
113
+ * { jobId, scope },
114
+ * );
115
+ * const body = fromBase64(result.body);
116
+ * ```
117
+ */
118
+ export declare function openJobResult(ciphertext: string, builderPrivateKey: Hex, ecies: ECIESProvider, expect: {
119
+ jobId: string;
120
+ scope?: string;
121
+ version?: string | null;
122
+ }): Promise<JobResult>;