@opendatalabs/vana-sdk 4.0.0 → 4.2.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/protocol/jobs-client.ts"],"sourcesContent":["/**\n * Builder-side client for encrypted Gateway jobs.\n *\n * @remarks\n * This Node-only client fetches an owner's sealed enclave identity, signs and\n * seals raw-read work, submits it to the Gateway, polls builder-visible state,\n * and decrypts the bound result with the builder private key.\n *\n * @category Protocol\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { sha256 } from \"@noble/hashes/sha2\";\nimport {\n bytesToHex,\n isAddress,\n isAddressEqual,\n isHex,\n type Address,\n type Hex,\n type LocalAccount,\n} from \"viem\";\nimport { privateKeyToAccount, publicKeyToAddress } from \"viem/accounts\";\nimport {\n buildWeb3SignedHeader,\n computeBodyHash,\n} from \"../auth/web3-signed-builder\";\nimport type { ECIESProvider } from \"../crypto/ecies/interface\";\nimport { NodeECIESUint8Provider } from \"../crypto/ecies/node\";\nimport {\n canonicalJobRequestBytes,\n openJobResult,\n sealJobRequest,\n} from \"../crypto/envelope/job\";\nimport {\n BuilderUnknownError,\n GrantInvalidError,\n JobIdTakenError,\n JobNotFoundError,\n JobRejectedError,\n JobResultIntegrityError,\n JobRequestTooLargeError,\n JobTimeoutError,\n JobTransportError,\n OwnerNotReadyError,\n type JobGatewayErrorCode,\n type JobsClientError,\n} from \"../errors\";\nimport { userPsId, type IdentityResponse } from \"./identity\";\nimport {\n CLAIM_POLL_FLOOR_MS,\n DEFAULT_JOB_DEADLINE_SECONDS,\n JOB_OPERATIONS,\n JOB_PROTOCOL_VERSION,\n JOB_STATES,\n MAX_JOB_DEADLINE_SECONDS,\n MAX_WAIT_SECONDS,\n type JobRequest,\n type JobResult,\n type JobOperation,\n type JobState,\n type JobStatus,\n type JobSubmission,\n type ResultHandle,\n} from \"./jobs\";\nimport { resolveWriteSigner, type WriteSignerSource } from \"./write-signer\";\n\nconst IDENTITY_PATH = \"/v1/identity\";\nconst JOBS_PATH = \"/v1/jobs\";\nconst JOB_EXECUTE_PATH = \"/v1/jobs/execute\";\nconst AUTHORIZATION_HEADER = \"Authorization\";\nconst CONTENT_TYPE_HEADER = \"Content-Type\";\nconst JSON_CONTENT_TYPE = \"application/json\";\nconst GET_METHOD = \"GET\";\nconst POST_METHOD = \"POST\";\nconst RAW_READ_OPERATION = \"raw_read\";\nconst SEALED_IDENTITY_STATE = \"sealed\";\nconst HTTP_OK = 200;\nconst HTTP_ACCEPTED = 202;\nconst HTTP_UNAUTHORIZED = 401;\nconst HTTP_FORBIDDEN = 403;\nconst HTTP_NOT_FOUND = 404;\nconst HTTP_CONFLICT = 409;\nconst HTTP_PAYLOAD_TOO_LARGE = 413;\nconst MILLISECONDS_PER_SECOND = 1_000;\nconst DEFAULT_JOB_TIMEOUT_MS = 120_000;\nconst DEFAULT_JOB_POLL_MS = CLAIM_POLL_FLOOR_MS;\nconst TERMINAL_JOB_STATES: ReadonlySet<JobState> = new Set([\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n]);\n\n/** A viem local account that exposes the public key required by job requests. */\nexport type JobsBuilderAccount = Extract<LocalAccount, WriteSignerSource>;\n\n/** Configuration captured by {@link createJobsClient}. */\nexport interface JobsClientOptions {\n /** Gateway base URL; its origin is the Web3Signed audience. */\n gatewayUrl: string;\n /** Vana chain ID used by the owner identity lookup. */\n chainId: number;\n /** Raw builder private key, used for signing and result decryption. */\n builderPrivateKey?: Hex;\n /**\n * viem local account used for signing. Its public key supports submission,\n * but without `builderPrivateKey` this client cannot decrypt results:\n * `openResult` and `readRaw` reject while submit/status/wait remain usable.\n */\n builderAccount?: JobsBuilderAccount;\n /** HTTP implementation; defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /** ECIES implementation; defaults to the Node provider. */\n ecies?: ECIESProvider;\n /** Clock used when constructing a job deadline. */\n now?: () => Date;\n}\n\n/** Parameters for one encrypted raw-read submission. */\nexport interface SubmitRawReadParams {\n /** Owner whose enclave executes the read. */\n owner: Address;\n /** Owner-issued builder grant. */\n grantId: Hex;\n /** Data scope to read. */\n scope: string;\n /** Exact record version to require, or `null` for an unpinned read. */\n pinnedVersion?: string | null;\n /** Requested deadline offset, clamped to the protocol maximum. */\n deadlineSeconds?: number;\n /** Gateway inline-wait seconds, clamped to `0..MAX_WAIT_SECONDS`. */\n wait?: number;\n}\n\n/** Result of submitting an encrypted raw-read job. */\nexport interface SubmitRawReadResult {\n /** Client-generated job UUID. */\n jobId: string;\n /** Current Gateway state. */\n state: JobState;\n /** Full status when the Gateway returned an inline 200 response. */\n job?: JobStatus;\n}\n\n/** Polling controls for {@link JobsClient.waitForJob}. */\nexport interface WaitForJobOptions {\n /** Total polling budget in milliseconds. */\n timeoutMs?: number;\n /** Delay between reads; values below `CLAIM_POLL_FLOOR_MS` are raised. */\n pollMs?: number;\n}\n\n/** Expected plaintext bindings for {@link JobsClient.openResult}. */\nexport interface OpenJobResultOptions {\n /** Bind the plaintext to this job id. */\n expect: {\n jobId: string;\n scope?: string;\n version?: string | null;\n };\n}\n\n/** Parameters for the submit, wait, and decrypt convenience flow. */\nexport interface ReadRawParams extends SubmitRawReadParams, WaitForJobOptions {}\n\n/** Builder operations exposed by {@link createJobsClient}. */\nexport interface JobsClient {\n /**\n * Encrypt and submit a raw-read job.\n *\n * @param params - Owner, grant, scope, deadline, and inline-wait controls.\n * @returns The job id and current state, plus inline status on HTTP 200.\n * @throws {OwnerNotReadyError} When the owner identity is not sealed.\n * @throws {JobsClientError} When the Gateway or transport rejects the call.\n */\n submitRawRead(params: SubmitRawReadParams): Promise<SubmitRawReadResult>;\n\n /**\n * Read one builder-visible job status.\n *\n * @param jobId - Client-generated job UUID.\n * @returns The current job status.\n * @throws {JobNotFoundError} When the job is absent or belongs to another builder.\n * @throws {JobsClientError} For other Gateway or transport failures.\n */\n getJob(jobId: string): Promise<JobStatus>;\n\n /**\n * Poll until a job completes, fails, expires, or is cancelled.\n *\n * @param jobId - Job UUID to poll.\n * @param options - Timeout and poll cadence.\n * @returns The terminal job status.\n * @throws {JobTimeoutError} When the caller or job deadline is exhausted.\n */\n waitForJob(jobId: string, options?: WaitForJobOptions): Promise<JobStatus>;\n\n /**\n * Fetch, decrypt, and validate a job result from object storage.\n *\n * @param handle - Object-storage location and integrity metadata.\n * @param options - Expected plaintext job, scope, and version bindings.\n * @returns The validated plaintext job result.\n * @remarks Decryption requires `builderPrivateKey`; `builderAccount` alone\n * only supports submission, status reads, and waiting.\n * @throws {JobRejectedError} When a raw private key is unavailable.\n * @throws {JobTransportError} When fetching the result object fails.\n * @throws {JobResultIntegrityError} When the fetched bytes mismatch the handle.\n * @throws {JobEnvelopeError} When decrypted protocol fields or bindings differ.\n */\n openResult(\n handle: ResultHandle,\n options: OpenJobResultOptions,\n ): Promise<JobResult>;\n\n /**\n * Submit, wait for, and decrypt one raw read.\n *\n * @param params - Raw-read request plus polling controls.\n * @returns The decrypted, binding-checked job result.\n * @remarks This convenience flow decrypts the result and therefore requires\n * `builderPrivateKey`; a client configured only with `builderAccount`\n * cannot call `readRaw`.\n * @throws {JobsClientError} When submission, polling, or decryption setup fails.\n * @throws {JobEnvelopeError} When decrypted result bindings differ.\n *\n * @example\n * ```typescript\n * const result = await client.readRaw({\n * owner,\n * grantId,\n * scope: \"profile.email\",\n * wait: 25,\n * });\n * ```\n */\n readRaw(params: ReadRawParams): Promise<JobResult>;\n}\n\ninterface ResolvedBuilder {\n address: Address;\n publicKey: Hex;\n signMessage: ReturnType<typeof resolveWriteSigner>[\"signMessage\"];\n}\n\ninterface GatewayErrorBody {\n code: JobGatewayErrorCode | null;\n message: string;\n reason?: string;\n details?: Record<string, unknown>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction normalizeGateway(gatewayUrl: string): {\n baseUrl: string;\n audience: string;\n} {\n try {\n const url = new URL(gatewayUrl);\n if (url.pathname !== \"/\" || url.search !== \"\" || url.hash !== \"\") {\n throw new JobRejectedError(\n \"gatewayUrl must be an origin without a path, query, or fragment\",\n );\n }\n return {\n baseUrl: url.origin,\n audience: url.origin,\n };\n } catch (error) {\n if (error instanceof JobRejectedError) throw error;\n throw new JobRejectedError(\n \"gatewayUrl must be an absolute URL\",\n undefined,\n null,\n {\n cause: error instanceof Error ? error.message : String(error),\n },\n );\n }\n}\n\nfunction resolveFetch(fetchFn?: typeof fetch): typeof fetch {\n const resolved = fetchFn ?? globalThis.fetch;\n if (resolved === undefined) {\n throw new JobRejectedError(\"No fetch implementation available\");\n }\n return resolved;\n}\n\nfunction sourcePublicKey(source: WriteSignerSource): Hex | undefined {\n if (isRecord(source) && typeof source[\"publicKey\"] === \"string\") {\n return source[\"publicKey\"] as Hex;\n }\n return undefined;\n}\n\nfunction resolveBuilder(options: JobsClientOptions): {\n builder: ResolvedBuilder;\n privateKey?: Hex;\n} {\n if (options.builderPrivateKey && options.builderAccount) {\n throw new JobRejectedError(\n \"Provide builderPrivateKey or builderAccount, not both\",\n );\n }\n let source: WriteSignerSource | undefined;\n try {\n source = options.builderPrivateKey\n ? privateKeyToAccount(options.builderPrivateKey)\n : options.builderAccount;\n } catch (error) {\n throw new JobRejectedError(\"Invalid builder private key\", undefined, null, {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n if (!source) {\n throw new JobRejectedError(\n \"builderPrivateKey or builderAccount is required\",\n );\n }\n let signer: ReturnType<typeof resolveWriteSigner>;\n try {\n signer = resolveWriteSigner(source);\n } catch (error) {\n throw new JobRejectedError(\"Invalid builder signer\", undefined, null, {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n const publicKey = sourcePublicKey(source);\n if (!signer.address || !publicKey) {\n throw new JobRejectedError(\n \"Builder signer must expose both address and publicKey\",\n );\n }\n return {\n builder: {\n address: signer.address,\n publicKey,\n signMessage: signer.signMessage,\n },\n ...(options.builderPrivateKey\n ? { privateKey: options.builderPrivateKey }\n : {}),\n };\n}\n\nfunction clampInteger(\n value: number | undefined,\n fallback: number,\n minimum: number,\n maximum: number,\n field: string,\n): number {\n const resolved = value ?? fallback;\n if (!Number.isFinite(resolved)) {\n throw new JobRejectedError(`${field} must be a finite number`);\n }\n return Math.min(maximum, Math.max(minimum, Math.trunc(resolved)));\n}\n\nasync function responseBody(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n}\n\nfunction gatewayErrorBody(body: unknown, fallback: string): GatewayErrorBody {\n if (!isRecord(body)) return { code: null, message: fallback };\n const code = typeof body[\"code\"] === \"string\" ? body[\"code\"] : null;\n const message =\n typeof body[\"error\"] === \"string\"\n ? body[\"error\"]\n : typeof body[\"message\"] === \"string\"\n ? body[\"message\"]\n : fallback;\n const reason =\n typeof body[\"reason\"] === \"string\" ? body[\"reason\"] : undefined;\n const details = isRecord(body[\"details\"]) ? body[\"details\"] : undefined;\n return {\n code,\n message,\n ...(reason ? { reason } : {}),\n ...(details ? { details } : {}),\n };\n}\n\nasync function rejectedResponse(\n response: Response,\n fallback: string,\n): Promise<JobsClientError> {\n const parsed = gatewayErrorBody(await responseBody(response), fallback);\n const details =\n response.status === HTTP_UNAUTHORIZED && parsed.reason\n ? { ...parsed.details, reason: parsed.reason }\n : parsed.details;\n const message =\n response.status === HTTP_UNAUTHORIZED && parsed.reason\n ? `${parsed.message}: ${parsed.reason}`\n : parsed.message;\n if (response.status === HTTP_FORBIDDEN) {\n if (parsed.code === \"BUILDER_UNKNOWN\") {\n return new BuilderUnknownError(parsed.message, details);\n }\n if (parsed.code === \"GRANT_INVALID\") {\n return new GrantInvalidError(parsed.message, details);\n }\n if (parsed.code === \"OWNER_NOT_READY\") {\n return new OwnerNotReadyError(parsed.message, details);\n }\n }\n if (response.status === HTTP_NOT_FOUND) {\n return new JobNotFoundError(parsed.message, details);\n }\n if (response.status === HTTP_CONFLICT && parsed.code === \"JOB_ID_TAKEN\") {\n return new JobIdTakenError(parsed.message, details);\n }\n if (response.status === HTTP_PAYLOAD_TOO_LARGE) {\n return new JobRequestTooLargeError(parsed.message, parsed.code, details);\n }\n return new JobRejectedError(message, response.status, parsed.code, details);\n}\n\nfunction requireStatus(value: unknown, status: number): JobStatus {\n if (\n !isRecord(value) ||\n !isNonEmptyString(value[\"jobId\"]) ||\n typeof value[\"state\"] !== \"string\" ||\n !JOB_STATES.includes(value[\"state\"] as JobState) ||\n !isNonEmptyString(value[\"owner\"]) ||\n !isNonEmptyString(value[\"grantId\"]) ||\n !isNonEmptyString(value[\"scope\"]) ||\n !isNonEmptyString(value[\"operation\"]) ||\n !JOB_OPERATIONS.includes(value[\"operation\"] as JobOperation) ||\n (value[\"state\"] === \"completed\" && !isResultHandle(value[\"result\"]))\n ) {\n throw new JobRejectedError(\n \"Gateway response did not contain a job status\",\n status,\n );\n }\n return value as unknown as JobStatus;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction isResultHandle(value: unknown): value is ResultHandle {\n if (\n !isRecord(value) ||\n !isNonEmptyString(value[\"objectKey\"]) ||\n !isNonEmptyString(value[\"url\"]) ||\n typeof value[\"size\"] !== \"number\" ||\n !Number.isInteger(value[\"size\"]) ||\n value[\"size\"] < 0 ||\n typeof value[\"hash\"] !== \"string\" ||\n !/^0x[0-9a-fA-F]{64}$/.test(value[\"hash\"]) ||\n typeof value[\"expiresAt\"] !== \"string\" ||\n Number.isNaN(Date.parse(value[\"expiresAt\"]))\n ) {\n return false;\n }\n try {\n const url = new URL(value[\"url\"]);\n return url.protocol === \"https:\" || url.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n\nfunction hasMatchingIdentityKey(value: Record<string, unknown>): boolean {\n const address = value[\"address\"];\n const publicKey = value[\"publicKey\"];\n if (\n typeof address !== \"string\" ||\n !isAddress(address) ||\n typeof publicKey !== \"string\" ||\n !isHex(publicKey)\n ) {\n return false;\n }\n try {\n return (\n publicKeyToAddress(publicKey).toLowerCase() === address.toLowerCase()\n );\n } catch {\n return false;\n }\n}\n\nfunction hasExpectedIdentity(\n value: Record<string, unknown>,\n owner: Address,\n chainId: number,\n): boolean {\n const identityOwner = value[\"ownerAddress\"];\n const identityChainId = value[\"chainId\"];\n const identityUserPsId = value[\"userPsId\"];\n if (\n !hasMatchingIdentityKey(value) ||\n typeof identityOwner !== \"string\" ||\n !isAddress(identityOwner) ||\n typeof identityChainId !== \"number\" ||\n typeof identityUserPsId !== \"string\" ||\n !isHex(identityUserPsId)\n ) {\n return false;\n }\n return (\n isAddressEqual(identityOwner, owner) &&\n identityChainId === chainId &&\n identityUserPsId.toLowerCase() === userPsId(chainId, owner).toLowerCase()\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction requireFiniteDuration(value: number, field: string): number {\n if (!Number.isFinite(value)) {\n throw new JobRejectedError(`${field} must be a finite number`);\n }\n return Math.max(0, value);\n}\n\n/**\n * Create a reusable Node builder client for encrypted Gateway jobs.\n *\n * @param options - Gateway, chain, builder signer, and injectable adapters.\n * @returns A client bound to the configured builder and Gateway.\n * @throws {JobRejectedError} When configuration or signer shape is invalid.\n *\n * @remarks\n * This client is Node-only. `builderPrivateKey` supports the complete flow;\n * `builderAccount` alone supports submit/status/wait but cannot decrypt, so\n * `openResult` and `readRaw` require the raw private key.\n *\n * @example\n * ```typescript\n * const client = createJobsClient({\n * gatewayUrl: \"https://gateway.example.com\",\n * chainId: 14800,\n * builderPrivateKey: process.env.BUILDER_PRIVATE_KEY as Hex,\n * });\n * const result = await client.readRaw({ owner, grantId, scope: \"profile\" });\n * ```\n */\nexport function createJobsClient(options: JobsClientOptions): JobsClient {\n const { baseUrl, audience } = normalizeGateway(options.gatewayUrl);\n const fetchFn = resolveFetch(options.fetch);\n const { builder, privateKey } = resolveBuilder(options);\n const ecies = options.ecies ?? new NodeECIESUint8Provider();\n const now = options.now ?? (() => new Date());\n\n async function request(\n url: string,\n init: RequestInit,\n description: string,\n ): Promise<Response> {\n try {\n return await fetchFn(url, init);\n } catch (error) {\n throw new JobTransportError(\n `${description} failed: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n async function getIdentity(\n owner: Address,\n ): Promise<IdentityResponse[\"identity\"]> {\n const query = new URLSearchParams({\n owner,\n chainId: String(options.chainId),\n });\n const response = await request(\n `${baseUrl}${IDENTITY_PATH}?${query.toString()}`,\n { method: GET_METHOD },\n \"Owner identity lookup\",\n );\n if (response.status === HTTP_NOT_FOUND) {\n const parsed = gatewayErrorBody(\n await responseBody(response),\n \"Owner identity not found\",\n );\n throw new OwnerNotReadyError(parsed.message, parsed.details);\n }\n if (!response.ok) {\n throw await rejectedResponse(response, \"Owner identity lookup rejected\");\n }\n const body = await responseBody(response);\n if (\n !isRecord(body) ||\n body[\"state\"] !== SEALED_IDENTITY_STATE ||\n !isRecord(body[\"identity\"])\n ) {\n throw new OwnerNotReadyError(\"Owner identity is not sealed\", {\n state: isRecord(body) ? body[\"state\"] : undefined,\n });\n }\n if (!hasExpectedIdentity(body[\"identity\"], owner, options.chainId)) {\n throw new JobRejectedError(\n \"Gateway returned a malformed or mismatched owner identity\",\n response.status,\n );\n }\n // Full TDX/KMS evidence verification awaits provisioned trust anchors and is intentionally outside this driver-matching client.\n return body[\"identity\"] as unknown as IdentityResponse[\"identity\"];\n }\n\n const client: JobsClient = {\n async submitRawRead(params) {\n const identity = await getIdentity(params.owner);\n const deadlineSeconds = clampInteger(\n params.deadlineSeconds,\n DEFAULT_JOB_DEADLINE_SECONDS,\n 1,\n MAX_JOB_DEADLINE_SECONDS,\n \"deadlineSeconds\",\n );\n const wait = clampInteger(params.wait, 0, 0, MAX_WAIT_SECONDS, \"wait\");\n const jobId = randomUUID();\n const nowMs = now().getTime();\n if (!Number.isFinite(nowMs)) {\n throw new JobRejectedError(\"now must return a valid Date\");\n }\n const deadline = new Date(\n nowMs + deadlineSeconds * MILLISECONDS_PER_SECOND,\n ).toISOString();\n const requestBody: JobRequest = {\n v: JOB_PROTOCOL_VERSION,\n jobId,\n owner: params.owner,\n builder: builder.address,\n builderPublicKey: builder.publicKey,\n grantId: params.grantId,\n scope: params.scope,\n operation: RAW_READ_OPERATION,\n pinnedVersion: params.pinnedVersion ?? null,\n deadline,\n };\n const auth = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: POST_METHOD,\n uri: JOB_EXECUTE_PATH,\n bodyHash: computeBodyHash(canonicalJobRequestBytes(requestBody)),\n nonce: randomUUID(),\n });\n const requestCiphertext = await sealJobRequest(\n { request: requestBody, auth },\n identity.publicKey,\n ecies,\n );\n const submission: JobSubmission = {\n owner: requestBody.owner,\n grantId: requestBody.grantId,\n scope: requestBody.scope,\n operation: requestBody.operation,\n idempotencyKey: randomUUID(),\n jobId,\n deadline,\n requestCiphertext,\n };\n const bodyBytes = new TextEncoder().encode(JSON.stringify(submission));\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: POST_METHOD,\n uri: JOBS_PATH,\n body: bodyBytes,\n nonce: randomUUID(),\n });\n const response = await request(\n `${baseUrl}${JOBS_PATH}?wait=${wait}`,\n {\n method: POST_METHOD,\n headers: {\n [AUTHORIZATION_HEADER]: authorization,\n [CONTENT_TYPE_HEADER]: JSON_CONTENT_TYPE,\n },\n body: bodyBytes,\n },\n \"Job submission\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job submission rejected\");\n }\n const body = await responseBody(response);\n if (response.status === HTTP_OK && isRecord(body)) {\n const job = requireStatus(body[\"job\"], response.status);\n if (job.jobId !== jobId) {\n throw new JobRejectedError(\n \"Gateway returned a job id that does not match the submission\",\n response.status,\n );\n }\n return { jobId, state: job.state, job };\n }\n if (\n response.status === HTTP_ACCEPTED &&\n isRecord(body) &&\n body[\"jobId\"] === jobId &&\n typeof body[\"state\"] === \"string\" &&\n JOB_STATES.includes(body[\"state\"] as JobState)\n ) {\n return { jobId, state: body[\"state\"] as JobState };\n }\n throw new JobRejectedError(\n \"Gateway returned an undocumented job submission response\",\n response.status,\n );\n },\n\n async getJob(jobId) {\n const path = `${JOBS_PATH}/${encodeURIComponent(jobId)}`;\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: GET_METHOD,\n uri: path,\n nonce: randomUUID(),\n });\n const response = await request(\n `${baseUrl}${path}`,\n {\n method: GET_METHOD,\n headers: { [AUTHORIZATION_HEADER]: authorization },\n },\n \"Job status read\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job status read rejected\");\n }\n const body = await responseBody(response);\n return requireStatus(\n isRecord(body) ? body[\"job\"] : undefined,\n response.status,\n );\n },\n\n async waitForJob(jobId, waitOptions = {}) {\n const timeoutMs = requireFiniteDuration(\n waitOptions.timeoutMs ?? DEFAULT_JOB_TIMEOUT_MS,\n \"timeoutMs\",\n );\n const pollMs = Math.max(\n CLAIM_POLL_FLOOR_MS,\n requireFiniteDuration(\n waitOptions.pollMs ?? DEFAULT_JOB_POLL_MS,\n \"pollMs\",\n ),\n );\n const startedAt = Date.now();\n const callerDeadline = startedAt + timeoutMs;\n let latest: JobStatus | undefined;\n for (;;) {\n if (Date.now() >= callerDeadline) {\n throw new JobTimeoutError(\n `Timed out waiting for terminal job ${jobId}`,\n {\n jobId,\n timeoutMs,\n state: latest?.state,\n },\n );\n }\n latest = await client.getJob(jobId);\n if (TERMINAL_JOB_STATES.has(latest.state)) return latest;\n const remaining = callerDeadline - Date.now();\n if (remaining <= 0) {\n throw new JobTimeoutError(\n `Timed out waiting for terminal job ${jobId}`,\n {\n jobId,\n timeoutMs,\n state: latest.state,\n },\n );\n }\n await sleep(Math.min(pollMs, remaining));\n }\n },\n\n async openResult(handle, openOptions) {\n if (!privateKey) {\n throw new JobRejectedError(\n \"openResult requires createJobsClient({ builderPrivateKey })\",\n );\n }\n const response = await request(\n handle.url,\n { method: GET_METHOD },\n \"Job result fetch\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job result fetch rejected\");\n }\n let bytes: Uint8Array;\n try {\n bytes = new Uint8Array(await response.arrayBuffer());\n } catch (error) {\n throw new JobTransportError(\n `Job result fetch failed: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n const actualHash = bytesToHex(sha256(bytes));\n if (actualHash.toLowerCase() !== handle.hash.toLowerCase()) {\n throw new JobResultIntegrityError(\n \"Fetched job result hash does not match its handle\",\n {\n objectKey: handle.objectKey,\n expectedHash: handle.hash,\n actualHash,\n },\n );\n }\n if (bytes.length !== handle.size) {\n throw new JobResultIntegrityError(\n \"Fetched job result size does not match its handle\",\n {\n objectKey: handle.objectKey,\n expectedSize: handle.size,\n actualSize: bytes.length,\n },\n );\n }\n return openJobResult(bytes, privateKey, ecies, openOptions.expect);\n },\n\n async readRaw(params) {\n const submitted = await client.submitRawRead(params);\n const job =\n submitted.job && TERMINAL_JOB_STATES.has(submitted.job.state)\n ? submitted.job\n : await client.waitForJob(submitted.jobId, {\n timeoutMs: params.timeoutMs,\n pollMs: params.pollMs,\n });\n if (job.state !== \"completed\") {\n throw new JobRejectedError(\n `Job ${job.jobId} ended in state ${job.state}${job.failureReason ? `: ${job.failureReason}` : \"\"}`,\n undefined,\n null,\n {\n jobId: job.jobId,\n state: job.state,\n ...(job.failureReason ? { failureReason: job.failureReason } : {}),\n },\n );\n }\n if (!job.result) {\n throw new JobRejectedError(\n \"Completed job status does not include a result handle\",\n undefined,\n null,\n { jobId: job.jobId, state: job.state },\n );\n }\n return client.openResult(job.result, {\n expect: {\n jobId: submitted.jobId,\n scope: params.scope,\n ...(params.pinnedVersion !== undefined &&\n params.pinnedVersion !== null\n ? { version: params.pinnedVersion }\n : {}),\n },\n });\n },\n };\n\n return client;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/protocol/jobs-client.ts"],"sourcesContent":["/**\n * Builder-side client for encrypted Gateway jobs.\n *\n * @remarks\n * This Node-only client fetches an owner's sealed enclave identity, signs and\n * seals raw-read work, submits it to the Gateway, polls builder-visible state,\n * and decrypts the bound result with the builder private key.\n *\n * @category Protocol\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { sha256 } from \"@noble/hashes/sha2\";\nimport {\n bytesToHex,\n isAddress,\n isAddressEqual,\n isHex,\n keccak256,\n toHex,\n type Address,\n type Hex,\n type LocalAccount,\n} from \"viem\";\nimport { privateKeyToAccount, publicKeyToAddress } from \"viem/accounts\";\nimport {\n buildWeb3SignedHeader,\n computeBodyHash,\n} from \"../auth/web3-signed-builder\";\nimport type { ECIESProvider } from \"../crypto/ecies/interface\";\nimport { NodeECIESUint8Provider } from \"../crypto/ecies/node\";\nimport {\n canonicalJobRequestBytes,\n openJobResult,\n sealJobRequest,\n} from \"../crypto/envelope/job\";\nimport {\n BuilderUnknownError,\n GrantInvalidError,\n JobIdTakenError,\n JobNotFoundError,\n JobRejectedError,\n JobResultIntegrityError,\n JobRequestTooLargeError,\n JobTimeoutError,\n JobTransportError,\n OwnerNotReadyError,\n type JobGatewayErrorCode,\n type JobsClientError,\n} from \"../errors\";\nimport { PaymentRequiredError } from \"../direct/errors\";\nimport { CONTRACTS } from \"../config/contracts.config\";\nimport { GENERIC_PAYMENT_TYPES, genericPaymentDomain } from \"./escrow\";\nimport { userPsId, type IdentityResponse } from \"./identity\";\nimport {\n CLAIM_POLL_FLOOR_MS,\n DEFAULT_JOB_DEADLINE_SECONDS,\n JOB_OPERATIONS,\n JOB_PROTOCOL_VERSION,\n JOB_STATES,\n MAX_JOB_DEADLINE_SECONDS,\n MAX_WAIT_SECONDS,\n type JobRequest,\n type JobResult,\n type JobOperation,\n type JobState,\n type JobPaymentAuthorization,\n type JobQuote,\n type JobStatus,\n type JobSubmission,\n type ResultHandle,\n} from \"./jobs\";\nimport { resolveWriteSigner, type WriteSignerSource } from \"./write-signer\";\n\nconst IDENTITY_PATH = \"/v1/identity\";\nconst JOBS_PATH = \"/v1/jobs\";\nconst JOB_QUOTE_PATH = \"/v1/jobs/quote\";\nconst JOB_EXECUTE_PATH = \"/v1/jobs/execute\";\n/**\n * `payments.op_type` for a job read. Distinct from the legacy standalone\n * `data_access` payment, whose opId is the receipt's recordId: a job is priced\n * before any receipt exists, so its opId is the job.\n */\nconst JOB_ACCESS_OP_TYPE = \"job_access\";\nconst AUTHORIZATION_HEADER = \"Authorization\";\nconst CONTENT_TYPE_HEADER = \"Content-Type\";\nconst JSON_CONTENT_TYPE = \"application/json\";\nconst GET_METHOD = \"GET\";\nconst POST_METHOD = \"POST\";\nconst RAW_READ_OPERATION = \"raw_read\";\nconst SEALED_IDENTITY_STATE = \"sealed\";\nconst HTTP_OK = 200;\nconst HTTP_ACCEPTED = 202;\nconst HTTP_UNAUTHORIZED = 401;\nconst HTTP_FORBIDDEN = 403;\nconst HTTP_NOT_FOUND = 404;\nconst HTTP_CONFLICT = 409;\nconst HTTP_PAYMENT_REQUIRED = 402;\nconst HTTP_PAYLOAD_TOO_LARGE = 413;\n/** High 32 bits ms, low 32 bits random: monotonic across ticks, unique within one. */\nconst NONCE_TIMESTAMP_SHIFT = 32n;\nconst UINT256_DECIMAL = /^(0|[1-9]\\d*)$/;\nconst POSITIVE_UINT256_DECIMAL = /^[1-9]\\d*$/;\nconst MILLISECONDS_PER_SECOND = 1_000;\nconst DEFAULT_JOB_TIMEOUT_MS = 120_000;\nconst DEFAULT_JOB_POLL_MS = CLAIM_POLL_FLOOR_MS;\nconst TERMINAL_JOB_STATES: ReadonlySet<JobState> = new Set([\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n]);\n\n/** A viem local account that exposes the public key required by job requests. */\nexport type JobsBuilderAccount = Extract<LocalAccount, WriteSignerSource>;\n\n/** Configuration captured by {@link createJobsClient}. */\nexport interface JobsClientOptions {\n /** Gateway base URL; its origin is the Web3Signed audience. */\n gatewayUrl: string;\n /** Vana chain ID used by the owner identity lookup. */\n chainId: number;\n /** Raw builder private key, used for signing and result decryption. */\n builderPrivateKey?: Hex;\n /**\n * viem local account used for signing. Its public key supports submission,\n * but without `builderPrivateKey` this client cannot decrypt results:\n * `openResult` and `readRaw` reject while submit/status/wait remain usable.\n */\n builderAccount?: JobsBuilderAccount;\n /** HTTP implementation; defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /** ECIES implementation; defaults to the Node provider. */\n ecies?: ECIESProvider;\n /** Clock used when constructing a job deadline. */\n now?: () => Date;\n /**\n * `DataPortabilityEscrow` address for the payment signature's EIP-712 domain.\n * Defaults to the deployment this SDK ships for `chainId`; override only when\n * pointing at a non-canonical escrow.\n */\n escrowContract?: Address;\n}\n\n/** Parameters for one encrypted raw-read submission. */\nexport interface SubmitRawReadParams {\n /** Owner whose enclave executes the read. */\n owner: Address;\n /** Owner-issued builder grant. */\n grantId: Hex;\n /** Data scope to read. */\n scope: string;\n /** Exact record version to require, or `null` for an unpinned read. */\n pinnedVersion?: string | null;\n /** Requested deadline offset, clamped to the protocol maximum. */\n deadlineSeconds?: number;\n /** Gateway inline-wait seconds, clamped to `0..MAX_WAIT_SECONDS`. */\n wait?: number;\n /**\n * The most this read may cost, uint256 decimal. A quote above it is refused\n * before anything is signed. Omit to accept whatever the chain's FeeRegistry\n * says at submission time.\n */\n maxPrice?: string;\n /**\n * Reuse a job id across attempts instead of minting one.\n *\n * @remarks\n * A charged read reserves escrow when the Gateway first accepts the job, so a\n * response lost in transit is ambiguous: retrying with a fresh id would\n * reserve a second time. Supply the id (and {@link idempotencyKey}) from\n * durable storage and a resubmission replays the original job and reserves\n * nothing. `JobTransportError.details` carries both back when a submission\n * fails mid-flight.\n */\n jobId?: string;\n idempotencyKey?: string;\n}\n\n/** Result of submitting an encrypted raw-read job. */\nexport interface SubmitRawReadResult {\n /** Client-generated job UUID. */\n jobId: string;\n /** Current Gateway state. */\n state: JobState;\n /** Full status when the Gateway returned an inline 200 response. */\n job?: JobStatus;\n}\n\n/** Polling controls for {@link JobsClient.waitForJob}. */\nexport interface WaitForJobOptions {\n /** Total polling budget in milliseconds. */\n timeoutMs?: number;\n /** Delay between reads; values below `CLAIM_POLL_FLOOR_MS` are raised. */\n pollMs?: number;\n}\n\n/** Expected plaintext bindings for {@link JobsClient.openResult}. */\nexport interface OpenJobResultOptions {\n /** Bind the plaintext to this job id. */\n expect: {\n jobId: string;\n scope?: string;\n version?: string | null;\n };\n}\n\n/** Parameters for the submit, wait, and decrypt convenience flow. */\nexport interface ReadRawParams extends SubmitRawReadParams, WaitForJobOptions {}\n\n/** Builder operations exposed by {@link createJobsClient}. */\nexport interface JobsClient {\n /**\n * Encrypt and submit a raw-read job.\n *\n * @param params - Owner, grant, scope, deadline, and inline-wait controls.\n * @returns The job id and current state, plus inline status on HTTP 200.\n * @throws {OwnerNotReadyError} When the owner identity is not sealed.\n * @throws {JobsClientError} When the Gateway or transport rejects the call.\n */\n submitRawRead(params: SubmitRawReadParams): Promise<SubmitRawReadResult>;\n\n /**\n * Ask what one read costs on this chain before committing to it.\n *\n * @returns The chain's `data_access` fee, its asset, and whether the Gateway\n * enforces it today.\n * @throws {JobsClientError} When the Gateway or transport rejects the call.\n *\n * @remarks\n * Optional: {@link JobsClient.submitRawRead} signs the quote the Gateway\n * returns with its 402, so a builder that does not care about the price ahead\n * of time never needs this call.\n */\n quoteJob(): Promise<JobQuote>;\n\n /**\n * Read one builder-visible job status.\n *\n * @param jobId - Client-generated job UUID.\n * @returns The current job status.\n * @throws {JobNotFoundError} When the job is absent or belongs to another builder.\n * @throws {JobsClientError} For other Gateway or transport failures.\n */\n getJob(jobId: string): Promise<JobStatus>;\n\n /**\n * Poll until a job completes, fails, expires, or is cancelled.\n *\n * @param jobId - Job UUID to poll.\n * @param options - Timeout and poll cadence.\n * @returns The terminal job status.\n * @throws {JobTimeoutError} When the caller or job deadline is exhausted.\n */\n waitForJob(jobId: string, options?: WaitForJobOptions): Promise<JobStatus>;\n\n /**\n * Fetch, decrypt, and validate a job result from object storage.\n *\n * @param handle - Object-storage location and integrity metadata.\n * @param options - Expected plaintext job, scope, and version bindings.\n * @returns The validated plaintext job result.\n * @remarks Decryption requires `builderPrivateKey`; `builderAccount` alone\n * only supports submission, status reads, and waiting.\n * @throws {JobRejectedError} When a raw private key is unavailable.\n * @throws {JobTransportError} When fetching the result object fails.\n * @throws {JobResultIntegrityError} When the fetched bytes mismatch the handle.\n * @throws {JobEnvelopeError} When decrypted protocol fields or bindings differ.\n */\n openResult(\n handle: ResultHandle,\n options: OpenJobResultOptions,\n ): Promise<JobResult>;\n\n /**\n * Submit, wait for, and decrypt one raw read.\n *\n * @param params - Raw-read request plus polling controls.\n * @returns The decrypted, binding-checked job result.\n * @remarks This convenience flow decrypts the result and therefore requires\n * `builderPrivateKey`; a client configured only with `builderAccount`\n * cannot call `readRaw`.\n * @throws {JobsClientError} When submission, polling, or decryption setup fails.\n * @throws {JobEnvelopeError} When decrypted result bindings differ.\n *\n * @example\n * ```typescript\n * const result = await client.readRaw({\n * owner,\n * grantId,\n * scope: \"profile.email\",\n * wait: 25,\n * });\n * ```\n */\n readRaw(params: ReadRawParams): Promise<JobResult>;\n}\n\ninterface ResolvedBuilder {\n address: Address;\n publicKey: Hex;\n signMessage: ReturnType<typeof resolveWriteSigner>[\"signMessage\"];\n /**\n * Present on any account that can sign typed data, which is every local\n * account. Absent only for a message-only signer, which cannot authorize a\n * price — so paying fails with a clear error rather than at construction,\n * where most reads are free and the signer would never be asked.\n */\n signTypedData?: LocalAccount[\"signTypedData\"];\n}\n\ninterface GatewayErrorBody {\n code: JobGatewayErrorCode | null;\n message: string;\n reason?: string;\n details?: Record<string, unknown>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction normalizeGateway(gatewayUrl: string): {\n baseUrl: string;\n audience: string;\n} {\n try {\n const url = new URL(gatewayUrl);\n if (url.pathname !== \"/\" || url.search !== \"\" || url.hash !== \"\") {\n throw new JobRejectedError(\n \"gatewayUrl must be an origin without a path, query, or fragment\",\n );\n }\n return {\n baseUrl: url.origin,\n audience: url.origin,\n };\n } catch (error) {\n if (error instanceof JobRejectedError) throw error;\n throw new JobRejectedError(\n \"gatewayUrl must be an absolute URL\",\n undefined,\n null,\n {\n cause: error instanceof Error ? error.message : String(error),\n },\n );\n }\n}\n\nfunction resolveFetch(fetchFn?: typeof fetch): typeof fetch {\n const resolved = fetchFn ?? globalThis.fetch;\n if (resolved === undefined) {\n throw new JobRejectedError(\"No fetch implementation available\");\n }\n return resolved;\n}\n\nfunction sourcePublicKey(source: WriteSignerSource): Hex | undefined {\n if (isRecord(source) && typeof source[\"publicKey\"] === \"string\") {\n return source[\"publicKey\"] as Hex;\n }\n return undefined;\n}\n\nfunction resolveBuilder(options: JobsClientOptions): {\n builder: ResolvedBuilder;\n privateKey?: Hex;\n} {\n if (options.builderPrivateKey && options.builderAccount) {\n throw new JobRejectedError(\n \"Provide builderPrivateKey or builderAccount, not both\",\n );\n }\n let source: WriteSignerSource | undefined;\n try {\n source = options.builderPrivateKey\n ? privateKeyToAccount(options.builderPrivateKey)\n : options.builderAccount;\n } catch (error) {\n throw new JobRejectedError(\"Invalid builder private key\", undefined, null, {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n if (!source) {\n throw new JobRejectedError(\n \"builderPrivateKey or builderAccount is required\",\n );\n }\n let signer: ReturnType<typeof resolveWriteSigner>;\n try {\n signer = resolveWriteSigner(source);\n } catch (error) {\n throw new JobRejectedError(\"Invalid builder signer\", undefined, null, {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n const publicKey = sourcePublicKey(source);\n if (!signer.address || !publicKey) {\n throw new JobRejectedError(\n \"Builder signer must expose both address and publicKey\",\n );\n }\n const typedDataSigner = (source as Partial<LocalAccount>).signTypedData;\n return {\n builder: {\n address: signer.address,\n publicKey,\n signMessage: signer.signMessage,\n ...(typedDataSigner\n ? {\n signTypedData: typedDataSigner.bind(\n source,\n ) as LocalAccount[\"signTypedData\"],\n }\n : {}),\n },\n ...(options.builderPrivateKey\n ? { privateKey: options.builderPrivateKey }\n : {}),\n };\n}\n\nfunction clampInteger(\n value: number | undefined,\n fallback: number,\n minimum: number,\n maximum: number,\n field: string,\n): number {\n const resolved = value ?? fallback;\n if (!Number.isFinite(resolved)) {\n throw new JobRejectedError(`${field} must be a finite number`);\n }\n return Math.min(maximum, Math.max(minimum, Math.trunc(resolved)));\n}\n\nasync function responseBody(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n}\n\nfunction gatewayErrorBody(body: unknown, fallback: string): GatewayErrorBody {\n if (!isRecord(body)) return { code: null, message: fallback };\n const code = typeof body[\"code\"] === \"string\" ? body[\"code\"] : null;\n const message =\n typeof body[\"error\"] === \"string\"\n ? body[\"error\"]\n : typeof body[\"message\"] === \"string\"\n ? body[\"message\"]\n : fallback;\n const reason =\n typeof body[\"reason\"] === \"string\" ? body[\"reason\"] : undefined;\n const details = isRecord(body[\"details\"]) ? body[\"details\"] : undefined;\n return {\n code,\n message,\n ...(reason ? { reason } : {}),\n ...(details ? { details } : {}),\n };\n}\n\nasync function rejectedResponse(\n response: Response,\n fallback: string,\n): Promise<JobsClientError> {\n const parsed = gatewayErrorBody(await responseBody(response), fallback);\n const details =\n response.status === HTTP_UNAUTHORIZED && parsed.reason\n ? { ...parsed.details, reason: parsed.reason }\n : parsed.details;\n const message =\n response.status === HTTP_UNAUTHORIZED && parsed.reason\n ? `${parsed.message}: ${parsed.reason}`\n : parsed.message;\n if (response.status === HTTP_FORBIDDEN) {\n if (parsed.code === \"BUILDER_UNKNOWN\") {\n return new BuilderUnknownError(parsed.message, details);\n }\n if (parsed.code === \"GRANT_INVALID\") {\n return new GrantInvalidError(parsed.message, details);\n }\n if (parsed.code === \"OWNER_NOT_READY\") {\n return new OwnerNotReadyError(parsed.message, details);\n }\n }\n if (response.status === HTTP_NOT_FOUND) {\n return new JobNotFoundError(parsed.message, details);\n }\n if (response.status === HTTP_CONFLICT && parsed.code === \"JOB_ID_TAKEN\") {\n return new JobIdTakenError(parsed.message, details);\n }\n if (response.status === HTTP_PAYLOAD_TOO_LARGE) {\n return new JobRequestTooLargeError(parsed.message, parsed.code, details);\n }\n return new JobRejectedError(message, response.status, parsed.code, details);\n}\n\nfunction requireStatus(value: unknown, status: number): JobStatus {\n if (\n !isRecord(value) ||\n !isNonEmptyString(value[\"jobId\"]) ||\n typeof value[\"state\"] !== \"string\" ||\n !JOB_STATES.includes(value[\"state\"] as JobState) ||\n !isNonEmptyString(value[\"owner\"]) ||\n !isNonEmptyString(value[\"grantId\"]) ||\n !isNonEmptyString(value[\"scope\"]) ||\n !isNonEmptyString(value[\"operation\"]) ||\n !JOB_OPERATIONS.includes(value[\"operation\"] as JobOperation) ||\n (value[\"state\"] === \"completed\" && !isResultHandle(value[\"result\"]))\n ) {\n throw new JobRejectedError(\n \"Gateway response did not contain a job status\",\n status,\n );\n }\n return value as unknown as JobStatus;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction isResultHandle(value: unknown): value is ResultHandle {\n if (\n !isRecord(value) ||\n !isNonEmptyString(value[\"objectKey\"]) ||\n !isNonEmptyString(value[\"url\"]) ||\n typeof value[\"size\"] !== \"number\" ||\n !Number.isInteger(value[\"size\"]) ||\n value[\"size\"] < 0 ||\n typeof value[\"hash\"] !== \"string\" ||\n !/^0x[0-9a-fA-F]{64}$/.test(value[\"hash\"]) ||\n typeof value[\"expiresAt\"] !== \"string\" ||\n Number.isNaN(Date.parse(value[\"expiresAt\"]))\n ) {\n return false;\n }\n try {\n const url = new URL(value[\"url\"]);\n return url.protocol === \"https:\" || url.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n\nfunction hasMatchingIdentityKey(value: Record<string, unknown>): boolean {\n const address = value[\"address\"];\n const publicKey = value[\"publicKey\"];\n if (\n typeof address !== \"string\" ||\n !isAddress(address) ||\n typeof publicKey !== \"string\" ||\n !isHex(publicKey)\n ) {\n return false;\n }\n try {\n return (\n publicKeyToAddress(publicKey).toLowerCase() === address.toLowerCase()\n );\n } catch {\n return false;\n }\n}\n\nfunction hasExpectedIdentity(\n value: Record<string, unknown>,\n owner: Address,\n chainId: number,\n): boolean {\n const identityOwner = value[\"ownerAddress\"];\n const identityChainId = value[\"chainId\"];\n const identityUserPsId = value[\"userPsId\"];\n if (\n !hasMatchingIdentityKey(value) ||\n typeof identityOwner !== \"string\" ||\n !isAddress(identityOwner) ||\n typeof identityChainId !== \"number\" ||\n typeof identityUserPsId !== \"string\" ||\n !isHex(identityUserPsId)\n ) {\n return false;\n }\n return (\n isAddressEqual(identityOwner, owner) &&\n identityChainId === chainId &&\n identityUserPsId.toLowerCase() === userPsId(chainId, owner).toLowerCase()\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * The escrow the payment signature names. Resolved from the chain the client is\n * configured for, so a builder never has to know the address.\n *\n * Resolved when a price is actually signed, not at construction: most reads are\n * free, and a client on a chain with no escrow deployment must still be able to\n * submit them.\n */\nfunction resolveEscrowContract(options: JobsClientOptions): Address {\n if (options.escrowContract) return options.escrowContract;\n const addresses = CONTRACTS[\"DataPortabilityEscrow\"]?.addresses as\n | Record<number, string>\n | undefined;\n const address = addresses?.[options.chainId];\n if (!address || !isAddress(address)) {\n throw new JobRejectedError(\n `No DataPortabilityEscrow address for chain ${options.chainId}; pass escrowContract`,\n );\n }\n return address as Address;\n}\n\n/**\n * High 32 bits are the millisecond clock, low 32 bits are random: strictly\n * increasing across ticks, collision-free within one. Matches the nonce the\n * Personal Server mints for a legacy paid read.\n */\nfunction nextPaymentNonce(): bigint {\n const random = new Uint32Array(1);\n globalThis.crypto.getRandomValues(random);\n return (BigInt(Date.now()) << NONCE_TIMESTAMP_SHIFT) + BigInt(random[0] ?? 0);\n}\n\n/** Validate every field rather than assert the shape of an external body. */\nfunction parseJobQuote(body: unknown): JobQuote | null {\n if (!isRecord(body)) return null;\n const { chainId, price, asset, payable, enforced } = body;\n if (typeof chainId !== \"number\" || !Number.isSafeInteger(chainId))\n return null;\n if (typeof price !== \"string\" || !UINT256_DECIMAL.test(price)) return null;\n if (typeof asset !== \"string\" || !isAddress(asset)) return null;\n if (typeof payable !== \"boolean\" || typeof enforced !== \"boolean\")\n return null;\n return { chainId, price, asset: asset as Address, payable, enforced };\n}\n\n/** The price the Gateway is asking for, parsed from its 402. */\nasync function paymentQuote(\n response: Response,\n): Promise<{ asset: Address; amount: string }> {\n const body = await responseBody(response);\n const asset = isRecord(body) ? body[\"asset\"] : undefined;\n const amount = isRecord(body) ? body[\"amount\"] : undefined;\n if (\n typeof asset !== \"string\" ||\n !isAddress(asset) ||\n typeof amount !== \"string\" ||\n !POSITIVE_UINT256_DECIMAL.test(amount)\n ) {\n throw new JobRejectedError(\n \"Gateway asked for payment without a usable quote\",\n response.status,\n );\n }\n return { asset: asset as Address, amount };\n}\n\n/** A second 402: the escrow cannot back this read, or the price moved. */\nasync function paymentRefusal(\n response: Response,\n): Promise<{ message: string; details: Record<string, unknown> }> {\n const body = await responseBody(response);\n const parsed = isRecord(body) ? body : {};\n const message =\n typeof parsed[\"error\"] === \"string\"\n ? parsed[\"error\"]\n : \"This read requires payment the builder escrow cannot back\";\n return { message, details: parsed };\n}\n\nfunction requireFiniteDuration(value: number, field: string): number {\n if (!Number.isFinite(value)) {\n throw new JobRejectedError(`${field} must be a finite number`);\n }\n return Math.max(0, value);\n}\n\n/**\n * Create a reusable Node builder client for encrypted Gateway jobs.\n *\n * @param options - Gateway, chain, builder signer, and injectable adapters.\n * @returns A client bound to the configured builder and Gateway.\n * @throws {JobRejectedError} When configuration or signer shape is invalid.\n *\n * @remarks\n * This client is Node-only. `builderPrivateKey` supports the complete flow;\n * `builderAccount` alone supports submit/status/wait but cannot decrypt, so\n * `openResult` and `readRaw` require the raw private key.\n *\n * @example\n * ```typescript\n * const client = createJobsClient({\n * gatewayUrl: \"https://gateway.example.com\",\n * chainId: 14800,\n * builderPrivateKey: process.env.BUILDER_PRIVATE_KEY as Hex,\n * });\n * const result = await client.readRaw({ owner, grantId, scope: \"profile\" });\n * ```\n */\nexport function createJobsClient(options: JobsClientOptions): JobsClient {\n const { baseUrl, audience } = normalizeGateway(options.gatewayUrl);\n const fetchFn = resolveFetch(options.fetch);\n const { builder, privateKey } = resolveBuilder(options);\n const ecies = options.ecies ?? new NodeECIESUint8Provider();\n const now = options.now ?? (() => new Date());\n\n /**\n * Sign the Gateway's quote as the same EIP-712 `GenericPayment` a legacy\n * Personal Server read signs into its `X-PAYMENT` header. The Gateway stores\n * it verbatim on the payment row, so one money artifact covers both paths.\n */\n async function signQuote(\n jobId: string,\n quote: { asset: Address; amount: string },\n ): Promise<JobPaymentAuthorization> {\n if (!builder.signTypedData) {\n throw new JobRejectedError(\n \"This read is charged, but the builder signer cannot sign typed data\",\n );\n }\n const paymentNonce = nextPaymentNonce();\n const signature = await builder.signTypedData({\n domain: genericPaymentDomain(\n options.chainId,\n resolveEscrowContract(options),\n ),\n types: GENERIC_PAYMENT_TYPES,\n primaryType: \"GenericPayment\",\n message: {\n payerAddress: builder.address,\n opType: JOB_ACCESS_OP_TYPE,\n opId: keccak256(toHex(jobId.toLowerCase())),\n asset: quote.asset,\n amount: BigInt(quote.amount),\n paymentNonce,\n },\n });\n return {\n signature,\n amount: quote.amount,\n asset: quote.asset,\n paymentNonce: paymentNonce.toString(),\n };\n }\n\n async function postSubmission(\n submission: JobSubmission,\n wait: number,\n ): Promise<Response> {\n const identifiers = {\n jobId: submission.jobId,\n idempotencyKey: submission.idempotencyKey,\n };\n const bodyBytes = new TextEncoder().encode(JSON.stringify(submission));\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: POST_METHOD,\n uri: JOBS_PATH,\n body: bodyBytes,\n nonce: randomUUID(),\n });\n try {\n return await request(\n `${baseUrl}${JOBS_PATH}?wait=${wait}`,\n {\n method: POST_METHOD,\n headers: {\n [AUTHORIZATION_HEADER]: authorization,\n [CONTENT_TYPE_HEADER]: JSON_CONTENT_TYPE,\n },\n body: bodyBytes,\n },\n \"Job submission\",\n );\n } catch (error) {\n // The Gateway may have committed the job and its escrow reservation\n // before the response was lost. Hand the ids back so the caller can\n // resubmit them or reconcile with getJob, rather than mint new ones and\n // reserve twice.\n if (error instanceof JobTransportError) {\n throw new JobTransportError(error.message, error.cause, {\n ...error.details,\n ...identifiers,\n });\n }\n throw error;\n }\n }\n\n async function request(\n url: string,\n init: RequestInit,\n description: string,\n ): Promise<Response> {\n try {\n return await fetchFn(url, init);\n } catch (error) {\n throw new JobTransportError(\n `${description} failed: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n async function getIdentity(\n owner: Address,\n ): Promise<IdentityResponse[\"identity\"]> {\n const query = new URLSearchParams({\n owner,\n chainId: String(options.chainId),\n });\n const response = await request(\n `${baseUrl}${IDENTITY_PATH}?${query.toString()}`,\n { method: GET_METHOD },\n \"Owner identity lookup\",\n );\n if (response.status === HTTP_NOT_FOUND) {\n const parsed = gatewayErrorBody(\n await responseBody(response),\n \"Owner identity not found\",\n );\n throw new OwnerNotReadyError(parsed.message, parsed.details);\n }\n if (!response.ok) {\n throw await rejectedResponse(response, \"Owner identity lookup rejected\");\n }\n const body = await responseBody(response);\n if (\n !isRecord(body) ||\n body[\"state\"] !== SEALED_IDENTITY_STATE ||\n !isRecord(body[\"identity\"])\n ) {\n throw new OwnerNotReadyError(\"Owner identity is not sealed\", {\n state: isRecord(body) ? body[\"state\"] : undefined,\n });\n }\n if (!hasExpectedIdentity(body[\"identity\"], owner, options.chainId)) {\n throw new JobRejectedError(\n \"Gateway returned a malformed or mismatched owner identity\",\n response.status,\n );\n }\n // Full TDX/KMS evidence verification awaits provisioned trust anchors and is intentionally outside this driver-matching client.\n return body[\"identity\"] as unknown as IdentityResponse[\"identity\"];\n }\n\n const client: JobsClient = {\n async submitRawRead(params) {\n const identity = await getIdentity(params.owner);\n const deadlineSeconds = clampInteger(\n params.deadlineSeconds,\n DEFAULT_JOB_DEADLINE_SECONDS,\n 1,\n MAX_JOB_DEADLINE_SECONDS,\n \"deadlineSeconds\",\n );\n const wait = clampInteger(params.wait, 0, 0, MAX_WAIT_SECONDS, \"wait\");\n // Caller-supplied ids make a resubmission exactly-once: the Gateway\n // replays the original job and reserves nothing. Without them a lost\n // response is ambiguous, so the transport error carries both back.\n const jobId = params.jobId ?? randomUUID();\n const idempotencyKey = params.idempotencyKey ?? randomUUID();\n const nowMs = now().getTime();\n if (!Number.isFinite(nowMs)) {\n throw new JobRejectedError(\"now must return a valid Date\");\n }\n const deadline = new Date(\n nowMs + deadlineSeconds * MILLISECONDS_PER_SECOND,\n ).toISOString();\n const requestBody: JobRequest = {\n v: JOB_PROTOCOL_VERSION,\n jobId,\n owner: params.owner,\n builder: builder.address,\n builderPublicKey: builder.publicKey,\n grantId: params.grantId,\n scope: params.scope,\n operation: RAW_READ_OPERATION,\n pinnedVersion: params.pinnedVersion ?? null,\n deadline,\n };\n const auth = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: POST_METHOD,\n uri: JOB_EXECUTE_PATH,\n bodyHash: computeBodyHash(canonicalJobRequestBytes(requestBody)),\n nonce: randomUUID(),\n });\n const requestCiphertext = await sealJobRequest(\n { request: requestBody, auth },\n identity.publicKey,\n ecies,\n );\n const submission: JobSubmission = {\n owner: requestBody.owner,\n grantId: requestBody.grantId,\n scope: requestBody.scope,\n operation: requestBody.operation,\n idempotencyKey,\n jobId,\n deadline,\n requestCiphertext,\n ...(params.maxPrice === undefined ? {} : { maxPrice: params.maxPrice }),\n };\n let response = await postSubmission(submission, wait);\n\n // The Gateway answers an unpaid charged read with its quote, exactly as a\n // Personal Server answers an unpaid legacy read with a 402 challenge. Sign\n // the quote and retry once. The header is built once per attempt and the\n // job id is unchanged, so a refused first attempt left no job behind and\n // the retry cannot mint a second charge.\n if (response.status === HTTP_PAYMENT_REQUIRED) {\n const quote = await paymentQuote(response);\n // Refuse before signing, not after. The Gateway rechecks the ceiling,\n // but an EIP-712 authorization above what the builder agreed to must\n // never leave this process — once signed it is a standalone artifact.\n if (\n params.maxPrice !== undefined &&\n BigInt(quote.amount) > BigInt(params.maxPrice)\n ) {\n throw new PaymentRequiredError(\n \"The quoted price is above the maxPrice you set for this read\",\n {\n jobId,\n scope: params.scope,\n grantId: params.grantId,\n asset: quote.asset,\n amount: quote.amount,\n maxPrice: params.maxPrice,\n },\n );\n }\n submission.payment = await signQuote(jobId, quote);\n submission.priceAsset = quote.asset;\n response = await postSubmission(submission, wait);\n if (response.status === HTTP_PAYMENT_REQUIRED) {\n const refusal = await paymentRefusal(response);\n throw new PaymentRequiredError(refusal.message, {\n jobId,\n scope: params.scope,\n grantId: params.grantId,\n ...refusal.details,\n });\n }\n }\n\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job submission rejected\");\n }\n const body = await responseBody(response);\n if (response.status === HTTP_OK && isRecord(body)) {\n const job = requireStatus(body[\"job\"], response.status);\n if (job.jobId !== jobId) {\n throw new JobRejectedError(\n \"Gateway returned a job id that does not match the submission\",\n response.status,\n );\n }\n return { jobId, state: job.state, job };\n }\n if (\n response.status === HTTP_ACCEPTED &&\n isRecord(body) &&\n body[\"jobId\"] === jobId &&\n typeof body[\"state\"] === \"string\" &&\n JOB_STATES.includes(body[\"state\"] as JobState)\n ) {\n return { jobId, state: body[\"state\"] as JobState };\n }\n throw new JobRejectedError(\n \"Gateway returned an undocumented job submission response\",\n response.status,\n );\n },\n\n async quoteJob() {\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: GET_METHOD,\n uri: JOB_QUOTE_PATH,\n nonce: randomUUID(),\n });\n const response = await request(\n `${baseUrl}${JOB_QUOTE_PATH}`,\n {\n method: GET_METHOD,\n headers: { [AUTHORIZATION_HEADER]: authorization },\n },\n \"Job quote\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job quote rejected\");\n }\n const quote = parseJobQuote(await responseBody(response));\n if (!quote) {\n throw new JobRejectedError(\n \"Gateway returned an undocumented job quote\",\n response.status,\n );\n }\n // A quote priced on another chain would be signed against the wrong\n // escrow domain and refused, so reject it here where the reason is clear.\n if (quote.chainId !== options.chainId) {\n throw new JobRejectedError(\n `Gateway quoted chain ${quote.chainId}, not the configured ${options.chainId}`,\n response.status,\n );\n }\n return quote;\n },\n\n async getJob(jobId) {\n const path = `${JOBS_PATH}/${encodeURIComponent(jobId)}`;\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: GET_METHOD,\n uri: path,\n nonce: randomUUID(),\n });\n const response = await request(\n `${baseUrl}${path}`,\n {\n method: GET_METHOD,\n headers: { [AUTHORIZATION_HEADER]: authorization },\n },\n \"Job status read\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job status read rejected\");\n }\n const body = await responseBody(response);\n return requireStatus(\n isRecord(body) ? body[\"job\"] : undefined,\n response.status,\n );\n },\n\n async waitForJob(jobId, waitOptions = {}) {\n const timeoutMs = requireFiniteDuration(\n waitOptions.timeoutMs ?? DEFAULT_JOB_TIMEOUT_MS,\n \"timeoutMs\",\n );\n const pollMs = Math.max(\n CLAIM_POLL_FLOOR_MS,\n requireFiniteDuration(\n waitOptions.pollMs ?? DEFAULT_JOB_POLL_MS,\n \"pollMs\",\n ),\n );\n const startedAt = Date.now();\n const callerDeadline = startedAt + timeoutMs;\n let latest: JobStatus | undefined;\n for (;;) {\n if (Date.now() >= callerDeadline) {\n throw new JobTimeoutError(\n `Timed out waiting for terminal job ${jobId}`,\n {\n jobId,\n timeoutMs,\n state: latest?.state,\n },\n );\n }\n latest = await client.getJob(jobId);\n if (TERMINAL_JOB_STATES.has(latest.state)) return latest;\n const remaining = callerDeadline - Date.now();\n if (remaining <= 0) {\n throw new JobTimeoutError(\n `Timed out waiting for terminal job ${jobId}`,\n {\n jobId,\n timeoutMs,\n state: latest.state,\n },\n );\n }\n await sleep(Math.min(pollMs, remaining));\n }\n },\n\n async openResult(handle, openOptions) {\n if (!privateKey) {\n throw new JobRejectedError(\n \"openResult requires createJobsClient({ builderPrivateKey })\",\n );\n }\n const response = await request(\n handle.url,\n { method: GET_METHOD },\n \"Job result fetch\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job result fetch rejected\");\n }\n let bytes: Uint8Array;\n try {\n bytes = new Uint8Array(await response.arrayBuffer());\n } catch (error) {\n throw new JobTransportError(\n `Job result fetch failed: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n const actualHash = bytesToHex(sha256(bytes));\n if (actualHash.toLowerCase() !== handle.hash.toLowerCase()) {\n throw new JobResultIntegrityError(\n \"Fetched job result hash does not match its handle\",\n {\n objectKey: handle.objectKey,\n expectedHash: handle.hash,\n actualHash,\n },\n );\n }\n if (bytes.length !== handle.size) {\n throw new JobResultIntegrityError(\n \"Fetched job result size does not match its handle\",\n {\n objectKey: handle.objectKey,\n expectedSize: handle.size,\n actualSize: bytes.length,\n },\n );\n }\n return openJobResult(bytes, privateKey, ecies, openOptions.expect);\n },\n\n async readRaw(params) {\n const submitted = await client.submitRawRead(params);\n const job =\n submitted.job && TERMINAL_JOB_STATES.has(submitted.job.state)\n ? submitted.job\n : await client.waitForJob(submitted.jobId, {\n timeoutMs: params.timeoutMs,\n pollMs: params.pollMs,\n });\n if (job.state !== \"completed\") {\n throw new JobRejectedError(\n `Job ${job.jobId} ended in state ${job.state}${job.failureReason ? `: ${job.failureReason}` : \"\"}`,\n undefined,\n null,\n {\n jobId: job.jobId,\n state: job.state,\n ...(job.failureReason ? { failureReason: job.failureReason } : {}),\n },\n );\n }\n if (!job.result) {\n throw new JobRejectedError(\n \"Completed job status does not include a result handle\",\n undefined,\n null,\n { jobId: job.jobId, state: job.state },\n );\n }\n return client.openResult(job.result, {\n expect: {\n jobId: submitted.jobId,\n scope: params.scope,\n ...(params.pinnedVersion !== undefined &&\n params.pinnedVersion !== null\n ? { version: params.pinnedVersion }\n : {}),\n },\n });\n },\n };\n\n return client;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n \"queued\",\n \"claimed\",\n \"running\",\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\nexport type PaymentState = \"none\" | \"reserved\" | \"settled\";\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n v: 1;\n jobId: string;\n owner: Address;\n builder: Address;\n builderPublicKey: Hex;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n request: JobRequest;\n /**\n * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n * `uri` = `/v1/jobs/execute`,\n * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n */\n auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n owner: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n idempotencyKey: string;\n /** Client UUID, echoed in `JobRequest.jobId`. */\n jobId: string;\n deadline?: string;\n /** Base64 ECIES from `sealJobRequest`. */\n requestCiphertext: string;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n objectKey: string;\n /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n url: string;\n /** Byte length of the sealed object. */\n size: number;\n /** sha256 of the sealed bytes, 0x-prefixed. */\n hash: Hex;\n /** Logical expiry. After this the Gateway stops serving the handle. */\n expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n jobId: string;\n state: JobState;\n operation: JobOperation;\n owner: Address;\n grantId: Hex;\n scope: string;\n pinnedVersion: string | null;\n attempt: number;\n price: string;\n payer: \"builder\";\n paymentState: PaymentState;\n createdAt: string;\n claimedAt: string | null;\n completedAt: string | null;\n failureReason: string | null;\n /** Present only when `state === \"completed\"`. */\n result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n leaseSeconds?: number;\n capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n job: {\n jobId: string;\n owner: Address;\n builder: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n requestCiphertext: string;\n attempt: number;\n deadlineAt: string | null;\n claimExpiresAt: string;\n fencingToken: number; /* = attempt; every node write echoes it */\n };\n identity: {\n userPsId: Hex;\n epoch: number;\n enclaveAddress: Address;\n enclavePublicKey: Hex;\n sealedEnvelope: SealedEnvelope;\n };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n leaseSeconds?: number;\n fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n fencingToken: number;\n resultHash: Hex;\n resultSize: number;\n /** `jobresults/{chainId}/{jobId}`. */\n resultObjectKey: string;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n fencingToken: number;\n reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n success: true;\n jobId: string;\n state: JobState;\n claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n v: 1;\n jobId: string;\n scope: string;\n version: string | null;\n contentType: string;\n /** Raw result bytes; callers decode text explicitly when appropriate. */\n body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n capacity: number;\n secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n composeHash: Hex;\n instanceId: string;\n activeSandboxes: number;\n capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n state: TeeNodeState;\n capacity: number;\n activeSandboxes: number;\n lastHeartbeatAt: string | null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}
1
+ {"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n \"queued\",\n \"claimed\",\n \"running\",\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\n/**\n * What actually happened to the money for one read.\n *\n * `none` is the pre-fee literal every job carried before job fees and stays in\n * the union for those rows. `free` is a chain whose `data_access` fee is\n * disabled or zero — priced honestly at nothing, not a placeholder. `unbilled`\n * is a read the Gateway priced but did not charge, either during the fee\n * rollout or on a path with no payer.\n */\nexport type PaymentState =\n | \"none\"\n | \"free\"\n | \"unbilled\"\n | \"reserved\"\n | \"settling\"\n | \"settled\"\n | \"released\";\n\n/**\n * The builder's signed authorization for the price of one read.\n *\n * The same EIP-712 `GenericPayment` a legacy Personal Server read signs into\n * its `X-PAYMENT` header, over `opType: \"job_access\"` and\n * `opId: keccak256(jobId)`. The Gateway stores it verbatim on the payment row.\n */\nexport interface JobPaymentAuthorization {\n signature: Hex;\n /** uint256 decimal; must equal the Gateway's quote for this chain. */\n amount: string;\n asset: Address;\n /** uint256 decimal, unique per (payer, kind). */\n paymentNonce: string;\n}\n\n/** The price of one delivered read, as `GET /v1/jobs/quote` reports it. */\nexport interface JobQuote {\n chainId: number;\n /** uint256 decimal; \"0\" when this chain charges nothing. */\n price: string;\n asset: Address;\n /** False when the fee is disabled or zero, so no payment is needed. */\n payable: boolean;\n /** Whether the Gateway refuses an unpaid submission today. */\n enforced: boolean;\n}\n\n/** RecordDataAccess receipt fields plus the enclave signature over them. */\nexport interface JobAccessRecord {\n dataPointId: Hex;\n /** uint256 decimal — the data-point version actually served. */\n version: string;\n accessor: Address;\n /** bytes32 per-event replay nonce; the contract pins it. */\n recordId: Hex;\n signature: Hex;\n}\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n v: 1;\n jobId: string;\n owner: Address;\n builder: Address;\n builderPublicKey: Hex;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n request: JobRequest;\n /**\n * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n * `uri` = `/v1/jobs/execute`,\n * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n */\n auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n owner: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n idempotencyKey: string;\n /** Client UUID, echoed in `JobRequest.jobId`. */\n jobId: string;\n deadline?: string;\n /** Base64 ECIES from `sealJobRequest`. */\n requestCiphertext: string;\n /**\n * The most the builder accepts for this read, uint256 decimal. The Gateway\n * refuses a quote above it rather than charging a price nobody agreed to.\n *\n * @remarks\n * This submission is authenticated by a Web3Signed body hash, not by an\n * EIP-712 struct, so these three fields are covered by the builder's existing\n * request signature. No typed-data version changes.\n */\n maxPrice?: string;\n priceAsset?: Address;\n payment?: JobPaymentAuthorization;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n objectKey: string;\n /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n url: string;\n /** Byte length of the sealed object. */\n size: number;\n /** sha256 of the sealed bytes, 0x-prefixed. */\n hash: Hex;\n /** Logical expiry. After this the Gateway stops serving the handle. */\n expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n jobId: string;\n state: JobState;\n operation: JobOperation;\n owner: Address;\n grantId: Hex;\n scope: string;\n pinnedVersion: string | null;\n attempt: number;\n price: string;\n /**\n * Null exactly when the read is free; nothing owed is owed to nobody.\n * Optional so a consumer's existing `JobStatus` fixtures keep compiling; the\n * Gateway always sends it.\n */\n priceAsset?: Address | null;\n payer: \"builder\";\n paymentState: PaymentState;\n createdAt: string;\n claimedAt: string | null;\n completedAt: string | null;\n failureReason: string | null;\n /** Present only when `state === \"completed\"`. */\n result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n leaseSeconds?: number;\n capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n job: {\n jobId: string;\n owner: Address;\n builder: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n requestCiphertext: string;\n attempt: number;\n deadlineAt: string | null;\n claimExpiresAt: string;\n fencingToken: number; /* = attempt; every node write echoes it */\n };\n identity: {\n userPsId: Hex;\n epoch: number;\n enclaveAddress: Address;\n enclavePublicKey: Hex;\n sealedEnvelope: SealedEnvelope;\n };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n leaseSeconds?: number;\n fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n fencingToken: number;\n resultHash: Hex;\n resultSize: number;\n /** `jobresults/{chainId}/{jobId}`. */\n resultObjectKey: string;\n /**\n * Server-signed delivery receipt for the read this job served, minted by the\n * node agent with the owner's enclave wallet. Binds the reserved payment to\n * an on-chain `recordDataAccess`, exactly as the legacy paid read does.\n * Required whenever the job reserved a fee.\n */\n accessRecord?: JobAccessRecord;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n fencingToken: number;\n reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n success: true;\n jobId: string;\n state: JobState;\n claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n v: 1;\n jobId: string;\n scope: string;\n version: string | null;\n contentType: string;\n /** Raw result bytes; callers decode text explicitly when appropriate. */\n body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n capacity: number;\n secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n composeHash: Hex;\n instanceId: string;\n activeSandboxes: number;\n capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n state: TeeNodeState;\n capacity: number;\n activeSandboxes: number;\n lastHeartbeatAt: string | null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA2DO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}
@@ -21,7 +21,52 @@ export type JobOperation = (typeof JOB_OPERATIONS)[number];
21
21
  export declare const JOB_STATES: readonly ["queued", "claimed", "running", "completed", "failed", "expired", "cancelled"];
22
22
  export type JobState = (typeof JOB_STATES)[number];
23
23
  /** Payment lifecycle recorded for a queued job. */
24
- export type PaymentState = "none" | "reserved" | "settled";
24
+ /**
25
+ * What actually happened to the money for one read.
26
+ *
27
+ * `none` is the pre-fee literal every job carried before job fees and stays in
28
+ * the union for those rows. `free` is a chain whose `data_access` fee is
29
+ * disabled or zero — priced honestly at nothing, not a placeholder. `unbilled`
30
+ * is a read the Gateway priced but did not charge, either during the fee
31
+ * rollout or on a path with no payer.
32
+ */
33
+ export type PaymentState = "none" | "free" | "unbilled" | "reserved" | "settling" | "settled" | "released";
34
+ /**
35
+ * The builder's signed authorization for the price of one read.
36
+ *
37
+ * The same EIP-712 `GenericPayment` a legacy Personal Server read signs into
38
+ * its `X-PAYMENT` header, over `opType: "job_access"` and
39
+ * `opId: keccak256(jobId)`. The Gateway stores it verbatim on the payment row.
40
+ */
41
+ export interface JobPaymentAuthorization {
42
+ signature: Hex;
43
+ /** uint256 decimal; must equal the Gateway's quote for this chain. */
44
+ amount: string;
45
+ asset: Address;
46
+ /** uint256 decimal, unique per (payer, kind). */
47
+ paymentNonce: string;
48
+ }
49
+ /** The price of one delivered read, as `GET /v1/jobs/quote` reports it. */
50
+ export interface JobQuote {
51
+ chainId: number;
52
+ /** uint256 decimal; "0" when this chain charges nothing. */
53
+ price: string;
54
+ asset: Address;
55
+ /** False when the fee is disabled or zero, so no payment is needed. */
56
+ payable: boolean;
57
+ /** Whether the Gateway refuses an unpaid submission today. */
58
+ enforced: boolean;
59
+ }
60
+ /** RecordDataAccess receipt fields plus the enclave signature over them. */
61
+ export interface JobAccessRecord {
62
+ dataPointId: Hex;
63
+ /** uint256 decimal — the data-point version actually served. */
64
+ version: string;
65
+ accessor: Address;
66
+ /** bytes32 per-event replay nonce; the contract pins it. */
67
+ recordId: Hex;
68
+ signature: Hex;
69
+ }
25
70
  export declare const DEFAULT_LEASE_SECONDS = 30;
26
71
  export declare const MAX_LEASE_SECONDS = 300;
27
72
  export declare const MAX_ATTEMPTS = 3;
@@ -64,6 +109,18 @@ export interface JobSubmission {
64
109
  deadline?: string;
65
110
  /** Base64 ECIES from `sealJobRequest`. */
66
111
  requestCiphertext: string;
112
+ /**
113
+ * The most the builder accepts for this read, uint256 decimal. The Gateway
114
+ * refuses a quote above it rather than charging a price nobody agreed to.
115
+ *
116
+ * @remarks
117
+ * This submission is authenticated by a Web3Signed body hash, not by an
118
+ * EIP-712 struct, so these three fields are covered by the builder's existing
119
+ * request signature. No typed-data version changes.
120
+ */
121
+ maxPrice?: string;
122
+ priceAsset?: Address;
123
+ payment?: JobPaymentAuthorization;
67
124
  }
68
125
  /** Where a completed job's sealed result lives. Bytes never transit the Gateway. */
69
126
  export interface ResultHandle {
@@ -89,6 +146,12 @@ export interface JobStatus {
89
146
  pinnedVersion: string | null;
90
147
  attempt: number;
91
148
  price: string;
149
+ /**
150
+ * Null exactly when the read is free; nothing owed is owed to nobody.
151
+ * Optional so a consumer's existing `JobStatus` fixtures keep compiling; the
152
+ * Gateway always sends it.
153
+ */
154
+ priceAsset?: Address | null;
92
155
  payer: "builder";
93
156
  paymentState: PaymentState;
94
157
  createdAt: string;
@@ -139,6 +202,13 @@ export interface CompleteRequest {
139
202
  resultSize: number;
140
203
  /** `jobresults/{chainId}/{jobId}`. */
141
204
  resultObjectKey: string;
205
+ /**
206
+ * Server-signed delivery receipt for the read this job served, minted by the
207
+ * node agent with the owner's enclave wallet. Binds the reserved payment to
208
+ * an on-chain `recordDataAccess`, exactly as the legacy paid read does.
209
+ * Required whenever the job reserved a fee.
210
+ */
211
+ accessRecord?: JobAccessRecord;
142
212
  }
143
213
  /** Request body for `POST /v1/jobs/:id/fail`. */
144
214
  export interface FailRequest {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n \"queued\",\n \"claimed\",\n \"running\",\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\nexport type PaymentState = \"none\" | \"reserved\" | \"settled\";\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n v: 1;\n jobId: string;\n owner: Address;\n builder: Address;\n builderPublicKey: Hex;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n request: JobRequest;\n /**\n * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n * `uri` = `/v1/jobs/execute`,\n * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n */\n auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n owner: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n idempotencyKey: string;\n /** Client UUID, echoed in `JobRequest.jobId`. */\n jobId: string;\n deadline?: string;\n /** Base64 ECIES from `sealJobRequest`. */\n requestCiphertext: string;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n objectKey: string;\n /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n url: string;\n /** Byte length of the sealed object. */\n size: number;\n /** sha256 of the sealed bytes, 0x-prefixed. */\n hash: Hex;\n /** Logical expiry. After this the Gateway stops serving the handle. */\n expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n jobId: string;\n state: JobState;\n operation: JobOperation;\n owner: Address;\n grantId: Hex;\n scope: string;\n pinnedVersion: string | null;\n attempt: number;\n price: string;\n payer: \"builder\";\n paymentState: PaymentState;\n createdAt: string;\n claimedAt: string | null;\n completedAt: string | null;\n failureReason: string | null;\n /** Present only when `state === \"completed\"`. */\n result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n leaseSeconds?: number;\n capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n job: {\n jobId: string;\n owner: Address;\n builder: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n requestCiphertext: string;\n attempt: number;\n deadlineAt: string | null;\n claimExpiresAt: string;\n fencingToken: number; /* = attempt; every node write echoes it */\n };\n identity: {\n userPsId: Hex;\n epoch: number;\n enclaveAddress: Address;\n enclavePublicKey: Hex;\n sealedEnvelope: SealedEnvelope;\n };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n leaseSeconds?: number;\n fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n fencingToken: number;\n resultHash: Hex;\n resultSize: number;\n /** `jobresults/{chainId}/{jobId}`. */\n resultObjectKey: string;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n fencingToken: number;\n reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n success: true;\n jobId: string;\n state: JobState;\n claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n v: 1;\n jobId: string;\n scope: string;\n version: string | null;\n contentType: string;\n /** Raw result bytes; callers decode text explicitly when appropriate. */\n body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n capacity: number;\n secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n composeHash: Hex;\n instanceId: string;\n activeSandboxes: number;\n capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n state: TeeNodeState;\n capacity: number;\n activeSandboxes: number;\n lastHeartbeatAt: string | null;\n}\n"],"mappings":"AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}
1
+ {"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n \"queued\",\n \"claimed\",\n \"running\",\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\n/**\n * What actually happened to the money for one read.\n *\n * `none` is the pre-fee literal every job carried before job fees and stays in\n * the union for those rows. `free` is a chain whose `data_access` fee is\n * disabled or zero — priced honestly at nothing, not a placeholder. `unbilled`\n * is a read the Gateway priced but did not charge, either during the fee\n * rollout or on a path with no payer.\n */\nexport type PaymentState =\n | \"none\"\n | \"free\"\n | \"unbilled\"\n | \"reserved\"\n | \"settling\"\n | \"settled\"\n | \"released\";\n\n/**\n * The builder's signed authorization for the price of one read.\n *\n * The same EIP-712 `GenericPayment` a legacy Personal Server read signs into\n * its `X-PAYMENT` header, over `opType: \"job_access\"` and\n * `opId: keccak256(jobId)`. The Gateway stores it verbatim on the payment row.\n */\nexport interface JobPaymentAuthorization {\n signature: Hex;\n /** uint256 decimal; must equal the Gateway's quote for this chain. */\n amount: string;\n asset: Address;\n /** uint256 decimal, unique per (payer, kind). */\n paymentNonce: string;\n}\n\n/** The price of one delivered read, as `GET /v1/jobs/quote` reports it. */\nexport interface JobQuote {\n chainId: number;\n /** uint256 decimal; \"0\" when this chain charges nothing. */\n price: string;\n asset: Address;\n /** False when the fee is disabled or zero, so no payment is needed. */\n payable: boolean;\n /** Whether the Gateway refuses an unpaid submission today. */\n enforced: boolean;\n}\n\n/** RecordDataAccess receipt fields plus the enclave signature over them. */\nexport interface JobAccessRecord {\n dataPointId: Hex;\n /** uint256 decimal — the data-point version actually served. */\n version: string;\n accessor: Address;\n /** bytes32 per-event replay nonce; the contract pins it. */\n recordId: Hex;\n signature: Hex;\n}\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n v: 1;\n jobId: string;\n owner: Address;\n builder: Address;\n builderPublicKey: Hex;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n request: JobRequest;\n /**\n * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n * `uri` = `/v1/jobs/execute`,\n * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n */\n auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n owner: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n idempotencyKey: string;\n /** Client UUID, echoed in `JobRequest.jobId`. */\n jobId: string;\n deadline?: string;\n /** Base64 ECIES from `sealJobRequest`. */\n requestCiphertext: string;\n /**\n * The most the builder accepts for this read, uint256 decimal. The Gateway\n * refuses a quote above it rather than charging a price nobody agreed to.\n *\n * @remarks\n * This submission is authenticated by a Web3Signed body hash, not by an\n * EIP-712 struct, so these three fields are covered by the builder's existing\n * request signature. No typed-data version changes.\n */\n maxPrice?: string;\n priceAsset?: Address;\n payment?: JobPaymentAuthorization;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n objectKey: string;\n /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n url: string;\n /** Byte length of the sealed object. */\n size: number;\n /** sha256 of the sealed bytes, 0x-prefixed. */\n hash: Hex;\n /** Logical expiry. After this the Gateway stops serving the handle. */\n expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n jobId: string;\n state: JobState;\n operation: JobOperation;\n owner: Address;\n grantId: Hex;\n scope: string;\n pinnedVersion: string | null;\n attempt: number;\n price: string;\n /**\n * Null exactly when the read is free; nothing owed is owed to nobody.\n * Optional so a consumer's existing `JobStatus` fixtures keep compiling; the\n * Gateway always sends it.\n */\n priceAsset?: Address | null;\n payer: \"builder\";\n paymentState: PaymentState;\n createdAt: string;\n claimedAt: string | null;\n completedAt: string | null;\n failureReason: string | null;\n /** Present only when `state === \"completed\"`. */\n result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n leaseSeconds?: number;\n capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n job: {\n jobId: string;\n owner: Address;\n builder: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n requestCiphertext: string;\n attempt: number;\n deadlineAt: string | null;\n claimExpiresAt: string;\n fencingToken: number; /* = attempt; every node write echoes it */\n };\n identity: {\n userPsId: Hex;\n epoch: number;\n enclaveAddress: Address;\n enclavePublicKey: Hex;\n sealedEnvelope: SealedEnvelope;\n };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n leaseSeconds?: number;\n fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n fencingToken: number;\n resultHash: Hex;\n resultSize: number;\n /** `jobresults/{chainId}/{jobId}`. */\n resultObjectKey: string;\n /**\n * Server-signed delivery receipt for the read this job served, minted by the\n * node agent with the owner's enclave wallet. Binds the reserved payment to\n * an on-chain `recordDataAccess`, exactly as the legacy paid read does.\n * Required whenever the job reserved a fee.\n */\n accessRecord?: JobAccessRecord;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n fencingToken: number;\n reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n success: true;\n jobId: string;\n state: JobState;\n claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n v: 1;\n jobId: string;\n scope: string;\n version: string | null;\n contentType: string;\n /** Raw result bytes; callers decode text explicitly when appropriate. */\n body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n capacity: number;\n secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n composeHash: Hex;\n instanceId: string;\n activeSandboxes: number;\n capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n state: TeeNodeState;\n capacity: number;\n activeSandboxes: number;\n lastHeartbeatAt: string | null;\n}\n"],"mappings":"AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA2DO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendatalabs/vana-sdk",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "A TypeScript library for interacting with Vana Network smart contracts.",
5
5
  "publishConfig": {
6
6
  "access": "public"