Home/Documentation/Agent SDK

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

Terminalbash
npm install @trustkernels/sdk

Self-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

agent.tstypescript
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

agent.tstypescript
// 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

agent.tstypescript
// 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

agent.tstypescript
// 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.

Environment Variablestypescript
# .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_xxxxxxxxxxxxxxxxxxxxx

API Key Scopes

proposals:writeSubmit and execute proposals
proposals:readRead proposal status and history
lattice:readQuery lattice nodes and edges
lattice:writeMutate lattice state (admin only)
policies:readRead compiled policies
events:subscribeSubscribe to real-time events

Proposals

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

Submit
Validate
Execute
Receipt

Proposal Intent Types

execute

Full execution with state mutation and effect emission. Default for production.

validate_only

Check policy compliance without execution. Use for pre-flight validation.

dry_run

Execute in sandbox mode. Returns computed effects without state changes.

query

Read-only query against lattice state. No validation or execution.

Proposal Typestypescript
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.

Policy Definitiontypescript
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_write

Database mutations

http_call

External API calls

payment_send

Payment processing

ai_infer

AI model inference

email_send

Email delivery

file_write

File system writes

notify

Push notifications

custom

Custom effect types

Effect and Receipt Typestypescript
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.

Trace Structuretypescript
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
}
Fetching and Verifying Tracestypescript
// 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.

Lattice Queriestypescript
// 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

POST
/api/engine/proposals

Submit a new proposal for validation and/or execution

POST
/api/engine/proposals/validate

Validate a proposal against the active policy

POST
/api/engine/proposals/execute

Execute a validated proposal

GET
/api/engine/proposals/:id

Get proposal status and details

Policies API

POST
/api/engine/policies/compile

Compile a policy source into a deployable artifact

GET
/api/engine/policies/:hash

Get a compiled policy by hash

GET
/api/engine/policies

List policies for a tenant

Lattice API

POST
/api/engine/lattice/query

Query nodes, edges, neighborhoods, or state roots

POST
/api/engine/lattice/mutate

Direct lattice mutation (admin only)

Verification API

POST
/api/engine/verify

Verify a receipt's cryptographic proofs

GET
/api/engine/traces/:id

Get execution trace with optional steps

Events API

GET
/api/engine/events

SSE stream of real-time kernel events

Subscribing to Eventstypescript
// 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

POST
/api/engine/governance/proposals

Create a new governance proposal for multi-sig approval

GET
/api/engine/governance/proposals

List governance proposals

GET
/api/engine/governance/proposals/:id

Get governance proposal status with vote counts

POST
/api/engine/governance/vote

Cast a vote on a governance proposal

Economics API

GET
/api/engine/economics/usage

Get usage meters for compute, storage, bandwidth

GET
/api/engine/economics/balance

Get account balance and credits

Emergency API

POST
/api/engine/emergency

Trigger emergency pause or resume (requires emergency key)

GET
/api/engine/emergency

Get current emergency state

Keys API

POST
/api/engine/keys

Create a new signing key (admin only)

GET
/api/engine/keys

List 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.

Governance Workflowtypescript
// 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.

Checking Usage and Balancetypescript
// 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.

Error Handlingtypescript
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

CodeDescription
POLICY_VIOLATIONAction not permitted by policy
EFFECT_LIMIT_EXCEEDEDEffect rate limit reached
GAS_LIMIT_EXCEEDEDExecution exceeded gas limit
SCHEMA_MISMATCHPayload doesn't match expected schema
EDGE_NOT_ALLOWEDEdge type not in policy's allowed list
APPROVAL_REQUIREDAction requires governance approval
INSUFFICIENT_STAKEAgent lacks required stake
INSUFFICIENT_BALANCENot 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)
    }
  }
)