@rankigi/sdk
v4.1.1
Published
Cryptographic proof layer SDK for autonomous AI agents.
Downloads
57
Readme
@rankigi/sdk
Cryptographic audit trails for autonomous AI agents. Node.js SDK (v4).
Every event is signed with your agent's Ed25519 passport and SHA-256 chained to the previous event before the server persists it.
Install
npm install @rankigi/sdkEnroll
npx @rankigi/cli initThis writes RANKIGI_CREDENTIAL and, when discoverable, RANKIGI_CHAIN_ID to .env.
Use
import Anthropic from "@anthropic-ai/sdk";
import { Rankigi } from "@rankigi/sdk";
const rk = Rankigi.fromEnv();
const claude = new Anthropic();
const reply = await rk.wrapAnthropic(
"contract-review",
{ prompt: "Summarize this clause." },
() =>
claude.messages.create({
model: "claude-sonnet-4-5",
max_tokens: 512,
messages: [{ role: "user", content: "Summarize this clause." }],
}),
);
console.log(reply.content);
await rk.close();Rankigi.fromEnv() reads RANKIGI_CREDENTIAL (and the optional RANKIGI_CHAIN_ID) from the environment. wrapAnthropic(label, args, fn) runs the thunk, captures the response, and seals a v4-bound event on your chain.
HIPAA / PHI events
If your organization has HIPAA mode enabled, every event must declare a PHI class and PHI-flagged events must reference a customer-held KMS key. Plaintext PHI is never accepted on the wire.
Setup:
- Request a Business Associate Agreement at
https://rankigi.com/dashboard/settings/compliance. RANKIGI countersigns within 2 business days. - Until the BAA is executed, ingest on a HIPAA-mode org returns
403 BAA_REQUIRED. - Once executed, PHI events are accepted only when both
phi_class: "phi"andpayload_kms_refare set.
await rk.track("patient_record_accessed", {
payload: {
// Reference data only -- the PHI itself is encrypted under your KEK and
// referenced via payload_kms_ref. RANKIGI stores the canonical hash of
// this object plus the KMS reference, never the plaintext PHI.
record_ref: "kms:patient-record:abc123",
},
phi_class: "phi",
payload_kms_ref: "arn:aws:kms:us-east-1:000000000000:key/your-cmk-id",
});
// Non-PHI events on a HIPAA-mode org omit phi_class (defaults to "none").
await rk.track("policy_evaluated", { payload: { rule: "off-hours" } });Server responses you should handle:
403 BAA_REQUIRED-- the org's BAA is not executed. Stop emitting PHI until status changes.422 PHI_CLASS_REQUIRED-- the payload contains an obvious PHI field butphi_classwas not declared. Set it explicitly.422 PHI_PAYLOAD_MUST_BE_ENCRYPTED--phi_class: "phi"requirespayload_kms_ref.
Every PHI ingest decision (allow or block) is recorded in baa_audit_log for SOC 2 / HIPAA evidence.
Durability and error handling (4.1.0)
Every track() call writes to an append-only JSONL spool on disk before the network attempt. The spool lives at:
${os.tmpdir()}/rankigi-queue/<sanitized-agent-id>/queue.jsonlOn 2xx the event is removed. On 4xx (401, 403, 413) the event is marked poisoned, surfaced via onError, and removed (no retries — these will never succeed). On 5xx, 429, and network failure the event is retried up to 3 times in-process with exponential backoff + cryptographic jitter (crypto.randomInt), then left on the queue. The next SDK instance with the same agentId replays the queue on startup.
429 Retry-After is honored: the first retry waits exactly the server's hint, subsequent retries exponentially back off.
const rk = new Rankigi({
apiKey: process.env.RANKIGI_API_KEY!,
agentId: process.env.RANKIGI_AGENT_ID!,
passportId: process.env.RANKIGI_PASSPORT_ID!,
signingPrivateKey: process.env.RANKIGI_SIGNING_KEY!,
onError: (err, event) => {
// Called for poisoned events, queue-full overflow, and after
// immediate retries are exhausted (event remains on disk for replay).
metrics.increment("rankigi.event_drop", { code: (err as any).code });
},
maxQueueSize: 1024, // Default. Beyond this, onError + drop.
flushTimeoutMs: 5000, // SIGTERM/SIGINT drain budget. Default 5s.
});The SDK registers process.once('SIGTERM' | 'SIGINT' | 'beforeExit') handlers that drain the queue with the configured timeout. Pass disableExitHandlers: true to opt out (containerized lifecycles that manage their own shutdown).
Universal crossing (intercept)
rk.intercept() wraps globalThis.fetch and mirrors every outbound HTTP call into the RANKIGI universal-crossing layer.
node:http patching is opt-in (default false). Patching node:http.request is incompatible with Datadog dd-trace, Sentry, NewRelic, OpenTelemetry HTTP instrumentation, and other libraries that also patch http.request. The fetch wrap is safe alongside all of these.
const stop = rk.intercept({
patchFetch: true, // default
patchNodeHttp: false, // default — only enable if no APM is present
});
// ... later
stop();The install heartbeat never throws out of the IIFE; 401 and network failures route through reportMirrorError (and your optional onMirrorError callback). Agent execution continues even if RANKIGI is unreachable at startup.
Migration from @rankigi/[email protected] (legacy)
The 1.0.0 line lived in packages/sdk-node and is now @rankigi/sdk-legacy (private). The canonical SDK is the v4 tree, published as @rankigi/[email protected].
| 1.0.0 | 4.1.0 |
|---|---|
| Optional signingKey (PKCS8 PEM) | Required signingPrivateKey (base64 raw 32-byte Ed25519 seed) |
| onError(err) | onError(err, event) — second arg includes id, action, agentId |
| In-memory promise array | Append-only JSONL spool, survives process restart |
| 4xx retried then dropped | 4xx marked poisoned, surfaced to onError, removed |
| intercept() not present | rk.intercept(opts) with opt-in node:http patching |
| No exit handlers | SIGTERM/SIGINT/beforeExit drain with flushTimeoutMs |
Common patterns
Wrap any AI call.
const result = await rk.wrap("my-task", { input: "data" }, () => myAIFunction());Group events by session.
await rk.withSession("session-id", async () => {
await rk.wrap("step-one", { ... }, () => stepOne());
});Enable debug mode.
const rk = new Rankigi({
apiKey: process.env.RANKIGI_API_KEY!,
agentId: process.env.RANKIGI_AGENT_ID!,
passportId: process.env.RANKIGI_PASSPORT_ID!,
signingPrivateKey: process.env.RANKIGI_SIGNING_KEY!,
debug: true,
});Or set RANKIGI_DEBUG=true in the environment and use Rankigi.fromEnv().
LangChain
import { Rankigi } from "@rankigi/sdk";
import { RankigiCallbackHandler } from "@rankigi/sdk/langchain";
const rk = Rankigi.fromEnv();
const callbacks = [new RankigiCallbackHandler({ client: rk, sessionId: "research-agent" })];Verify
Export a chain bundle from the dashboard and run python3 public/verify.py bundle.json to recompute hashes and check every signature offline.
Links
License
Proprietary. (C) RANKIGI Inc.
