TypeScript SDK reference¶
Audience: TypeScript / JavaScript engineers integrating Nanorix into a Node.js backend or modern bundler-built browser app. Package:
@nanorix/sdk(v0.5.0). The 1.0 contract freeze is planned; the current surface is stable and additive. Node: 18+ · TypeScript: 5.x · ES Modules + CommonJS dual export. Substrate framing. The SDK wraps the HTTP API documented in api-reference.md. Surface is byte-symmetric with the Python SDK: every method has the same name (camelCase instead of snake_case), the same arguments, the same return shape, the same exception hierarchy.
Install¶
Or from source: github.com/nanorix-io/nanorix-sdk.
Dependencies: zero runtime deps for crypto (uses Node crypto builtins for HMAC, signatures, hashing).
Top-level surface¶
import { Client, AsyncClient } from "@nanorix/sdk";
// Construction
const client = new Client({ apiKey: "nrx_live_..." });
const client2 = new Client(); // auto-loads NANORIX_API_KEY
const client3 = new Client({ baseUrl: "https://nanorix.io",
timeout: 30000, maxRetries: 4 });
// Resources
client.capsulefiles // Capsulefile lifecycle
client.capsules // capsule lifecycle + .run(), .batch(), .session()
client.cdp // AuditProof retrieval (alias: client.auditProofs)
client.auditProofs // alias of .cdp
client.egressProfiles // named egress allowlists
client.usage // usage summary, line-items, CSV export
client.webhooks // webhook endpoints + deliveries
// Subpackage namespaces
import { verifyAuditProof, type VerifierPolicy } from "@nanorix/sdk";
import { Webhook } from "@nanorix/sdk";
import { HIPAAContext, GDPRContext, SOC2Context } from "@nanorix/sdk";
// Top-level
await client.health(); // GET /v1/health
client.close(); // release HTTP transport
The SDK is async-first; every method returns a Promise. There is no separate AsyncClient because Node-side TypeScript is async by default. The AsyncClient symbol is exported as an alias to Client for symmetry with the Python SDK.
Exceptions¶
Full reference: error catalog.
import {
NanorixError, // base
AuthenticationError, // 401
NotFoundError, // 404
ConflictError, // 409
BadRequestError, // 400
QuotaExceededError, // 402, 403 (quota)
PayloadTooLargeError, // 413
GoneError, // 410 (capsule destroyed)
RateLimitError, // 429 (after auto-retry budget exhausted)
UnprocessableEntityError, // 422 default
InputProvenanceRejectedError, // 422 input_provenance_rejected
SecretsInjectionRejectedError, // 422 secrets_injection_rejected
ExecutionError, // non-zero exit; .cdp attached if destroyed
CeilingError, // safety-ceiling destroy; .cdp attached
TimeoutError, // exec timeout; .capsuleId attached
} from "@nanorix/sdk";
Every exception carries statusCode?: number and body?: Record<string, unknown>. The body.error.request_id field is the support-ticket correlation id; quote it when filing.
client.capsulefiles¶
Class: Capsulefiles.
create(opts)¶
async create(opts: {
name: string;
runtime: string;
entrypoint: string;
files?: string[] | Record<string, Buffer>;
dataClassification?: "general" | "PHI" | "PII" | "financial" | "credentials";
egressDeclaration?: string[];
defaultTtl?: number;
memoryMb?: number;
envVars?: Record<string, string>;
packages?: Record<string, string[]>;
finalize?: boolean; // default true
}): Promise<Capsulefile>
Maps to POST /v1/capsulefiles (+ POST /:id/files + POST /:id/finalize).
Example:
const cf = await client.capsulefiles.create({
name: "med-ai-extraction",
runtime: "python3.12",
entrypoint: "python main.py",
files: ["main.py"],
dataClassification: "PHI",
egressDeclaration: ["api.anthropic.com"],
defaultTtl: 1800,
memoryMb: 2048,
envVars: { ANTHROPIC_MODEL: "claude-3-5-sonnet-latest" },
packages: { python: ["anthropic==0.39.0", "pypdf==5.0.1"] },
});
console.log(cf.id, cf.contentHash);
list(params) / get(id) / delete(id)¶
async list(params?: Record<string, string>): Promise<CapsulefileList>
async get(capsulefileId: string): Promise<Capsulefile>
async delete(capsulefileId: string): Promise<void>
update(id, fields)¶
waitUntilFinalized(id, opts)¶
Polls until the Capsulefile transitions from draft to finalized.
async waitUntilFinalized(id: string, opts?: { timeoutMs?: number; pollIntervalMs?: number }): Promise<Capsulefile>
uploadDir(id, dirPath)¶
Uploads every file under dirPath recursively. Returns the file count.
client.capsules¶
Class: Capsules.
create(opts)¶
async create(opts?: {
capsulefile?: string;
maxLifetimeSeconds?: number;
egressProfile?: string;
metadata?: Record<string, string>;
}): Promise<LiveCapsule>
Maps to POST /v1/capsules. metadata binds customer-declared context (16 keys max, 64-char keys, 256-char values, 4 KB total; nanorix:* prefix reserved).
run(opts)¶
async run(opts: {
capsulefile: string;
inputs?: Record<string, Buffer>;
command?: string;
timeout?: number;
maxLifetimeSeconds?: number;
metadata?: Record<string, string>;
}): Promise<{ outputs: Record<string, Buffer>; cdp: CdpAccess; result: ExecutionResult }>
One-shot lifecycle: create → upload → execute → download → destroy.
Example (Pattern 3, single-shot extraction):
import * as fs from "node:fs";
const { outputs, cdp, result } = await client.capsules.run({
capsulefile: "cf_018f...",
inputs: { "input.pdf": fs.readFileSync("test.pdf") },
metadata: { matter_id: "ACME-001" },
});
console.log(result.stdout.toString().slice(0, 200));
console.log("AuditProof id:", cdp.full().id);
console.log("final_hash:", cdp.full().finalHash);
batch(opts)¶
Anthropic-Batches-shape batch processing with RFC 6962 Merkle-root client-side (byte-equivalent to Rust runtime + Python SDK).
async batch(opts: {
capsulefile: string;
records: Iterable<Record<string, unknown>>;
onRecord?: (record: BatchRecord) => void;
recordIdField?: string; // default "custom_id"
onRecordError?: "fail_isolated" | "abort_batch"; // default "fail_isolated"
maxLifetimeSeconds?: number;
metadata?: Record<string, string>;
timeout?: number;
}): Promise<BatchResult>
BatchResult carries merkleRoot, auditProof, records, successCount, errorCount. Customer entrypoint reads /data/input/records.jsonl and writes /data/output/results.jsonl per the same wire shape as the Python SDK.
session(opts)¶
LangGraph-shape stateful session-mode for multi-capsule reasoning chains. Customer-managed encrypted state continuity via parentCdpId.
async session(opts: {
capsulefile: string;
sessionId: string;
parentCdpId?: string;
initialState?: Buffer;
maxLifetimeSeconds?: number;
metadata?: Record<string, string>;
timeout?: number;
}): Promise<SessionResult>
list(params) / get(id)¶
Standard list/get.
LiveCapsule workspace methods¶
LiveCapsule is the handle returned by create(). Methods:
| Method | Maps to |
|---|---|
.id (property) |
capsule id |
.status (property) |
current status |
.upload(filename, data) |
POST /v1/capsules/:id/upload?filename=... |
.execute({ command?, inputData?, timeout? }) |
POST /v1/capsules/:id/exec |
.listOutputs() |
GET /v1/capsules/:id/output |
.download(filename) |
GET /v1/capsules/:id/output/:filename |
.destroy() → CdpAccess |
DELETE /v1/capsules/:id |
.refresh() |
GET /v1/capsules/:id |
Workspace-mode example:
const capsule = await client.capsules.create({ capsulefile: "cf_018f..." });
await capsule.upload("input.pdf", pdfBytes);
const result = await capsule.execute({ command: "python main.py", timeout: 60 });
console.log(result.stdout);
// Iterate without re-uploading
const result2 = await capsule.execute({ command: "python main.py --verbose" });
// Destroy and get the AuditProof
const cdp = await capsule.destroy();
console.log(cdp.full().finalHash);
client.cdp (alias: client.auditProofs)¶
Class: CdpResource.
async get(capsuleId: string): Promise<CdpAccess>
async verify(capsuleId: string): Promise<AuditProof> // the verification proof object
get() retrieves the AuditRecord (GET /v1/capsules/:id/cdp); verify() retrieves the shareable AuditProof projection (GET /v1/capsules/:id/cdp/verify).
CdpAccess exposes:
.full()→CdpDocument— the AuditRecord (private complete record)..verification()→AuditProof— the shareable verification proof object..id,.capsuleId,.finalHash,.createdAt— top-level fields.
client.auditProofs is the brand-aligned alias; both names point at the same resource and coexist forever.
client.egressProfiles¶
Class: EgressProfiles. Tier: Team+.
client.egressProfiles.create({ name, description })
client.egressProfiles.list()
client.egressProfiles.get(profileId)
client.egressProfiles.update(profileId, { name?, description? })
client.egressProfiles.delete(profileId)
client.egressProfiles.addRule(profileId, { kind: "domain", value: "api.example.com", port: 443 })
client.egressProfiles.listRules(profileId)
client.egressProfiles.removeRule(profileId, ruleId)
Maps to /v1/egress/profiles/....
kind enum: "domain", "ip_v4", "ip_v6", "cidr_v4", "cidr_v6".
client.webhooks¶
Class: Webhooks.
client.webhooks.create({ url, events, description? })
client.webhooks.list()
client.webhooks.get(webhookId)
client.webhooks.update(webhookId, { url?, events?, description?, active? })
client.webhooks.delete(webhookId)
client.webhooks.test(webhookId)
client.webhooks.deliveries(webhookId)
The signingSecret returned from create() is shown once. Use Webhook.verifySignature() in your handler:
import { Webhook } from "@nanorix/sdk";
if (!Webhook.verifySignature({
payload: req.body,
signatureHeader: req.headers["nanorix-webhook-signature"],
signingSecret: process.env.NANORIX_WEBHOOK_SECRET!,
})) {
res.status(401).end();
return;
}
Maps to /v1/webhooks/.... Full event-type catalog at integrations/webhooks.md.
client.usage¶
Class: Usage.
async summary(billingCycle?: string): Promise<UsageSummary>
async estimate(): Promise<UsageEstimate>
async listItems(opts?: ListItemsOptions): AsyncIterable<UsageItem>
async exportCsv(opts?: ListItemsOptions): Promise<Buffer>
listItems() returns an AsyncIterable paginated automatically. exportCsv() returns the raw CSV bytes — same shape as the API's /v1/usage/items.csv.
Maps to /v1/usage/....
Verifier subpackage¶
import { verifyAuditProof, type AuditProofVerificationResult, type VerifierPolicy } from "@nanorix/sdk";
import * as fs from "node:fs";
const proof = JSON.parse(fs.readFileSync("auditproof.json", "utf8"));
const result: AuditProofVerificationResult = verifyAuditProof(proof, {
rejectDiagnostic: true,
requiredRegion: "us-central1",
requiredAuthorityId: undefined,
});
if (!result.valid) {
console.log("failure:", result.failureReason?.type);
console.log("stage_reached:", result.stageReached);
}
Cross-impl byte-equivalence: verifyAuditProof() produces output byte-equivalent to the Rust nanorix-verify, Go auditproof-verifier-go, and Python SDK verify_auditproof() on the 100-fixture reference corpus. The FailureReason discriminated union is identical across all five paths (Rust, Go, Python, TypeScript, server-side POST /v1/verify).
The failure-reason enum is closed-set and byte-equivalent across all verifier implementations.
Compliance helpers¶
Framework-typed metadata builders that produce Record<string, string> payloads for the metadata argument above. Bind customer-declared context that lands in the AuditProof's metadata_hash.
import { HIPAAContext, GDPRContext, SOC2Context } from "@nanorix/sdk";
const metadata = HIPAAContext({
coveredEntityId: "ce-001",
businessAssociateId: "ba-002",
treatmentPurpose: true,
}).toMetadata();
const { outputs, cdp } = await client.capsules.run({
capsulefile: "cf_...",
inputs: { "input.pdf": pdfBytes },
metadata,
});
The metadata is customer-defined; the compliance helpers are Nanorix-published convenience builders, not Nanorix-imposed schemas.
End-to-end mapping to the API surface¶
Every TypeScript SDK method maps to one or more API endpoints documented in api-reference.md:
| TypeScript method | API endpoint |
|---|---|
Client.health() |
GET /v1/health |
client.capsulefiles.create() |
POST /v1/capsulefiles (+ files + finalize) |
client.capsulefiles.list() |
GET /v1/capsulefiles |
client.capsulefiles.get(id) |
GET /v1/capsulefiles/:id |
client.capsulefiles.delete(id) |
DELETE /v1/capsulefiles/:id |
client.capsules.create() |
POST /v1/capsules |
LiveCapsule.upload() |
POST /v1/capsules/:id/upload |
LiveCapsule.execute() |
POST /v1/capsules/:id/exec |
LiveCapsule.listOutputs() |
GET /v1/capsules/:id/output |
LiveCapsule.download() |
GET /v1/capsules/:id/output/:filename |
LiveCapsule.destroy() |
DELETE /v1/capsules/:id |
client.capsules.list() |
GET /v1/capsules |
client.capsules.get(id) |
GET /v1/capsules/:id |
client.cdp.get() / client.auditProofs.get() |
GET /v1/capsules/:id/cdp |
client.cdp.verify() |
GET /v1/capsules/:id/cdp/verify |
client.egressProfiles.* |
/v1/egress/profiles/... |
client.webhooks.* |
/v1/webhooks/... |
client.usage.* |
/v1/usage/... |
verifyAuditProof() |
offline (no network) |
client.capsules.run(), .batch(), and .session() are SDK-side compositions over create + upload + exec + destroy, not separate endpoints.
Browser support¶
The SDK is Node-first; browser bundlers (Vite, esbuild, webpack) can import it but the Node crypto builtins must be polyfilled or the verifier subpackage must be excluded. The browser-native verifier ships as a separate package (forthcoming TypeScript browser verifier).
Recommended browser pattern: keep API key on a server-side proxy, expose only verifier-relevant calls to the client.
Cross-references¶
- API reference — every endpoint with full request/response schemas.
- Error catalog — every exception with cause and fix.
- Python SDK reference — mirror surface in Python.
- Webhooks — full HMAC verification recipe.
- AuditProof specification — full AuditProof / AuditRecord schema.
Forever-Standard discipline. Every public TypeScript SDK method, every option key, every exception class, every model field is permanent. New methods / options / classes / fields are additive only. The internal-vs-external naming alias (
client.cdp↔client.auditProofs) is structural, not transitional — both names exist forever.