Skip to content

TypeScript SDK

The TypeScript SDK is byte-for-byte interoperable with Go, Python, and Rust. It runs in Node 20+ and in modern browsers (the cryptographic primitives use audited pure-JS implementations from the Noble suite — no native dependencies).

npm publish is queued behind @identities-ai org approval. For now, install from source:

Terminal window
git clone https://github.com/identities-ai/ratify-protocol
cd ratify-protocol/sdks/typescript
npm install

Once the org is approved, this becomes:

Terminal window
npm install @identities-ai/ratify-protocol
import {
generateHumanRoot,
generateAgent,
issueDelegation,
signChallenge,
generateChallenge,
verifyBundle,
PROTOCOL_VERSION,
SCOPE_MEETING_ATTEND,
SCOPE_MEETING_SPEAK,
type DelegationCert,
type ProofBundle,
} from "@identities-ai/ratify-protocol";
// 1. Alice generates her hybrid root identity.
const { root: alice, privateKey: alicePriv } = await generateHumanRoot();
// 2. Her AI agent generates its own hybrid keypair.
const { agent, privateKey: agentPriv } = await generateAgent(
"Alice's Scheduler",
"custom"
);
// 3. Alice signs a delegation cert.
const cert: DelegationCert = {
cert_id: crypto.randomUUID(),
version: PROTOCOL_VERSION,
issuer_id: alice.id,
issuer_pub_key: alice.public_key,
subject_id: agent.id,
subject_pub_key: agent.public_key,
scope: [SCOPE_MEETING_ATTEND, SCOPE_MEETING_SPEAK],
issued_at: Math.floor(Date.now() / 1000),
expires_at: Math.floor(Date.now() / 1000) + 7 * 24 * 3600,
signature: { ed25519: new Uint8Array(), ml_dsa_65: new Uint8Array() },
};
await issueDelegation(cert, alicePriv);
// 4. Verifier issues a challenge. Agent signs it.
const challenge = generateChallenge(); // 32 random bytes
const challengeAt = Math.floor(Date.now() / 1000);
const challengeSig = await signChallenge(challenge, challengeAt, agentPriv);
// 5. Agent assembles a proof bundle.
const bundle: ProofBundle = {
agent_id: agent.id,
agent_pub_key: agent.public_key,
delegations: [cert],
challenge,
challenge_at: challengeAt,
challenge_sig: challengeSig,
};
// 6. Verifier runs the verifier.
const result = await verifyBundle(bundle, {
required_scope: SCOPE_MEETING_ATTEND,
});
if (result.valid) {
console.log("✓ Authorized:", result.agent_id, "granted:", result.granted_scope);
} else {
console.log("✗ Rejected:", result.identity_status, "", result.error_reason);
}

The same SDK works in browsers. The Noble-suite primitives use crypto.subtle where available and fall back to pure-JS implementations elsewhere. No native binary, no WebAssembly required.

// In a Vite / Next.js / SvelteKit / etc. browser bundle
import { verifyBundle } from "@identities-ai/ratify-protocol";
// Bundle arrives from your server as JSON
const result = await verifyBundle(bundle, {
required_scope: "api:read",
});

The bundle size for verifyBundle is about 48 KB minified + gzipped (most of which is the ML-DSA-65 verifier — Ed25519 alone would be much smaller).

const result = await verifyBundle(bundle, {
required_scope: "meeting:attend", // What action is being attempted?
required_custom_scope: undefined, // Optional custom: scope
now_override: undefined, // Use real time (test only)
revocation_list: revList, // Optional signed revocation list
});

The verifier is fail-closed. Branch on result.identity_status:

import {
IDENTITY_STATUS_VALID,
IDENTITY_STATUS_BAD_SIGNATURE,
IDENTITY_STATUS_EXPIRED,
IDENTITY_STATUS_SCOPE_DENIED,
IDENTITY_STATUS_REVOKED,
IDENTITY_STATUS_REPLAY,
} from "@identities-ai/ratify-protocol";
switch (result.identity_status) {
case IDENTITY_STATUS_VALID:
return acceptRequest();
case IDENTITY_STATUS_BAD_SIGNATURE:
return reject("tampered");
case IDENTITY_STATUS_EXPIRED:
return reject("expired");
case IDENTITY_STATUS_SCOPE_DENIED:
return reject("insufficient scope");
case IDENTITY_STATUS_REVOKED:
return reject("revoked");
case IDENTITY_STATUS_REPLAY:
return reject("challenge too old");
}

The SDK exports full TypeScript types for every protocol object:

import type {
DelegationCert,
ProofBundle,
HybridSignature,
HybridPublicKey,
HybridPrivateKey,
HumanRoot,
AgentIdentity,
VerifyResult,
VerifyOptions,
Scope,
CustomScope,
GeoConstraint,
TemporalConstraint,
VersionConstraint,
RevocationList,
} from "@identities-ai/ratify-protocol";

Scope is a string-literal union of the 52 canonical scopes; the compiler catches typos before they reach the verifier.

Terminal window
cd sdks/typescript
npm run test:conformance

You should see 59 fixtures pass. If any fail, the bytes have drifted from the reference and the SDK is not interoperable — file a bug.