Agent SDK Documentation
Everything you need to integrate your AI agents with TrustKernels. Submit proposals, validate actions against policies, and receive cryptographically signed receipts.
Introduction
The TrustKernels Agent SDK provides a type-safe interface for AI agents to interact with the UTMF (Universal Trust Management Framework) governance kernel. Every action your agent takes is validated against organizational policies, executed in a deterministic rewrite engine, and recorded with cryptographic proofs.
Policy Enforcement
Every agent action is validated against compiled policy artifacts with O(1) lookup time.
Full Auditability
Complete execution traces with before/after state roots and signed receipts.
Lattice Storage
Content-addressed tuple storage with labeled edges and neighborhood queries.
Effect Ordering
Deterministic effect ordering with idempotency keys and boundary emission.
Installation
npm install @trustkernels/sdkSelf-Hosted Engine Required
The SDK communicates with a self-hosted kernel engine. See the engine setup guide for Docker deployment instructions.
Quick Start
Get your first agent action validated and executed in under 5 minutes.
1Initialize the Client
import { createKernelClient } from "@trustkernels/sdk"
const kernel = createKernelClient({
baseUrl: process.env.KERNEL_ENGINE_URL,
apiKey: process.env.KERNEL_API_KEY,
timeout: 30000,
retry: { attempts: 3, backoffMs: 1000 }
})2Submit a Proposal
// Your agent wants to process a customer refund
const proposal = await kernel.submitProposal({
tenant_id: "org_acme",
agent_id: "support-agent-1",
handle_id: "handle_customer_support",
intent: "execute",
payload: {
action: "process_refund",
ticket_id: "TKT-12345",
customer_email: "customer@example.com",
refund_amount: 149.99,
reason: "defective_product"
},
idempotency_key: "refund-TKT-12345-v1"
})
console.log("Proposal submitted:", proposal.proposal_id)3Validate Against Policy
// Validate the proposal against the active policy
const validation = await kernel.validateProposal({
proposal_id: proposal.proposal_id
})
if (!validation.valid) {
console.error("Policy violations:", validation.errors)
// Handle policy rejection - maybe request human approval
return
}
console.log("Estimated gas:", validation.estimated_gas)
console.log("Effects to emit:", validation.computed_effects)4Execute and Get Receipt
// Execute the validated proposal
const result = await kernel.executeProposal({
proposal_id: proposal.proposal_id,
approved_by: "system", // or human approver ID
gas_limit: 10000,
timeout_ms: 5000
})
// Check execution status
if (result.status === "completed") {
console.log("Execution successful!")
console.log("Trace ID:", result.trace_id)
console.log("State root:", result.after_root)
// Process emitted effects
for (const effect of result.effects) {
console.log(`Effect ${effect.kind}: ${effect.status}`)
}
// Get cryptographic receipts
for (const receipt of result.receipts) {
console.log(`Receipt ${receipt.id}: ${receipt.receipt_hash}`)
}
} else {
console.error("Execution failed:", result.status)
}Authentication
The kernel engine uses API keys for authentication. Each agent should have its own API key with scoped permissions.
# .env
KERNEL_ENGINE_URL=https://kernel.your-org.com
KERNEL_API_KEY=tk_live_xxxxxxxxxxxxxxxxxxxxx
# For development
KERNEL_ENGINE_URL=http://localhost:8080
KERNEL_API_KEY=tk_dev_xxxxxxxxxxxxxxxxxxxxxAPI Key Scopes
proposals:writeSubmit and execute proposalsproposals:readRead proposal status and historylattice:readQuery lattice nodes and edgeslattice:writeMutate lattice state (admin only)policies:readRead compiled policiesevents:subscribeSubscribe to real-time eventsProposals
A proposal represents an agent's intent to perform an action. Every action must be submitted as a proposal, validated against policy, and executed through the rewrite engine.
Proposal Lifecycle
Proposal Intent Types
executeFull execution with state mutation and effect emission. Default for production.
validate_onlyCheck policy compliance without execution. Use for pre-flight validation.
dry_runExecute in sandbox mode. Returns computed effects without state changes.
queryRead-only query against lattice state. No validation or execution.
interface SubmitProposalRequest {
tenant_id: string // Organization ID
agent_id: string // Your agent's unique ID
handle_id: string // Execution context/scope
intent: ProposalIntent // "execute" | "validate_only" | "dry_run" | "query"
payload: Record<string, unknown> // Action-specific data
ordkey?: string // Optional ordering key for sequencing
idempotency_key?: string // Prevents duplicate execution
}
interface SubmitProposalResponse {
proposal_id: string // Unique proposal identifier
status: string // "pending" | "validated" | "rejected"
request_hash: TupleHash // Content-addressed hash of request
created_at: string // ISO timestamp
errors: ValidationError[] // Any immediate validation errors
}Policies
Policies define what actions agents can take, which effects they can emit, and resource limits. Policies are compiled into binary artifacts for O(1) validation lookups.
interface PolicySource {
id: string
name: string
version: number
// Allowed edge types in the lattice
allowed_edges: AllowedEdge[]
// Permitted rewrite rules
allowed_rules: AllowedRule[]
// Effect emission permissions
allowed_effects: AllowedEffect[]
// Execution bounds
temperature: {
min_steps?: number
max_steps?: number
gas_limit?: number
timeout_ms?: number
}
// Trace configuration
trace_config: {
required?: boolean
include_intermediate?: boolean
hash_algorithm?: "sha256" | "blake3" | "poseidon"
}
// Sealing rules for finalization
sealing_rules: SealingRule[]
}
interface AllowedEffect {
kind: EffectKind // "db_write" | "http_call" | "payment_send" | etc.
max_per_epoch?: number // Rate limit per epoch
max_payload_bytes?: number // Size limit
requires_approval?: boolean // Human-in-the-loop
}Compiled Policy Benefits
Policies are compiled into bitsets and lookup tables, enabling constant-time O(1) validation. No policy evaluation overhead during execution.
Effects & Receipts
Effects are the side-effects emitted when proposals execute. Each effect is recorded with a cryptographic receipt containing state roots and gateway signatures.
Effect Types
db_writeDatabase mutations
http_callExternal API calls
payment_sendPayment processing
ai_inferAI model inference
email_sendEmail delivery
file_writeFile system writes
notifyPush notifications
customCustom effect types
interface Effect {
id: string
kind: EffectKind
status: "pending" | "executing" | "completed" | "failed" | "cancelled"
ordkey: string // Ordering key for sequencing
ordseq: number // Sequence within ordkey
idempotency_key: string // Deduplication key
request_hash: TupleHash // Hash of the request tuple
payload: Record<string, unknown>
trace_id?: string // Parent execution trace
handle_id: string // Execution context
created_at: string
executed_at?: string
}
interface Receipt {
id: string
effect_id: string
receipt_hash: TupleHash // Content-addressed receipt hash
status: string
response?: Record<string, unknown>
error?: string
before_root?: StateRoot // State before effect
after_root?: StateRoot // State after effect
gateway_signature?: string // Ed25519 signature from gateway
created_at: string
}Traces
Every execution produces a verifiable trace containing all steps, state transitions, and a chain of Merkle roots. Traces enable full replay and audit.
interface Trace {
id: string
hash: TraceHash // Deterministic trace hash
before_root: StateRoot // Initial state root
after_root: StateRoot // Final state root
policy_hash: PolicyHash // Policy used for validation
gas_used: number // Total gas consumed
duration_ms: number // Execution time
step_count: number // Number of rewrite steps
status: "running" | "completed" | "failed" | "timeout" | "gas_exhausted"
proposal_id?: string
handle_id?: string
created_at: string
}
interface TraceStep {
id: string
trace_id: string
step_number: number
rule_id: string // Rewrite rule applied
matched_at: string // Pattern match location
gas_used: number // Gas for this step
nodes_added: string[] // New lattice nodes
nodes_removed: string[] // Deleted lattice nodes
edges_added: string[] // New lattice edges
edges_removed: string[] // Deleted lattice edges
effects_emitted: string[] // Effects produced
intermediate_root?: StateRoot // State after this step
}// Get a trace with all steps
const { trace, steps } = await kernel.getTrace({
trace_id: result.trace_id,
include_steps: true
})
console.log(`Trace ${trace.id}:`)
console.log(` Status: ${trace.status}`)
console.log(` Steps: ${trace.step_count}`)
console.log(` Gas used: ${trace.gas_used}`)
console.log(` Before root: ${trace.before_root}`)
console.log(` After root: ${trace.after_root}`)
// Examine individual steps
for (const step of steps) {
console.log(` Step ${step.step_number}: ${step.rule_id}`)
console.log(` Nodes added: ${step.nodes_added.length}`)
console.log(` Effects: ${step.effects_emitted.length}`)
}Lattice Store
The lattice is a content-addressed graph store where nodes are tuples (identified by hash) and edges are labeled relationships. This provides a unified data model for all agent state.
// Query a specific node by hash
const nodeResult = await kernel.queryLattice({
tenant_id: "org_acme",
query: { type: "node", hash: "sha256:abc123..." }
})
// Query neighborhood (radius-limited BFS)
const neighborhood = await kernel.queryLattice({
tenant_id: "org_acme",
query: {
type: "neighborhood",
center: "sha256:abc123...",
radius: 2,
labels: ["references", "created_by"]
}
})
// Query edges by criteria
const edges = await kernel.queryLattice({
tenant_id: "org_acme",
query: {
type: "edges",
src: "sha256:abc123...",
status: "committed"
}
})
// Get current state root
const { state_root } = await kernel.queryLattice({
tenant_id: "org_acme",
query: { type: "state_root" }
})
// Get lattice statistics
const { stats } = await kernel.queryLattice({
tenant_id: "org_acme",
query: { type: "stats" }
})
console.log(`Nodes: ${stats.node_count}, Edges: ${stats.edge_count}`)API Reference
Complete REST API endpoints exposed by the kernel engine.
Proposals API
/api/engine/proposalsSubmit a new proposal for validation and/or execution
/api/engine/proposals/validateValidate a proposal against the active policy
/api/engine/proposals/executeExecute a validated proposal
/api/engine/proposals/:idGet proposal status and details
Policies API
/api/engine/policies/compileCompile a policy source into a deployable artifact
/api/engine/policies/:hashGet a compiled policy by hash
/api/engine/policiesList policies for a tenant
Lattice API
/api/engine/lattice/queryQuery nodes, edges, neighborhoods, or state roots
/api/engine/lattice/mutateDirect lattice mutation (admin only)
Verification API
/api/engine/verifyVerify a receipt's cryptographic proofs
/api/engine/traces/:idGet execution trace with optional steps
Events API
/api/engine/eventsSSE stream of real-time kernel events
// Subscribe to real-time events
const unsubscribe = kernel.subscribe(
{
tenant_id: "org_acme",
events: ["proposal.submitted", "proposal.executed", "effect.completed"],
agent_id: "support-agent-1" // Optional filter
},
(event) => {
switch (event.type) {
case "proposal.submitted":
console.log("New proposal:", event.payload.proposal_id)
break
case "effect.completed":
console.log("Effect completed:", event.payload.effect_id)
break
}
}
)
// Later: stop listening
unsubscribe()Governance API
/api/engine/governance/proposalsCreate a new governance proposal for multi-sig approval
/api/engine/governance/proposalsList governance proposals
/api/engine/governance/proposals/:idGet governance proposal status with vote counts
/api/engine/governance/voteCast a vote on a governance proposal
Economics API
/api/engine/economics/usageGet usage meters for compute, storage, bandwidth
/api/engine/economics/balanceGet account balance and credits
Emergency API
/api/engine/emergencyTrigger emergency pause or resume (requires emergency key)
/api/engine/emergencyGet current emergency state
Keys API
/api/engine/keysCreate a new signing key (admin only)
/api/engine/keysList signing keys for a tenant
Governance
High-impact actions can require multi-signature approval. The governance system supports configurable thresholds, voting, and time-locked execution.
// Check if action requires approval
const validation = await kernel.validateProposal({
proposal_id: proposal.proposal_id
})
const requiresApproval = validation.computed_effects.some(
e => e.requires_approval
)
if (requiresApproval) {
// Submit for governance approval
const govProposal = await kernel.governance.createProposal({
tenant_id: "org_acme",
proposal_type: "execute_action",
title: "Approve high-value refund",
description: "Customer refund exceeding $1000 threshold",
linked_proposal_id: proposal.proposal_id,
threshold_id: "high_value_actions"
})
// Approvers vote
await kernel.governance.vote({
proposal_id: govProposal.id,
voter_id: "admin_1",
vote: "approve",
comment: "Verified customer complaint"
})
// Check if threshold met
const status = await kernel.governance.getProposalStatus(govProposal.id)
if (status.approved) {
// Now execute with governance approval
await kernel.executeProposal({
proposal_id: proposal.proposal_id,
approved_by: govProposal.id
})
}
}Economics
The kernel tracks resource usage (compute, storage, bandwidth) and supports prepaid credits, staking, and slashing for misbehavior.
// Get current usage meters
const usage = await kernel.economics.getUsage({
tenant_id: "org_acme",
agent_id: "support-agent-1"
})
console.log("Usage this epoch:")
console.log(` Compute: ${usage.compute_gas} gas`)
console.log(` Storage: ${usage.storage_bytes} bytes`)
console.log(` Bandwidth: ${usage.bandwidth_bytes} bytes`)
console.log(` Effects: ${usage.effect_count}`)
// Check account balance
const balance = await kernel.economics.getBalance({
tenant_id: "org_acme"
})
console.log(`Balance: ${balance.available} credits`)
console.log(`Reserved: ${balance.reserved} credits`)Error Handling
The SDK provides typed errors for different failure modes. Always handle errors appropriately based on their type.
import { KernelError } from "@trustkernels/sdk"
try {
const result = await kernel.executeProposal({
proposal_id: "...",
approved_by: "system"
})
} catch (error) {
if (error instanceof KernelError) {
switch (error.statusCode) {
case 400:
// Invalid request - check payload
console.error("Invalid request:", error.body)
break
case 401:
// Authentication failed - check API key
console.error("Authentication failed")
break
case 403:
// Policy violation - action not allowed
console.error("Policy violation:", error.body)
break
case 404:
// Resource not found
console.error("Proposal not found")
break
case 409:
// Conflict - maybe idempotency key collision
console.error("Conflict:", error.body)
break
case 429:
// Rate limited - back off
console.error("Rate limited, retry later")
break
case 503:
// Engine paused or unavailable
console.error("Service unavailable")
break
default:
console.error("Kernel error:", error.message)
}
} else {
// Network or other error
console.error("Unexpected error:", error)
}
}Validation Error Codes
| Code | Description |
|---|---|
POLICY_VIOLATION | Action not permitted by policy |
EFFECT_LIMIT_EXCEEDED | Effect rate limit reached |
GAS_LIMIT_EXCEEDED | Execution exceeded gas limit |
SCHEMA_MISMATCH | Payload doesn't match expected schema |
EDGE_NOT_ALLOWED | Edge type not in policy's allowed list |
APPROVAL_REQUIRED | Action requires governance approval |
INSUFFICIENT_STAKE | Agent lacks required stake |
INSUFFICIENT_BALANCE | Not enough credits for fees |
Best Practices
Always Use Idempotency Keys
Include an idempotency key with every proposal to prevent duplicate execution on retries. Use a deterministic key based on the action content.
await kernel.submitProposal({
// ...
idempotency_key: `refund-${ticket_id}-${amount}-v1`
})Validate Before Execute
Always call validateProposal before executeProposal to catch policy violations early and provide better user feedback.
const validation = await kernel.validateProposal({ proposal_id })
if (!validation.valid) {
// Handle gracefully - don't attempt execution
return { error: validation.errors }
}Handle Receipts Appropriately
Store receipts for audit purposes. Verify signatures on critical receipts before considering actions complete.
for (const receipt of result.receipts) {
// Store receipt for audit
await db.receipts.insert(receipt)
// Verify critical receipts
if (receipt.effect_id === criticalEffectId) {
const verification = await kernel.verifyReceipt({
receipt_id: receipt.id,
verify_trace: true
})
if (!verification.valid) {
throw new Error("Receipt verification failed")
}
}
}Use Appropriate Gas Limits
Set gas limits based on expected operation complexity. Too low causes failures; too high may indicate runaway execution.
// Simple operations: 1,000-5,000 gas
// Medium operations: 5,000-20,000 gas
// Complex operations: 20,000-100,000 gas
await kernel.executeProposal({
proposal_id,
approved_by: "system",
gas_limit: 10000, // Appropriate for medium complexity
timeout_ms: 5000 // 5 second timeout
})Subscribe to Events for Async Workflows
For long-running or async workflows, subscribe to events rather than polling.
// Submit proposal
const proposal = await kernel.submitProposal({ ... })
// Subscribe to completion event
kernel.subscribe(
{
tenant_id: "org_acme",
events: ["proposal.executed"],
},
(event) => {
if (event.payload.proposal_id === proposal.proposal_id) {
handleCompletion(event.payload)
}
}
)