Documentation Index
Fetch the complete documentation index at: https://aarm.dev/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This guide walks through implementing basic AARM patterns. By the end, you’ll have:
- A policy that blocks sensitive actions
- Action interception and evaluation
- Basic receipt generation
Prerequisites
- An AI agent that invokes tools (LangChain, OpenAI Agents, custom)
- Python 3.10+ or Node.js 18+
- 30 minutes
Step 1: Define Your First Policy
Create a policy file that blocks external email with sensitive data:
# policies/email-policy.yaml
policy:
id: block-pii-external
version: "1.0"
rules:
- id: rule-001
name: block-sensitive-external-email
match:
tool: email
operation: send
parameters:
to: { external: true }
context:
data_classification: [PII, CONFIDENTIAL]
action: DENY
reason: "Cannot send sensitive data to external recipients"
Step 2: Create the Policy Engine
A minimal policy engine that evaluates actions against rules:
# aarm/policy_engine.py
from dataclasses import dataclass
from typing import Literal
import yaml
@dataclass
class Decision:
result: Literal["ALLOW", "DENY", "MODIFY", "STEP_UP"]
policy_id: str | None = None
reason: str | None = None
class PolicyEngine:
def __init__(self, policy_path: str):
with open(policy_path) as f:
self.policy = yaml.safe_load(f)
def evaluate(self, action: dict) -> Decision:
for rule in self.policy.get("rules", []):
if self._matches(rule, action):
return Decision(
result=rule["action"],
policy_id=rule["id"],
reason=rule.get("reason")
)
return Decision(result="ALLOW")
def _matches(self, rule: dict, action: dict) -> bool:
match = rule.get("match", {})
if match.get("tool") and match["tool"] != action.get("tool"):
return False
if match.get("operation") and match["operation"] != action.get("operation"):
return False
# Add parameter and context matching as needed
return True
Step 3: Create the AARM Hook
Wrap tool calls with AARM enforcement:
# aarm/hook.py
from functools import wraps
from datetime import datetime
import uuid
from .policy_engine import PolicyEngine, Decision
engine = PolicyEngine("policies/email-policy.yaml")
def aarm_protected(tool_name: str):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Build action
action = {
"action_id": str(uuid.uuid4()),
"timestamp": datetime.utcnow().isoformat(),
"tool": tool_name,
"operation": func.__name__,
"parameters": kwargs,
"identity": get_current_identity(),
"context": get_current_context()
}
# Evaluate policy
decision = engine.evaluate(action)
# Enforce
if decision.result == "DENY":
emit_receipt(action, decision, None)
raise PermissionError(f"Action denied: {decision.reason}")
# Execute
result = func(*args, **kwargs)
# Record
emit_receipt(action, decision, result)
return result
return wrapper
return decorator
def emit_receipt(action, decision, result):
receipt = {
"receipt_id": str(uuid.uuid4()),
"action": action,
"decision": {
"result": decision.result,
"policy_id": decision.policy_id,
"reason": decision.reason
},
"result": result,
"timestamp": datetime.utcnow().isoformat()
}
# Write to receipt store (file, database, etc.)
print(f"RECEIPT: {receipt}")
Apply the decorator to your tool functions:
# tools/email.py
from aarm.hook import aarm_protected
@aarm_protected("email")
def send(to: str, subject: str, body: str):
"""Send an email."""
# Your actual email sending logic
return {"status": "sent", "to": to}
Step 5: Test It
# Test allowed action
send(to="colleague@company.com", subject="Report", body="See attached")
# → Executes successfully, receipt generated
# Test denied action
send(to="external@gmail.com", subject="Data", body="Customer list...")
# → PermissionError: Action denied: Cannot send sensitive data to external recipients
Next Steps
Write Your First Policy
Deep dive into policy syntax and patterns
MCP Gateway Pattern
Network-level enforcement for MCP tools