@certnode/agent-sdk
v0.1.1
Published
CertNode Agent SDK - Accountability for AI agent actions with cryptographic proof
Maintainers
Readme
@certnode/agent-sdk
Prove what your AI agent actually did. Cryptographically signed, independently timestamped receipts for every agent action — scoped to a human-granted authorization, verifiable by anyone without trusting your servers.
When an AI agent makes a consequential decision — issues a refund, sends a record, executes a transfer — "what did it actually do, and was it allowed to?" becomes a question you may have to answer to a regulator, an auditor, or a court. A log you wrote yourself is a claim; a cryptographic receipt is evidence.
Why this, and why now
- EU AI Act Article 12 & 14 (high-risk obligations phasing in through 2026) require traceable records of automated decisions and evidence that human-oversight measures actually fired — not documentation that they exist. A self-reported log requires trusting the operator; a cryptographic receipt does not.
- Records you can't repudiate. Every action is signed (ES256 JWS) over a content hash, timestamped by an independent RFC 3161 authority, and anchored to Bitcoin. Tamper-evident and structured to the FRE 902(13)/(14) self-authenticating standard for electronic records.
- Log, don't gate. CertNode records every action and flags out-of-scope ones, integrating in one line around your existing agent — no blocking gate to wire into every step. (Want hard enforcement too? Flip
enforceScopeon the adapter.) - Human authorization chains. "Human X authorized Agent Y to do Z, within constraints W, until time T" — signed, with scope, prohibited actions, and expiry.
Model- and framework-neutral: works with Claude, OpenAI, or any agent stack.
Installation
npm install @certnode/agent-sdkQuick Start
import { AgentClient } from '@certnode/agent-sdk'
// Initialize the client
const agent = new AgentClient({
apiKey: process.env.CERTNODE_API_KEY,
agentType: 'claude-agent',
agentName: 'Customer Service Bot',
agentVersion: '1.0.0',
capabilities: ['read_tickets', 'respond_tickets', 'escalate_tickets'],
})
// Create an authorization from a human supervisor
const auth = await agent.createAuthorization({
grantedByUserId: 'supervisor_123',
grantedByName: 'Sarah Chen',
grantedByEmail: '[email protected]',
scope: ['read_tickets', 'respond_tickets'],
prohibitedActions: ['delete_tickets', 'issue_refunds'],
expiresAt: new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString(), // 8 hours
reason: 'Weekend shift coverage',
})
// Log an action
const action = await agent.logAction({
authorizationId: auth.id,
actionType: 'respond_tickets',
actionDescription: 'Sent response to ticket #12345',
actionDetails: {
ticketId: '12345',
message: 'Thank you for contacting us...',
},
})
console.log(`Action logged: ${action.action.id}`)
console.log(`Within scope: ${action.action.withinScope}`)
console.log(`Proof URL: https://certnode.io/verify/${action.action.id}`)Vercel AI SDK
Wrap any Vercel AI SDK tool so every call is signed, timestamped, and scope-checked — one line, no logging code inside your tools:
import { tool } from 'ai'
import { z } from 'zod'
import { AgentClient, accountableTool } from '@certnode/agent-sdk'
const agent = new AgentClient({
apiKey: process.env.CERTNODE_API_KEY!,
agentType: 'support-agent',
agentName: 'Support Bot',
})
const auth = await agent.createAuthorization({
grantedByUserId: 'u_1',
grantedByName: 'Sarah Chen',
scope: ['issue_refund'],
prohibitedActions: ['close_account'],
})
const refundTool = accountableTool(
agent,
{ authorizationId: auth.id, actionType: 'issue_refund' },
tool({
description: 'Issue a refund to a customer',
parameters: z.object({ orderId: z.string(), amount: z.number() }),
execute: async ({ orderId, amount }) => refunds.issue(orderId, amount),
})
)
// Use refundTool with generateText / streamText as normal — every invocation
// now produces a signed, timestamped, scope-checked accountability receipt.Pass { enforceScope: true } to also throw ScopeViolationError when the model attempts something outside the granted scope. For non-tool call sites, withAccountability(client, options, fn) wraps any async function the same way.
Core Concepts
Authorization Chain
Every agent action must be authorized by a human. Authorizations include:
- Scope: What actions the agent can take
- Constraints: Limits on how actions can be performed
- Prohibited actions: Explicitly forbidden operations
- Time bounds: When the authorization expires
const auth = await agent.createAuthorization({
grantedByUserId: 'user_123',
grantedByName: 'John Doe',
scope: ['analyze_data', 'generate_report'],
constraints: {
maxRecordsPerQuery: 1000,
allowedDatabases: ['analytics', 'reporting'],
},
prohibitedActions: ['delete_data', 'export_pii'],
expiresAt: '2026-01-31T23:59:59Z',
})Action Logging
Every action is logged with cryptographic proof:
const result = await agent.logAction({
authorizationId: auth.id,
actionType: 'analyze_data',
actionDescription: 'Analyzed Q4 sales data',
actionDetails: {
query: 'SELECT region, SUM(revenue) FROM sales GROUP BY region',
rowCount: 45,
},
parentActionId: previousAction.id, // Optional: link to parent action
sessionId: 'session_abc123', // Optional: group related actions
})
// Check if action was within scope
if (!result.action.withinScope) {
console.warn('Scope violation:', result.violations)
}Proof Verification
Every action receives:
- SHA-256 content hash - Tamper-evident fingerprint
- JWS signature - ES256 cryptographic signature
- RFC 3161 timestamp - Legally compliant timestamp
// Verify an action
const verification = await agent.verifyAction(actionId)
console.log(verification.valid) // true
console.log(verification.contentHash) // sha256:abc123...
console.log(verification.rfc3161Timestamp) // 2026-01-05T12:00:00ZAPI Reference
AgentClient
new AgentClient(options: AgentClientOptions)Options:
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| apiKey | string | Yes | Your CertNode API key |
| agentType | string | Yes | Agent type identifier |
| agentName | string | Yes | Human-readable agent name |
| agentVersion | string | No | Semantic version |
| description | string | No | Agent description |
| capabilities | string[] | No | List of capabilities |
| baseUrl | string | No | API base URL (default: https://certnode.io) |
createAuthorization()
agent.createAuthorization(params: CreateAuthorizationParams): Promise<Authorization>Parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| grantedByUserId | string | Yes | User ID of grantor |
| grantedByName | string | Yes | Name of grantor |
| grantedByEmail | string | No | Email of grantor |
| grantedByTitle | string | No | Job title of grantor |
| scope | string[] | Yes | Allowed action types |
| constraints | object | No | Action constraints |
| prohibitedActions | string[] | No | Explicitly forbidden actions |
| validFrom | string | No | ISO timestamp (default: now) |
| expiresAt | string | No | ISO timestamp |
| reason | string | No | Reason for authorization |
logAction()
agent.logAction(params: LogActionParams): Promise<ActionResult>Parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| authorizationId | string | Yes | Authorization ID |
| actionType | string | Yes | Type of action |
| actionDescription | string | Yes | Human-readable description |
| actionDetails | object | No | Structured action data |
| parentActionId | string | No | Parent action for chaining |
| sessionId | string | No | Session identifier |
listActions()
agent.listActions(params: ListActionsParams): Promise<ActionList>Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| authorizationId | string | Filter by authorization |
| sessionId | string | Filter by session |
| actionType | string | Filter by action type |
| withinScope | boolean | Filter by scope status |
| limit | number | Results per page (default: 50) |
| offset | number | Pagination offset |
revokeAuthorization()
agent.revokeAuthorization(authorizationId: string, reason?: string): Promise<void>Integration Examples
With Claude (Anthropic)
import { AgentClient } from '@certnode/agent-sdk'
import Anthropic from '@anthropic-ai/sdk'
const agent = new AgentClient({
apiKey: process.env.CERTNODE_API_KEY,
agentType: 'claude-agent',
agentName: 'Content Moderator',
})
const anthropic = new Anthropic()
async function moderateContent(authId: string, content: string) {
// Log the moderation action
const action = await agent.logAction({
authorizationId: authId,
actionType: 'moderate_content',
actionDescription: 'Analyzing content for policy violations',
actionDetails: { contentLength: content.length },
})
// Call Claude
const result = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
messages: [{ role: 'user', content: `Moderate this: ${content}` }],
})
return { action, result }
}With OpenAI
import { AgentClient } from '@certnode/agent-sdk'
import OpenAI from 'openai'
const agent = new AgentClient({
apiKey: process.env.CERTNODE_API_KEY,
agentType: 'openai-agent',
agentName: 'Data Analyst',
})
const openai = new OpenAI()
async function analyzeData(authId: string, data: object[]) {
const action = await agent.logAction({
authorizationId: authId,
actionType: 'analyze_data',
actionDescription: 'Running data analysis',
actionDetails: { recordCount: data.length },
})
const completion = await openai.chat.completions.create({
model: 'gpt-4o',
messages: [
{ role: 'system', content: 'Analyze the data and provide insights.' },
{ role: 'user', content: JSON.stringify(data) },
],
})
return { action, analysis: completion.choices[0].message.content }
}Dashboard
View all agent activity at certnode.io/dashboard/agents:
- Real-time action feed
- Authorization management
- Scope violation alerts
- Audit log export
Error Handling
import { AgentClient, AgentError, AuthorizationExpiredError } from '@certnode/agent-sdk'
try {
await agent.logAction({ ... })
} catch (error) {
if (error instanceof AuthorizationExpiredError) {
// Request new authorization
} else if (error instanceof AgentError) {
console.error('Agent error:', error.code, error.message)
}
}TypeScript Support
Full TypeScript support with exported types:
import type {
AgentClientOptions,
Authorization,
CreateAuthorizationParams,
LogActionParams,
ActionResult,
AgentAction,
} from '@certnode/agent-sdk'License
MIT
