Bastio
Agent Security

Agent Identity

Cryptographic authentication and identity management for AI agents.

Agent Identity

Agent identity provides cryptographic authentication for your AI agents. Each agent gets a unique identity with an Ed25519 key pair, enabling:

  • Authentication - Verify which agent made a request
  • Authorization - Control what each agent can do
  • Audit trails - Track all actions to specific agents
  • Trust levels - Differentiate development vs production agents

Overview

When you register an agent with Bastio, you receive:

  1. Agent ID - Unique identifier (bastio_agent_xxx)
  2. Public Key - Ed25519 public key for verification
  3. Private Key - Ed25519 private key for signing (keep secret!)

Agents sign their requests, and Bastio verifies the signature before processing.

Registering an Agent

Via Dashboard

Navigate to Agent Security > Agents and click "Register Agent".

Via API

curl -X POST https://api.bastio.com/v1/guard/{proxyId}/agents \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Customer Support Agent",
    "description": "Handles customer inquiries and support tickets",
    "trust_level": "standard"
  }'

Response:

{
  "agent_id": "bastio_agent_abc123xyz",
  "name": "Customer Support Agent",
  "public_key": "MCowBQYDK2VwAyEA...",
  "private_key": "MC4CAQAwBQYDK2VwBCIEIP...",
  "trust_level": "standard",
  "created_at": "2024-01-15T10:00:00Z"
}

Store the private key securely! It won't be shown again. Use a secrets manager like AWS Secrets Manager, HashiCorp Vault, or environment variables.

Trust Levels

LevelDescriptionUse Case
developmentRelaxed policies, higher loggingTesting and development
standardNormal policy evaluationProduction agents
elevatedTrusted for sensitive operationsInternal tools, admin tasks
restrictedStricter policies, extra scrutinyUntrusted or new agents

Trust levels affect policy evaluation:

// Policy with trust level condition
{
  "name": "Allow Elevated Agents Only",
  "tool_pattern": "admin_*",
  "action": "allow",
  "conditions": {
    "agent_trust_level": ["elevated"]
  }
}

Authenticating Requests

Signing Requests

Sign your tool validation requests with the agent's private key:

import base64
import json
import time
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization

def sign_request(private_key_pem: str, request_body: dict) -> dict:
    """Sign a request with Ed25519 private key."""

    # Load private key
    private_key = serialization.load_pem_private_key(
        private_key_pem.encode(),
        password=None
    )

    # Create signing payload
    timestamp = int(time.time())
    payload = {
        "body": json.dumps(request_body, sort_keys=True),
        "timestamp": timestamp
    }
    payload_bytes = json.dumps(payload, sort_keys=True).encode()

    # Sign
    signature = private_key.sign(payload_bytes)
    signature_b64 = base64.b64encode(signature).decode()

    # Return headers to include
    return {
        "X-Bastio-Agent-Id": agent_id,
        "X-Bastio-Signature": signature_b64,
        "X-Bastio-Timestamp": str(timestamp)
    }

# Usage
headers = sign_request(AGENT_PRIVATE_KEY, tool_call_body)
response = requests.post(
    f"https://api.bastio.com/v1/guard/{proxy_id}/tool",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        **headers
    },
    json=tool_call_body
)
import * as crypto from 'crypto';

interface SignedHeaders {
  'X-Bastio-Agent-Id': string;
  'X-Bastio-Signature': string;
  'X-Bastio-Timestamp': string;
}

function signRequest(
  privateKeyPem: string,
  agentId: string,
  requestBody: object
): SignedHeaders {
  // Create signing payload
  const timestamp = Math.floor(Date.now() / 1000);
  const payload = JSON.stringify({
    body: JSON.stringify(requestBody),
    timestamp,
  });

  // Sign with Ed25519
  const privateKey = crypto.createPrivateKey(privateKeyPem);
  const signature = crypto.sign(null, Buffer.from(payload), privateKey);

  return {
    'X-Bastio-Agent-Id': agentId,
    'X-Bastio-Signature': signature.toString('base64'),
    'X-Bastio-Timestamp': timestamp.toString(),
  };
}

// Usage
const signedHeaders = signRequest(AGENT_PRIVATE_KEY, AGENT_ID, toolCallBody);
const response = await fetch(`https://api.bastio.com/v1/guard/${proxyId}/tool`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
    ...signedHeaders,
  },
  body: JSON.stringify(toolCallBody),
});

Request Headers

HeaderDescription
X-Bastio-Agent-IdAgent ID (bastio_agent_xxx)
X-Bastio-SignatureBase64-encoded Ed25519 signature
X-Bastio-TimestampUnix timestamp (prevents replay attacks)

Signature Verification

Bastio verifies:

  1. Agent exists - Agent ID is registered and not revoked
  2. Signature valid - Signature matches request content
  3. Timestamp fresh - Request is within 5 minutes

Managing Agents

List Agents

curl https://api.bastio.com/v1/guard/{proxyId}/agents \
  -H "Authorization: Bearer YOUR_API_KEY"

Get Agent Details

curl https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId} \
  -H "Authorization: Bearer YOUR_API_KEY"

Update Agent

