@nodatachat/sdk
v2.0.0
Published
NoData SDK for the Information Access Processor: one call decides whether an identity may access information and returns a signed proof.
Downloads
183
Maintainers
Readme
@nodatachat/sdk
The developer SDK for NoData, the Information Access Processor.
Give AI access without giving it the data. One API. One decision. One proof.
The primitive is a single call — compute(information, intent, clearance) → a decision with an authorized view and a signed proof. The data the AI isn't cleared for never reaches the model. You pay per decision, and every decision is the proof.
Everything else in this SDK — capsules, documents, scan, engines — is an adapter that resolves to that same decision.
Install
npm install @nodatachat/sdkZero dependencies. Works in Node.js, Bun, Deno, Cloudflare Workers, and any runtime with fetch. Get a key at nodatacapsule.com/capsule-api/register.
The primitive — compute()
import { NoData } from '@nodatachat/sdk';
const nd = new NoData({ apiKey: 'ndp_...' });
const { answer, sections, proof } = await nd.compute({
question: 'What is the claim status?',
clearance: 'internal',
sections: [
{ title: 'Status', text: 'Claim status: approved. Region: Tel Aviv.' },
{ title: 'Client', text: 'Dana Cohen, ID 039821582, amount 42,500 NIS.' },
],
});
// "Client" auto-classifies as confidential and is WITHHELD at internal clearance —
// it never reaches the model. `answer` comes only from "Status".
// sections → [{ title: 'Status', sent: true }, { title: 'Client', sent: false }]
const v = await nd.verify(proof.id); // → { authentic: true, unaltered: true, ... }Wire it to any model runtime — the pattern is always the same: NoData decides + proves; your model only ever sees the authorized slice.
// OpenAI / Anthropic / Gemini / LangChain / n8n — same shape:
const gate = await nd.compute({ question, clearance, sections });
if (gate.decision === 'deny') return 'Not authorized.';
const reply = gate.answer; // already computed over the authorized slice
// attach gate.proof.id to your response as the tamper-evident receipt.Adapter — a customer capsule in a few calls
The capsule is one adapter over the primitive: an isolated, content-blind workspace per customer; every read is a governed decision with a receipt.
import { NoData } from '@nodatachat/sdk';
const nd = new NoData({ apiKey: 'ndp_...' });
// Mint a customer capsule (isolated, content-blind)
const cap = await nd.capsules.create({ handle: 'case-4471', display_name: 'Yossi Levy' });
// Seal a document in — a key per recipient
await cap.documents.add({
title: 'Policy',
sections: [
{ label: 'Summary', classification: 'public', text: 'Public summary.' },
{ label: 'Terms', classification: 'confidential', text: 'Confidential terms.' },
],
recipients: [{ email: '[email protected]', classifications: ['public'], code: 'blue-harbor' }],
});
// Grant a member access across the docs
await cap.members.add({ email: '[email protected]', classifications: ['public'], code: 'blue-harbor' });
// Signed audit ledger — who opened, when, on which device
const ledger = await cap.audit.get();
// Kill-switch: blocks the key capsule-wide — every live link 410s at once.
// Reversible (a paused capsule can be restored); use burn to crypto-shred the bytes.
await cap.revoke({ reason: 'offboarded' });The server stores ciphertext only — never your section plaintext or the key.
AI governance — let an agent read only what you allow
Grant a named agent scoped classifications; on read it gets back only those — denied sections never decrypt (deny-by-default), and every read is audited as an AI access.
const g = await cap.ai.grant({ agent_name: 'claims-bot', classifications: ['public'] });
const read = await cap.ai.read({ agent_id: g.agent_id });
// read.documents[].sections → 'public' only; 'confidential' is absent, never decrypted
await cap.ai.revoke(g.agent_id);Embedded Capsule — the white-label widget
Mint an origin-bound token for one of the capsule's documents and frame the viewer on your own site. The raw bearer never leaves NoData's origin — the token encrypts it.
const { embed_url } = await cap.embed({
bearer: '<document bearer from documents.add>',
allowed_origin: 'https://portal.myfirm.com',
ttl_seconds: 3600,
});
// <iframe src={embed_url} />Tenants — provision a whole organization
const t = await nd.tenants.create({ company_name: 'Acme', slug: 'acme', capsule_install: true });
const state = await nd.tenants.get(t.tenant_id);Requires a key with the partner.provision_tenants scope.
Fleet overview — you manage a fleet, not a capsule
const fleet = await nd.capsules.overview();
// { fleet: { active_vaults, total_documents, ... }, ai_governance: { agents, grants_active } }Capsule API reference
| Method | Endpoint | What it does |
|--------|----------|--------------|
| nd.capsules.create(req) | POST /v1/capsules | Mint a customer capsule |
| nd.capsules.list(opts?) | GET /v1/capsules | List this org's capsules |
| nd.capsules.get(idOrSlug) | GET /v1/capsules/{id} | Capsule summary |
| cap.documents.add(req) | POST /v1/capsules/{id}/documents | Seal a document in |
| cap.documents.list() | GET /v1/capsules/{id}/documents | List documents (metadata) |
| cap.members.add(req) | POST /v1/capsules/{id}/members | Grant a member access |
| cap.members.list() | GET /v1/capsules/{id}/members | The member roster |
| cap.ai.grant(req) | POST /v1/capsules/{id}/ai-grants | Grant an AI agent scoped classifications |
| cap.ai.list() | GET /v1/capsules/{id}/ai-grants | List grants (allowed vs denied) |
| cap.ai.read(req) | POST /v1/capsules/{id}/ai-read | Agent reads only its allowed classifications |
| cap.ai.revoke(id) | POST /v1/capsules/{id}/ai-grants | Revoke an agent |
| cap.audit.get(opts?) | GET /v1/capsules/{id}/audit | Signed audit ledger |
| cap.embed(req) | POST /v1/capsules/{id}/embed-token | Origin-bound embed token |
| cap.revoke(opts?) | POST /v1/capsules/{id}/revoke | Kill-switch — every live link 410s at once (reversible) |
| nd.tenants.create(req) | POST /v1/tenants | Provision a tenant |
| nd.tenants.get(id) | GET /v1/tenants/{id} | Read tenant state |
| nd.capsules.overview() | GET /v1/overview | Fleet totals |
Configuration
const nd = new NoData({
apiKey: 'ndp_...',
baseUrl: 'https://www.nodatacapsule.com', // default
timeout: 30000, // ms
retries: 2, // on 5xx / network error
headers: { 'X-Custom': 'value' },
onRequest: (url, init) => console.log(`→ ${url}`),
onResponse: (url, status, ms) => console.log(`← ${status} ${ms}ms`),
onError: (url, err) => console.error(`! ${url}: ${err.message}`),
});Error handling
import { NoData, NoDataError } from '@nodatachat/sdk';
try {
await nd.capsules.create({ handle: 'case-4471' });
} catch (err) {
if (err instanceof NoDataError) {
console.log(err.status, err.message, err.retryAfter);
}
}Rate limits are handled automatically — the SDK waits and retries on 429.
Platform Engines — nd.engines.*
The shared machinery beneath the capsule (and beneath File / Code / AI / Data Capsule). These are the engines, not the pitch — the way Stripe doesn't put its internal ledger in the hero. Reach for them directly only when you need the raw primitive.
// Blind Relay — field-level encrypt/decrypt (server stores nothing)
const { ciphertext } = await nd.engines.blindRelay.encrypt({ field: 'credit_card', value: '4111...' });
// Governance — register data + grant/revoke agents (runs on Excel/DB/CRM too)
await nd.engines.governance.grant(/* ... */);
// Channel — system-to-system encrypted transfer
const ch = await nd.engines.channel.create({ ttl: '1h' });
// Delivery — burn-after-read
await nd.engines.deliver.send({ content: 'API_KEY=...', burn: true });
// Evidence — audit trail + proofs
await nd.engines.evidence.query({ field: 'credit_card' });
// Vault — zero-knowledge blob storage · Webhooks — encrypted at rest
await nd.engines.vault.create({ /* ... */ });
await nd.engines.webhook.create({ label: 'stripe' });| Engine | Namespace | Purpose |
|--------|-----------|---------|
| Governance | nd.engines.governance.* | Data permissions — register, grant, revoke (product-independent) |
| Blind Relay | nd.engines.blindRelay.* | encrypt / decrypt / batch — field-level, server-blind |
| Channel | nd.engines.channel.* | Encrypted system-to-system transfer |
| Delivery | nd.engines.deliver.* | Burn-after-read secret delivery |
| Evidence | nd.engines.evidence.* | Audit trail + cryptographic proofs |
| Vault | nd.engines.vault.* | Zero-knowledge blob storage |
| Webhooks | nd.engines.webhook.* | Receive webhooks, encrypted at rest |
The old top-level accessors (
nd.encrypt,nd.channel,nd.governance, …) still work but are deprecated — usend.engines.*.
Framework plugins are available: @nodatachat/sdk/express and @nodatachat/sdk/fastify.
Architecture
one capsule per customer
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ Your App │ ─────→ │ NoData API │ ─────→ │ Your world │
│ plaintext │ │ ciphertext │ │ ciphertext │
│ lives here │ │ content- │ │ + receipts │
│ │ │ blind · 0 │ │ │
└─────────────┘ └──────────────┘ └─────────────┘
Capsule = the entity · Engines = the machinery underneathContent-blind by design — not by policy, by architecture. We store ciphertext; never your data, never the key. A zero-knowledge capsule is sealed to a key only your customer holds, so we can never unwrap it.
Get your API key
NoData on npm
@nodatachat/nodata: the main package, with all protection, scanning and governance capabilities, from the terminal.@nodatachat/sdk(this one): integration for developers.@nodatachat/mcp: integration for AI agents and MCP clients.
License
FSL-1.1-ALv2 (Functional Source License 1.1, Apache 2.0 future license). Use it for any purpose except a competing product or service. Each release becomes available under the Apache License 2.0 two years after it is published. Version 1.0.0 was published under MIT and remains available under that license. Copyright 2026 Capsule Ltd.
