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).
Install
Section titled “Install”npm publish is queued behind @identities-ai org approval. For now, install from source:
git clone https://github.com/identities-ai/ratify-protocolcd ratify-protocol/sdks/typescriptnpm installOnce the org is approved, this becomes:
npm install @identities-ai/ratify-protocolThree minutes, end to end
Section titled “Three minutes, end to end”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 bytesconst 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);}Browser usage
Section titled “Browser usage”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 bundleimport { verifyBundle } from "@identities-ai/ratify-protocol";
// Bundle arrives from your server as JSONconst 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).
Verifier options
Section titled “Verifier options”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");}Type safety
Section titled “Type safety”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.
Conformance
Section titled “Conformance”cd sdks/typescriptnpm run test:conformanceYou should see 59 fixtures pass. If any fail, the bytes have drifted from the reference and the SDK is not interoperable — file a bug.
Where to next
Section titled “Where to next”- Protocol concepts — what the bytes actually mean
- Scopes — the canonical 52-scope vocabulary
- Integration guides — wiring the SDK into a real surface