curl -X PUT https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId} \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Updated Agent Name",
    "trust_level": "elevated"
  }'

Revoke Agent

curl -X DELETE https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId} \
  -H "Authorization: Bearer YOUR_API_KEY"

Revoked agents immediately lose access. All subsequent requests will be rejected.

Key Rotation

Rotate agent keys periodically for security:

curl -X POST https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId}/rotate-key \
  -H "Authorization: Bearer YOUR_API_KEY"

Response includes the new key pair:

{
  "agent_id": "bastio_agent_abc123xyz",
  "public_key": "MCowBQYDK2VwAyEA...",
  "private_key": "MC4CAQAwBQYDK2VwBCIEIP...",
  "previous_key_valid_until": "2024-01-22T10:00:00Z"
}

The previous key remains valid for 7 days to allow graceful migration.

Per-Agent Tool Restrictions

Restrict which tools each agent can use:

Allowed Tools

Only permit specific tools:

curl -X PUT https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId} \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "allowed_tools": ["read_file", "write_file", "search_files"]
  }'

Blocked Tools

Block specific tools:

curl -X PUT https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId} \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "blocked_tools": ["execute_shell", "http_post", "db_admin"]
  }'

Tool restrictions are evaluated before policies:

Agent Tool Call


┌─────────────────┐
│ Agent Allowed/  │
│ Blocked Tools   │ → Block if restricted
└─────────────────┘


┌─────────────────┐
│ Policy Engine   │ → Normal policy evaluation
└─────────────────┘

Session Management

Track agent sessions:

Active Sessions

curl https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId}/sessions \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "sessions": [
    {
      "session_id": "session_abc123",
      "started_at": "2024-01-15T10:00:00Z",
      "last_activity": "2024-01-15T10:30:00Z",
      "tool_calls": 45,
      "risk_score_avg": 0.12
    }
  ]
}

Terminate Session

curl -X POST https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId}/sessions/{sessionId}/terminate \
  -H "Authorization: Bearer YOUR_API_KEY"

Agent Activity Logs

View agent activity:

curl https://api.bastio.com/v1/guard/{proxyId}/agents/{agentId}/activity \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -G \
  -d "start_time=2024-01-01T00:00:00Z" \
  -d "limit=100"
{
  "activity": [
    {
      "timestamp": "2024-01-15T10:30:00Z",
      "tool_name": "read_file",
      "action": "allow",
      "risk_score": 0.1,
      "session_id": "session_abc"
    }
  ]
}

Code Examples

Complete Agent Integration

import os
import httpx
from dataclasses import dataclass

@dataclass
class BastioAgent:
    agent_id: str
    private_key: str
    proxy_id: str
    api_key: str

    async def validate_tool(self, session_id: str, tool_call: dict) -> dict:
        """Validate a tool call with agent authentication."""

        body = {
            "session_id": session_id,
            "tool_call": tool_call
        }

        # Sign request
        headers = sign_request(self.private_key, body)
        headers["Authorization"] = f"Bearer {self.api_key}"
        headers["Content-Type"] = "application/json"

        async with httpx.AsyncClient() as client:
            response = await client.post(
                f"https://api.bastio.com/v1/guard/{self.proxy_id}/tool",
                headers=headers,
                json=body
            )
            return response.json()

# Initialize agent
agent = BastioAgent(
    agent_id=os.environ["BASTIO_AGENT_ID"],
    private_key=os.environ["BASTIO_AGENT_PRIVATE_KEY"],
    proxy_id=os.environ["BASTIO_PROXY_ID"],
    api_key=os.environ["BASTIO_API_KEY"]
)

# Use in your agent loop
async def execute_tool_with_validation(session_id: str, tool_call: dict):
    result = await agent.validate_tool(session_id, tool_call)

    if result["action"] == "allow":
        return await execute_tool(tool_call)
    else:
        return f"Tool blocked: {result['message']}"
class BastioAgent {
  constructor(
    private agentId: string,
    private privateKey: string,
    private proxyId: string,
    private apiKey: string
  ) {}

  async validateTool(sessionId: string, toolCall: object): Promise<any> {
    const body = {
      session_id: sessionId,
      tool_call: toolCall,
    };

    const signedHeaders = signRequest(this.privateKey, this.agentId, body);

    const response = await fetch(
      `https://api.bastio.com/v1/guard/${this.proxyId}/tool`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${this.apiKey}`,
          'Content-Type': 'application/json',
          ...signedHeaders,
        },
        body: JSON.stringify(body),
      }
    );

    return response.json();
  }
}

// Initialize
const agent = new BastioAgent(
  process.env.BASTIO_AGENT_ID!,
  process.env.BASTIO_AGENT_PRIVATE_KEY!,
  process.env.BASTIO_PROXY_ID!,
  process.env.BASTIO_API_KEY!
);

// Use in your agent
async function executeWithValidation(sessionId: string, toolCall: object) {
  const result = await agent.validateTool(sessionId, toolCall);

  if (result.action === 'allow') {
    return await executeTool(toolCall);
  } else {
    return `Tool blocked: ${result.message}`;
  }
}

Best Practices

Next Steps