@dispersed/sdk
v0.1.0
Published
Web-standards TypeScript SDK for the Dispersed Network compute API
Maintainers
Readme
@dispersed/sdk
TypeScript SDK for the Dispersed Network compute API. Built on Web Standards (fetch, URL, AbortSignal, crypto.subtle) - runs unchanged in Node.js, Bun, Deno, Cloudflare Workers, and modern browsers.
npm install @dispersed/sdk
# or: pnpm add @dispersed/sdkQuick start
import { DispersedClient } from "@dispersed/sdk";
const client = new DispersedClient({
publicKey: process.env.DISPERSED_PUBLIC_KEY!,
secretKey: process.env.DISPERSED_SECRET_KEY!,
});
const jobs = await client.jobs.list({ limit: 10, "filter[status]": "RUNNING" });
console.log(`${jobs.total} running jobs`);Get API keys at https://console.dispersed.com/keys.
Configuration
new DispersedClient({
publicKey: string; // required - your API public key
secretKey: string; // required - your API secret key (HMAC signing)
baseUrl?: string; // default: "https://api.dispersed.com"
timeout?: number; // default: 30_000 ms
retry?: number; // default: 2 (retries 5xx and 429 with exponential backoff)
before?: BeforeHook[]; // observability hooks run before each request
after?: AfterHook[]; // observability hooks run after each response
});Auth is HMAC-SHA256 over a canonical request string - the SDK handles signing on every request.
Resources
client.accounts; // .getMe(), .getBalance(uuid), .getSpendSummary(uuid), .getConsumerSummary(uuid)
client.jobs; // .list(params?), .get(uuid), .create(input), .cancel(uuid, input?)
client.jobRuns; // .list(params?), .get(uuid), .cancel(uuid, input?), .getCostSummary(uuid)
client.jobRecipes; // .list(params?), .get(uuid), .create(input), .update(uuid, input),
// .delete(uuid), .fork(uuid), .cook(uuid, input?), .createFromJob(jobUuid)
client.gpuRegistry; // .list(params?)
client.compute; // .rewards(params?), .ledgerAuthorizations(params?), .ledgerTransactions(params?)
client.apiKeys; // .revoke(uuid)
client.sshPublicKeys; // .list(params?), .create(input), .update(uuid, input), .delete(uuid)Method shape: positional args for IDs and required inputs, optional object for filters/params, and a trailing RequestOptions for per-call overrides.
RequestOptions - per-call overrides
Every resource method accepts an optional trailing RequestOptions argument:
interface RequestOptions {
signal?: AbortSignal; // cancellation
headers?: Record<string, string>; // additional request headers
timeout?: number; // override the client default
retry?: number; // override the client default
}Cancellation - pass any AbortSignal. The SDK composes it with its own timeout via AbortSignal.any(), so a user signal never silently disables the timeout. Aborting mid-retry-backoff stops immediately rather than waiting out the delay.
const controller = new AbortController();
setTimeout(() => controller.abort(), 5_000);
await client.jobs.create(input, { signal: controller.signal });Headers - merged into the outgoing request as a baseline. Useful for idempotency keys, request IDs, or trace context. The SDK's auth headers (X-API-Key, X-Time, X-Nonce, X-Signature) always overlay caller-supplied values, so you can't accidentally break signing.
await client.jobs.create(input, {
headers: { "X-Idempotency-Key": "create-job-2026-04-21-abc" },
});Timeout / retry - override the client defaults for a single call. Useful for slow operations or non-idempotent POSTs you don't want retried.
await client.jobs.create(input, { retry: 0 }); // never retry
await client.jobRuns.getCostSummary(uuid, { timeout: 60_000 }); // allow longerHooks
Hooks are arrays of plain functions, run for every request. Use them for cross-cutting observability (logging, correlation IDs, metrics) - not for control flow.
const client = new DispersedClient({
publicKey,
secretKey,
before: [
(req) => {
req.headers["X-Request-Id"] = crypto.randomUUID();
},
],
after: [
(res, req) => {
console.log(`${req.method} ${req.url.pathname} → ${res.status}`);
},
],
});The SDK passes a cloned Response to after-hooks, so you can read the body without consuming it for the caller.
Error handling
Non-2xx responses throw an ApiError:
import { ApiError } from "@dispersed/sdk";
try {
await client.jobs.get("missing-uuid");
} catch (err) {
if (err instanceof ApiError) {
if (err.isNotFound) {
/* 404 */
}
if (err.isAuth) {
/* 401 / 403 */
}
if (err.isRetryable) {
/* 5xx / 429 - already retried per config.retry */
}
console.error(err.code, err.detail, err.details);
}
}ApiError exposes status, code, detail, details[], timestamp, and the convenience getters isAuth, isNotFound, isRetryable.
Web Standards
The SDK depends on no HTTP library and no runtime-specific APIs. It uses:
| Concern | API |
| ------------ | ------------------------------------------------------------------------------------------------- |
| HTTP | fetch, Request, Response, Headers |
| Cancellation | AbortController, AbortSignal, AbortSignal.any(), AbortSignal.timeout() |
| URLs | URL, URLSearchParams |
| Crypto | crypto.subtle.importKey, crypto.subtle.sign, crypto.subtle.digest, crypto.getRandomValues |
| Text | TextEncoder |
The same bundle runs in Node.js (≥20), Bun, Deno, Cloudflare Workers, Vercel Edge, and modern browsers - no polyfills, no platform-specific code paths.
Browser caveat: the User-Agent header is on the Fetch spec's forbidden header list, so browsers silently drop it. The SDK always sends X-Dispersed-Client: @dispersed/sdk/<version> as well, which works everywhere - server-side analytics should key off that header.
Version
import { VERSION } from "@dispersed/sdk";A unit test asserts the exported VERSION stays in sync with package.json.
License
MIT
