DEV Community

Scott Smith (JNYCode)
Scott Smith (JNYCode)

Posted on

How to add trust boundaries to your multi-agent system

You have a personal AI assistant that can search the web, read documents, and send emails. It delegates research tasks to a cheaper sub-agent. How do you prevent that sub-agent from sending emails as you?

If your answer involves prompt engineering or "it just wouldn't do that," this tutorial is for you.

The problem
Multi-agent frameworks like CrewAI, AutoGen, and LangGraph handle orchestration well. MCP handles tool access. But none of them answer the delegation trust question: when Agent A hands a task to Agent B, what limits B's authority?

DelegateOS solves this with cryptographic delegation tokens. Let's build it step by step.

Setup

npm install delegateos
Enter fullscreen mode Exit fullscreen mode
import {
  generateKeypair,
  createDCT,
  attenuateDCT,
  verifyDCT,
  createMCPPlugin,
  InMemoryRevocationList,
} from 'delegateos';
Enter fullscreen mode Exit fullscreen mode

Step 1: Create identities
Every participant gets an Ed25519 keypair. This is their identity in the delegation system.

const you = generateKeypair();        // Root authority (you)
const assistant = generateKeypair();   // Your main AI assistant
const researcher = generateKeypair();  // Cheap research sub-agent
Enter fullscreen mode Exit fullscreen mode

Step 2: Grant capabilities to your assistant
You create a root Delegation Capability Token (DCT) for your assistant. This token defines exactly what it can do.

const assistantToken = createDCT({
  issuer: you,
  delegatee: assistant.principal,
  capabilities: [
    { namespace: 'web', action: 'search', resource: '*' },
    { namespace: 'docs', action: 'read', resource: '/home/me/**' },
    { namespace: 'email', action: 'send', resource: '*' },
  ],
  contractId: 'ct_daily_tasks',
  delegationId: 'del_001',
  parentDelegationId: 'root',
  chainDepth: 0,
  maxChainDepth: 2,
  maxBudgetMicrocents: 1_000_000, // $10
  expiresAt: new Date(Date.now() + 86400_000).toISOString(), // 24 hours
});
Enter fullscreen mode Exit fullscreen mode

Your assistant can search the web, read your docs, and send emails. With a $10 budget and 24-hour expiry.

Step 3: Delegate research with narrower scope
Here's where it gets interesting. Your assistant delegates to a research sub-agent, but attenuates the token. The sub-agent gets strictly less authority.

const researchToken = attenuateDCT({
  token: assistantToken,
  attenuator: assistant,
  delegatee: researcher.principal,
  delegationId: 'del_002',
  contractId: 'ct_daily_tasks',
  allowedCapabilities: [
    { namespace: 'web', action: 'search', resource: '*.edu/**' },
  ],
  maxBudgetMicrocents: 50_000,    // $0.50
  expiresAt: new Date(Date.now() + 600_000).toISOString(), // 10 minutes
});
Enter fullscreen mode Exit fullscreen mode

The researcher can only search .edu domains. No docs access. No email. $0.50 budget. 10-minute window. These aren't suggestions. They're cryptographically enforced.

Step 4: Verify at the point of use
When the researcher tries to use a tool, verify the token:

// Allowed: web search on an .edu domain
const allowed = verifyDCT(researchToken, {
  resource: 'arxiv.org/search',
  namespace: 'web',
  operation: 'search',
  now: new Date().toISOString(),
  spentMicrocents: 0,
  rootPublicKey: you.principal.id,
  revocationIds: [],
});
console.log(allowed.ok); // true

// Denied: email was never delegated
const denied = verifyDCT(researchToken, {
  resource: 'boss@company.com',
  namespace: 'email',
  operation: 'send',
  now: new Date().toISOString(),
  spentMicrocents: 0,
  rootPublicKey: you.principal.id,
  revocationIds: [],
});
console.log(denied.ok); // false
Enter fullscreen mode Exit fullscreen mode

Step 5: Add MCP middleware
If you're using MCP, DelegateOS drops in as middleware:

const plugin = createMCPPlugin({
  toolCapabilities: {
    web_search: { namespace: 'web', action: 'search' },
    read_file: {
      namespace: 'docs',
      action: 'read',
      resourceExtractor: (args) => args.path as string,
    },
    send_email: { namespace: 'email', action: 'send' },
  },
  trustedRoots: [you.principal.id],
  revocations: new InMemoryRevocationList(),
  budgetTracker: { getSpent: () => 0, recordSpend: () => {} },
});

// Every tools/call request gets checked against the caller's DCT
const result = await plugin.handleRequest(mcpRequest);
Enter fullscreen mode Exit fullscreen mode

The plugin also filters tools/list responses. The researcher would only see web_search in its available tools. read_file and send_email don't exist from its perspective.

Step 6: Revoke if needed
Something going wrong? Revoke the delegation instantly:

const revocationList = new InMemoryRevocationList();
revocationList.revoke('del_002'); // Single delegation
// or
revocationList.revokeCascade('del_001'); // Revoke assistant + all downstream
Enter fullscreen mode Exit fullscreen mode

All subsequent verification checks will reject the revoked tokens.

What you get
Sub-agents with cryptographically scoped authority
Budget enforcement across delegation chains
Time-limited delegations with automatic expiry
Mid-flight revocation
MCP integration with zero changes to your existing tools
The full source, docs, and a PR review demo are at github.com/newtro/delegateos. MIT licensed, 374 tests passing.

Top comments (0)