AI Safety & Guardrails for Token Operations

Implement validation layers, rate limiting, and approval workflows to safely let AI agents manage tokens.

15 minIntermediate

Prerequisites

  • Completed the Build an AI Agent with MCP tutorial
  • Basic understanding of LLM tool calling

What You'll Build

AI agents are powerful, but power without guardrails is dangerous. An unchecked LLM could transfer all your tokens, delete templates, or burn through your API rate limits. In this tutorial you'll build a safety layer that wraps DUAL API calls with validation, rate limiting, human-in-the-loop approval, and audit logging — ensuring AI agents operate within safe boundaries.

Step 1 — Define Permission Tiers

Categorize every DUAL operation by risk level:

javascript
const PERMISSION_TIERS = {
  // Green: AI can execute freely
  read: [
    'list_objects', 'get_object', 'list_templates',
    'get_template', 'search_objects', 'get_balance',
    'list_webhooks', 'get_sequencer_status'
  ],

  // Yellow: AI can execute with rate limits
  limited: [
    'mint_object', 'update_properties',
    'create_webhook', 'send_notification'
  ],

  // Red: Requires human approval
  restricted: [
    'transfer_object', 'burn_object',
    'delete_template', 'create_api_key',
    'bulk_transfer', 'update_template'
  ],

  // Black: AI can never execute
  forbidden: [
    'delete_organization', 'change_owner',
    'reset_api_keys', 'modify_roles'
  ]
};

Step 2 — Build the Rate Limiter

Prevent AI agents from making too many calls in a short period:

javascript
class RateLimiter {
  constructor() {
    this.windows = new Map();
  }

  check(operation, limits = { perMinute: 30, perHour: 200 }) {
    const now = Date.now();
    const key = operation;

    if (!this.windows.has(key)) {
      this.windows.set(key, []);
    }

    const calls = this.windows.get(key);
    // Clean old entries
    const minuteAgo = now - 60000;
    const hourAgo = now - 3600000;
    const recentMinute = calls.filter(t => t > minuteAgo);
    const recentHour = calls.filter(t => t > hourAgo);

    if (recentMinute.length >= limits.perMinute) {
      return { allowed: false, reason: 'Rate limit: too many calls per minute' };
    }
    if (recentHour.length >= limits.perHour) {
      return { allowed: false, reason: 'Rate limit: too many calls per hour' };
    }

    calls.push(now);
    this.windows.set(key, calls.filter(t => t > hourAgo));
    return { allowed: true };
  }
}

Step 3 — Human-in-the-Loop Approval

For restricted operations, pause execution and ask for confirmation:

javascript
class ApprovalQueue {
  constructor() {
    this.pending = new Map();
  }

  async requestApproval(operation, params, context) {
    const id = crypto.randomUUID();
    const request = {
      id, operation, params, context,
      requestedAt: new Date().toISOString(),
      status: 'pending'
    };

    this.pending.set(id, request);

    // Send notification to admin
    await sendAdminNotification({
      title: "AI Agent Approval Request",
      body: "Operation: " + operation + "\nParams: " + JSON.stringify(params) + "\nContext: " + context + "",
      approval_id: id
    });

    return { approval_id: id, status: 'pending' };
  }

  approve(id) {
    const req = this.pending.get(id);
    if (req) { req.status = 'approved'; return true; }
    return false;
  }

  deny(id, reason) {
    const req = this.pending.get(id);
    if (req) { req.status = 'denied'; req.reason = reason; return true; }
    return false;
  }
}

Step 4 — The Guardrail Wrapper

Wrap every AI tool call through the safety layer:

javascript
const rateLimiter = new RateLimiter();
const approvalQueue = new ApprovalQueue();
const auditLog = [];

async function safeExecute(operation, params, aiContext) {
  // 1. Check if forbidden
  if (PERMISSION_TIERS.forbidden.includes(operation)) {
    auditLog.push({ operation, status: 'blocked', reason: 'forbidden' });
    return { error: 'This operation is not available to AI agents.' };
  }

  // 2. Check rate limits for non-read operations
  if (!PERMISSION_TIERS.read.includes(operation)) {
    const rateCheck = rateLimiter.check(operation);
    if (!rateCheck.allowed) {
      auditLog.push({ operation, status: 'rate_limited' });
      return { error: rateCheck.reason };
    }
  }

  // 3. Require approval for restricted operations
  if (PERMISSION_TIERS.restricted.includes(operation)) {
    const approval = await approvalQueue.requestApproval(
      operation, params, aiContext
    );
    auditLog.push({ operation, status: 'awaiting_approval', id: approval.approval_id });
    return { pending: true, message: 'Awaiting human approval', ...approval };
  }

  // 4. Execute
  const result = await executeOperation(operation, params);
  auditLog.push({
    operation, status: 'executed',
    timestamp: new Date().toISOString(), params
  });

  return result;
}

Step 5 — Audit Dashboard

Expose the audit log so you can review what the AI agent has been doing:

javascript
app.get('/admin/audit', (req, res) => {
  const { operation, status, since } = req.query;
  let logs = [...auditLog];

  if (operation) logs = logs.filter(l => l.operation === operation);
  if (status) logs = logs.filter(l => l.status === status);
  if (since) logs = logs.filter(l => new Date(l.timestamp) > new Date(since));

  res.json({
    total: logs.length,
    logs: logs.slice(-100) // Last 100 entries
  });
});
Defense in Depth: These client-side guardrails are your first line of defense, but always combine them with server-side rate limits and DUAL's built-in permission system. Use scoped API keys that only grant the permissions the AI actually needs.
Companion Repo: Get the full working source code for this tutorial at github.com/ro-ro-b/dual-ai-guardrails — clone it, add your API keys, and run it locally in minutes.

Summary

You've built a comprehensive safety layer for AI-powered token operations: permission tiers prevent dangerous calls, rate limiting stops runaway agents, human-in-the-loop approval catches high-risk operations, and audit logging provides full visibility. For more on the DUAL permission model, see Organization Roles & Permissions.