@custodex/sdk
v0.2.1
Published
Node.js SDK for Custodex AI agent governance
Maintainers
Readme
Custodex Node.js SDK
AI agent governance made simple.
Features
- 🔐 Identity management for AI agents (JWT, API keys, session tokens)
- 📋 Policy enforcement (TypeScript-based rules, visual editor)
- 📊 Behavioral monitoring (real-time telemetry, anomaly detection)
- ✅ Compliance automation (Singapore MGF, EU AI Act, SOC 2)
- 🤝 Multi-agent security (coordination monitoring, collusion detection)
Installation
npm install @custodex/sdk
# or
yarn add @custodex/sdk
# or
pnpm add @custodex/sdkQuick Start
import { Custodex } from '@custodex/sdk';
// Initialize client
const custodex = new Custodex({ apiKey: 'cus_xxx' });
// Govern a function
const updateCustomer = custodex.govern(
{ scope: 'crm:write' },
async (customerId: string, data: object) => {
return await crmApi.update(customerId, data);
}
);
// Use the governed function
const result = await updateCustomer('cust_123', { name: 'John Doe' });Core Concepts
Client Initialization
import { Custodex } from '@custodex/sdk';
const custodex = new Custodex({
apiKey: 'cus_xxx', // Required
baseUrl: 'https://custodex.vercel.app', // Optional
timeout: 10000, // Optional (ms)
});Verify Actions
// Check if an action is allowed
const decision = await custodex.verify(
'tool.search', // Action identifier
'tool:search', // Permission scope
{ // Optional metadata
userId: '123',
query: 'test'
}
);
if (decision.decision === 'allowed') {
// Proceed with action
} else if (decision.decision === 'denied') {
console.error('Action denied:', decision.reason);
} else if (decision.decision === 'pending_approval') {
console.log('Approval required:', decision.approvalId);
}Log Telemetry
await custodex.log(
'tool.search', // Action
'tool:search', // Scope
'allowed', // Decision
150, // Latency in ms
{ results: 42 } // Optional metadata
);Govern Functions
// Basic governance
const searchProducts = custodex.govern(
{ scope: 'products:read' },
async (query: string) => {
return await api.search(query);
}
);
// With approval requirement
const deleteCustomer = custodex.govern(
{
scope: 'crm:delete',
requireApproval: true
},
async (customerId: string) => {
return await api.delete(customerId);
}
);
// With metadata
const processRefund = custodex.govern(
{
scope: 'billing:refund',
metadata: { department: 'finance' }
},
async (orderId: string, amount: number) => {
return await billing.refund(orderId, amount);
}
);Integrations
Express Middleware
import express from 'express';
import { Custodex, custodexMiddleware } from '@custodex/sdk';
const app = express();
const custodex = new Custodex({ apiKey: 'cus_xxx' });
// Apply to all routes
app.use(custodexMiddleware(custodex));
app.get('/api/customers', async (req, res) => {
const customers = await getCustomers();
res.json(customers);
});
// Require specific scope
import { requireScope } from '@custodex/sdk';
app.delete(
'/api/customers/:id',
requireScope('crm:delete'),
async (req, res) => {
await deleteCustomer(req.params.id);
res.status(204).send();
}
);Fastify Plugin
import Fastify from 'fastify';
import { Custodex, custodexPlugin } from '@custodex/sdk';
const fastify = Fastify();
const custodex = new Custodex({ apiKey: 'cus_xxx' });
await fastify.register(custodexPlugin, { custodex });
fastify.get('/api/customers', async (request, reply) => {
const customers = await getCustomers();
return customers;
});
await fastify.listen({ port: 3000 });LangChain.js
import { ChatOpenAI } from '@langchain/openai';
import { initializeAgentExecutorWithOptions } from 'langchain/agents';
import { Custodex } from '@custodex/sdk';
import { CustodexCallbackHandler } from '@custodex/sdk/langchain';
const custodex = new Custodex({ apiKey: 'cus_xxx' });
const handler = new CustodexCallbackHandler(custodex);
const llm = new ChatOpenAI({
callbacks: [handler],
});
const agent = await initializeAgentExecutorWithOptions(
tools,
llm,
{
agentType: 'openai-functions',
callbacks: [handler],
}
);
// All LLM calls, tool executions, and agent actions are now governed
const result = await agent.invoke({ input: 'What is 2+2?' });Model Context Protocol (MCP)
Server-side
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { Custodex } from '@custodex/sdk';
import { GovernedMCPServer } from '@custodex/sdk/mcp';
const custodex = new Custodex({ apiKey: 'cus_xxx' });
const mcpServer = new Server({ name: 'my-server', version: '1.0.0' }, {});
const governed = new GovernedMCPServer(custodex, mcpServer as any);
// Configure governance
governed.addGovernance({
defaultScope: 'mcp:tool',
toolScopes: {
'delete_file': 'filesystem:delete',
'send_email': 'email:send',
},
requireApproval: ['delete_file'],
blockedTools: ['format_disk'],
});
// Add governed tools
governed.wrapTool({
name: 'read_file',
inputSchema: {
type: 'object',
properties: {
path: { type: 'string' }
}
},
handler: async (args: any) => {
return await fs.readFile(args.path, 'utf-8');
},
}, 'filesystem:read');Client-side
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { Custodex } from '@custodex/sdk';
import { GovernedMCPClient } from '@custodex/sdk/mcp';
const custodex = new Custodex({ apiKey: 'cus_xxx' });
const mcpClient = new Client({ name: 'my-client', version: '1.0.0' }, {});
const governed = new GovernedMCPClient(custodex, mcpClient as any);
// Call tools with automatic governance
const result = await governed.callTool('read_file', { path: '/etc/hosts' });Decorators
Method Decorators
import { requireApproval, rateLimit } from '@custodex/sdk';
class CustomerService {
@requireApproval('delete_customer')
async deleteCustomer(customerId: string) {
return await api.delete(customerId);
}
@rateLimit(100, 60000) // 100 calls per minute
async searchCustomers(query: string) {
return await api.search(query);
}
}Error Handling
import {
ActionDeniedException,
ApprovalRequiredException,
AuthenticationError,
RateLimitError,
} from '@custodex/sdk';
try {
await updateCustomer('cust_123', data);
} catch (error) {
if (error instanceof ActionDeniedException) {
console.error('Action denied:', error.reason);
} else if (error instanceof ApprovalRequiredException) {
console.log('Approval required:', error.approvalId);
// Check approval status
const status = await custodex.getApprovalStatus(error.approvalId);
console.log('Approval status:', status);
} else if (error instanceof AuthenticationError) {
console.error('Invalid API key');
} else if (error instanceof RateLimitError) {
console.error('Rate limit exceeded');
}
}TypeScript
This SDK is written in TypeScript and provides full type definitions out of the box.
import type {
Decision,
Agent,
Policy,
Approval,
TelemetryEvent
} from '@custodex/sdk';
const decision: Decision = await custodex.verify('action', 'scope');Performance
- Identity verification: <10ms p99
- Policy evaluation: <5ms p99
- Action logging: <2ms p99 (async, non-blocking)
- Total overhead: <100ms p99
Requirements
- Node.js 18+
- TypeScript 5+ (for TypeScript projects)
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
License
MIT License - see LICENSE for details.
Support
- 📧 Email: [email protected]
- 💬 Discord: discord.gg/custodex
- 📚 Docs: docs.custodex.dev
- 🐛 Issues: GitHub Issues
