Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.swarmd.ai/llms.txt

Use this file to discover all available pages before exploring further.

Policy Configuration

Policy groups control what flows through the relay. Each policy group is a Rego file (Open Policy Agent’s policy language) that defines which middleware rules to apply. Rego gives you conditional logic, time-based gating, and the ability to combine multiple rules in a single group. Policy groups are applied through a two-step process:
  1. Create a policy group with Rego source that defines the middleware rules
  2. Create a policy binding that attaches the group to a tenant, agent, or subscription

How the Middleware Pipeline Works

Source Agent -> [REQUEST_FROM_SOURCE] -> Relay -> [REQUEST_TO_SINK] -> Sink Agent
                                                                        |
Source Agent <- [RESPONSE_TO_SOURCE] <- Relay <- [RESPONSE_FROM_SINK] <- Sink Agent
Every middleware rule specifies which legs of this flow it applies to via enabledLegs:
LegDirectionExample use
requestFromSourceSource -> RelayScan outgoing requests for PII before they leave
requestToSinkRelay -> SinkRestrict which skills can be invoked on the target agent
responseFromSinkSink -> RelayDetect sensitive data in agent responses
responseToSourceRelay -> SourceMask data before returning to the caller
At least one leg must be enabled. The API will reject a policy where all four legs are false.

How Evaluation Works

When a message flows through the relay:
  1. The system finds all active bindings that match the current tenant, agent, and subscription
  2. For each binding, the associated policy group’s Rego file is evaluated with the request context as input
  3. The Rego file returns a list of middleware configs to execute
  4. Each middleware config is instantiated and executed in the specified order
  5. If any middleware returns BLOCK, the message is rejected
Bindings are evaluated in priority order (lower number = higher priority). Within a group, middlewares are ordered by their order field.

Rego File Structure

Every policy group’s Rego file must follow this structure:
package swarmd.policy

import rego.v1

middlewares contains {
    "name": "Human-readable name for this rule",
    "type": "MIDDLEWARE_TYPE",
    "order": 0,
    "config": {
        "type": "MIDDLEWARE_TYPE",
        ...type-specific fields...
    }
}
FieldRequiredDescription
nameYesA human-readable name for this rule (shown in audit logs)
typeYesOne of: SKILL_RESTRICTION, REGEX_DETECTION, PRESIDIO_DETECTION, COMPREHEND_DETECTION, RATE_LIMIT, HITL
orderYesEvaluation order within the group (lower = first)
configYesType-specific configuration (see sections below)
config.typeYesMust match the outer type field
config.enabledLegsYesWhich relay legs this rule applies to

Middleware Types

Skill Restriction

Control which skills an agent is allowed to use. You can allowlist or blocklist skills by ID, name, or tag.
package swarmd.policy

import rego.v1

middlewares contains {
    "name": "Block filesystem access",
    "type": "SKILL_RESTRICTION",
    "order": 0,
    "config": {
        "type": "SKILL_RESTRICTION",
        "mode": "BLOCKLIST",
        "skillNames": ["read_file", "write_file", "delete_file"],
        "skillIds": [],
        "skillTags": ["filesystem"],
        "action": "BLOCK",
        "enabledLegs": {
            "requestFromSource": true,
            "requestToSink": true,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
}
FieldRequiredDescription
modeYesALLOWLIST (only these skills permitted) or BLOCKLIST (these skills denied)
actionYesLOG, WARN, or BLOCK
skillIdsNoSet of skill IDs to match
skillNamesNoSet of skill names to match
skillTagsNoSet of skill tags to match
At least one of skillIds, skillNames, or skillTags must be non-empty. Use ALLOWLIST mode for high-security environments where agents should only use explicitly approved skills.

Regex Detection

Match message content against a regular expression.
package swarmd.policy

import rego.v1

middlewares contains {
    "name": "Block AWS access keys",
    "type": "REGEX_DETECTION",
    "order": 0,
    "config": {
        "type": "REGEX_DETECTION",
        "pattern": {
            "regex": "AKIA[0-9A-Z]{16}",
            "action": "BLOCK"
        },
        "enabledLegs": {
            "requestFromSource": true,
            "requestToSink": true,
            "responseFromSink": true,
            "responseToSource": true
        }
    }
}
FieldRequiredDescription
pattern.regexYesJava-compatible regular expression (validated at creation time)
pattern.actionYesLOG, MASK, BLOCK, WARN, or HUMAN_REVIEW_REQUIRED

PII Detection (Presidio)

Detect personally identifiable information using Microsoft Presidio.
package swarmd.policy

import rego.v1

middlewares contains {
    "name": "Mask credit card numbers",
    "type": "PRESIDIO_DETECTION",
    "order": 0,
    "config": {
        "type": "PRESIDIO_DETECTION",
        "entity": {
            "type": "CREDIT_CARD",
            "scoreThreshold": 0.8,
            "action": "MASK"
        },
        "enabledLegs": {
            "requestFromSource": true,
            "requestToSink": true,
            "responseFromSink": true,
            "responseToSource": true
        }
    }
}
FieldRequiredDescription
entity.typeYesPresidio entity type (e.g. CREDIT_CARD, US_SSN, EMAIL_ADDRESS, PERSON, PHONE_NUMBER)
entity.scoreThresholdYesConfidence threshold between 0.0 and 1.0
entity.actionYesLOG, MASK, BLOCK, WARN, or HUMAN_REVIEW_REQUIRED
Entity TypeDescription
CREDIT_CARDCredit card numbers
CRYPTOCryptocurrency wallet addresses
EMAIL_ADDRESSEmail addresses
IBAN_CODEInternational bank account numbers
IP_ADDRESSIP addresses
LOCATIONPhysical locations
PERSONPerson names
PHONE_NUMBERPhone numbers
MEDICAL_LICENSEMedical licence numbers
US_BANK_NUMBERUS bank account numbers
US_DRIVER_LICENSEUS driver licence numbers
US_ITINUS Individual Taxpayer Identification Numbers
US_PASSPORTUS passport numbers
US_SSNUS Social Security Numbers
UK_NHSUK National Health Service numbers
ES_NIFSpanish tax identification numbers
IT_FISCAL_CODEItalian fiscal codes
IT_DRIVER_LICENSEItalian driver licence numbers
IT_VAT_CODEItalian VAT codes
IT_PASSPORTItalian passport numbers
IT_IDENTITY_CARDItalian identity card numbers
SG_NRIC_FINSingapore NRIC/FIN numbers
AU_ABNAustralian Business Numbers
AU_ACNAustralian Company Numbers
AU_TFNAustralian Tax File Numbers
AU_MEDICAREAustralian Medicare numbers
IN_PANIndian PAN card numbers
IN_AADHAARIndian Aadhaar numbers
IN_VEHICLE_REGISTRATIONIndian vehicle registration numbers

PII Detection (AWS Comprehend)

Detect PII using AWS Comprehend.
package swarmd.policy

import rego.v1

middlewares contains {
    "name": "Block SSN exposure",
    "type": "COMPREHEND_DETECTION",
    "order": 0,
    "config": {
        "type": "COMPREHEND_DETECTION",
        "entity": {
            "type": "SSN",
            "scoreThreshold": 0.9,
            "action": "BLOCK"
        },
        "enabledLegs": {
            "requestFromSource": false,
            "requestToSink": false,
            "responseFromSink": true,
            "responseToSource": true
        }
    }
}
FieldRequiredDescription
entity.typeYesComprehend entity type (e.g. CREDIT_DEBIT_NUMBER, NAME, SSN, EMAIL, ADDRESS)
entity.scoreThresholdYesConfidence threshold between 0.0 and 1.0
entity.actionYesLOG, MASK, BLOCK, WARN, or HUMAN_REVIEW_REQUIRED

Rate Limiting

Throttle request volume. Three strategies are available: Sliding Window:
middlewares contains {
    "name": "100 requests per minute",
    "type": "RATE_LIMIT",
    "order": 0,
    "config": {
        "type": "RATE_LIMIT",
        "strategy": {
            "strategyType": "SLIDING_WINDOW",
            "maxRequests": 100,
            "windowSizeSeconds": 60
        },
        "enabledLegs": {
            "requestFromSource": true,
            "requestToSink": false,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
}
Token Bucket:
"strategy": {
    "strategyType": "TOKEN_BUCKET",
    "bucketCapacity": 100,
    "refillRatePerSecond": 10.0
}
Leaky Bucket:
"strategy": {
    "strategyType": "LEAKY_BUCKET",
    "bucketCapacity": 50,
    "leakRatePerSecond": 5.0
}

Human-in-the-Loop (HITL)

Pause messages for manual human approval before they reach the target agent.
middlewares contains {
    "name": "Require human approval",
    "type": "HITL",
    "order": 0,
    "config": {
        "type": "HITL",
        "enabledLegs": {
            "requestFromSource": false,
            "requestToSink": true,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
}
See the Human-in-the-Loop tutorial for the complete approval workflow.

Conditional Logic with Rego

This is where OPA’s power comes in. Instead of static rules, you can use Rego’s if statements to activate rules conditionally based on the request context, time of day, day of week, and more.

Available Input Context

Every Rego evaluation receives this input:
{
    "tenantId": "uuid",
    "sourceAgentId": "uuid",
    "sourceUserId": "uuid",
    "sinkAgentId": "uuid",
    "timestamp": "2026-04-03T14:30:00Z",
    "timestampNanos": 1775225400000000000
}
FieldTypeDescription
tenantIdUUID stringThe tenant executing the policy
sourceAgentIdUUID stringThe agent sending the message (null for user sources)
sourceUserIdUUID stringThe user sending the message (null for agent sources)
sinkAgentIdUUID stringThe agent receiving the message
timestampRFC 3339 stringEvaluation time (usable with time.parse_rfc3339_ns())
timestampNanosintegerEvaluation time as epoch nanoseconds (usable with time.clock(), time.weekday(), etc.)

Time-Based Activation

Activate rules only during specific time windows using OPA’s built-in time.* functions with input.timestampNanos:
package swarmd.policy

import rego.v1

# HITL review only during business hours (Mon-Fri 09:00-17:00 UTC)
middlewares contains {
    "name": "Business hours HITL",
    "type": "HITL",
    "order": 0,
    "config": {
        "type": "HITL",
        "enabledLegs": {
            "requestFromSource": false,
            "requestToSink": true,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
} if {
    hour := time.clock(input.timestampNanos)[0]
    hour >= 9
    hour < 17
    day := time.weekday(input.timestampNanos)
    day != "Saturday"
    day != "Sunday"
}

Combining Always-On and Conditional Rules

package swarmd.policy

import rego.v1

# Always block dangerous tools (no condition)
middlewares contains {
    "name": "Block shell commands",
    "type": "SKILL_RESTRICTION",
    "order": 0,
    "config": {
        "type": "SKILL_RESTRICTION",
        "mode": "BLOCKLIST",
        "skillNames": ["shell_command", "execute_code"],
        "skillIds": [],
        "skillTags": [],
        "action": "BLOCK",
        "enabledLegs": {
            "requestFromSource": true,
            "requestToSink": true,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
}

# Stricter rate limiting outside business hours
middlewares contains {
    "name": "After-hours rate limit",
    "type": "RATE_LIMIT",
    "order": 1,
    "config": {
        "type": "RATE_LIMIT",
        "strategy": {
            "strategyType": "SLIDING_WINDOW",
            "maxRequests": 10,
            "windowSizeSeconds": 60
        },
        "enabledLegs": {
            "requestFromSource": true,
            "requestToSink": false,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
} if {
    hour := time.clock(input.timestampNanos)[0]
    hour < 9
}

middlewares contains {
    "name": "After-hours rate limit",
    "type": "RATE_LIMIT",
    "order": 1,
    "config": {
        "type": "RATE_LIMIT",
        "strategy": {
            "strategyType": "SLIDING_WINDOW",
            "maxRequests": 10,
            "windowSizeSeconds": 60
        },
        "enabledLegs": {
            "requestFromSource": true,
            "requestToSink": false,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
} if {
    hour := time.clock(input.timestampNanos)[0]
    hour >= 17
}

# HITL for all weekend requests
middlewares contains {
    "name": "Weekend HITL",
    "type": "HITL",
    "order": 2,
    "config": {
        "type": "HITL",
        "enabledLegs": {
            "requestFromSource": false,
            "requestToSink": true,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
} if {
    day := time.weekday(input.timestampNanos)
    day == "Saturday"
}

middlewares contains {
    "name": "Weekend HITL",
    "type": "HITL",
    "order": 2,
    "config": {
        "type": "HITL",
        "enabledLegs": {
            "requestFromSource": false,
            "requestToSink": true,
            "responseFromSink": false,
            "responseToSource": false
        }
    }
} if {
    day := time.weekday(input.timestampNanos)
    day == "Sunday"
}
Rego uses AND logic within a single if block (all conditions must be true). For OR logic, define multiple middlewares contains blocks with the same middleware config but different conditions - if any block matches, the middleware is included.

Negation

Apply rules when a condition is NOT met:
# Strict PII scanning outside business hours
middlewares contains {
    "name": "After-hours PII scan",
    "type": "PRESIDIO_DETECTION",
    "order": 0,
    "config": { ... }
} if {
    hour := time.clock(input.timestampNanos)[0]
    not hour >= 9
}

Common Time-Gating Patterns

Business hours only (Mon-Fri 09:00-17:00 UTC):
} if {
    hour := time.clock(input.timestampNanos)[0]
    hour >= 9
    hour < 17
    day := time.weekday(input.timestampNanos)
    day != "Saturday"
    day != "Sunday"
}
Weekdays only:
} if {
    day := time.weekday(input.timestampNanos)
    day != "Saturday"
    day != "Sunday"
}
Date-range / maintenance window:
} if {
    # Active from 1 May 2026 onwards
    input.timestampNanos >= time.parse_rfc3339_ns("2026-05-01T00:00:00Z")
}
} if {
    # Active until end of Q2 2026
    input.timestampNanos < time.parse_rfc3339_ns("2026-07-01T00:00:00Z")
}
Agent-specific conditions:
} if {
    input.sinkAgentId == "550e8400-e29b-41d4-a716-446655440000"
}

Future Possibilities

Rego supports many more patterns that can be used as we enrich the input context:
  • Tenant-specific overrides - different rules per tenant ID
  • Agent metadata matching - rules based on agent properties like name and group
  • Dynamic config values - threshold values computed from input data

Policy Actions

Every detection rule requires an action that determines what happens when a match is found:
ActionBehaviour
LOGRecord the detection in the audit log. Message continues normally.
WARNLog an advisory warning. Message continues normally.
MASKRedact the matched content before forwarding the message.
BLOCKReject the message entirely. The sender receives an error.
HUMAN_REVIEW_REQUIREDPause the message and create a HITL approval request.

Creating a Policy Group

curl -X POST https://api.swarmd.ai/relay/v1/policy-groups \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SWARMD_TOKEN" \
  -d '{
    "name": "PII Protection Suite",
    "description": "Detect and mask common PII types",
    "regoSource": "package swarmd.policy\n\nimport rego.v1\n\nmiddlewares contains {\n    \"name\": \"Mask credit cards\",\n    \"type\": \"PRESIDIO_DETECTION\",\n    \"order\": 0,\n    \"config\": {\n        \"type\": \"PRESIDIO_DETECTION\",\n        \"entity\": {\n            \"type\": \"CREDIT_CARD\",\n            \"scoreThreshold\": 0.8,\n            \"action\": \"MASK\"\n        },\n        \"enabledLegs\": {\n            \"requestFromSource\": true,\n            \"requestToSink\": true,\n            \"responseFromSink\": true,\n            \"responseToSource\": true\n        }\n    }\n}\n\nmiddlewares contains {\n    \"name\": \"Mask SSNs\",\n    \"type\": \"PRESIDIO_DETECTION\",\n    \"order\": 1,\n    \"config\": {\n        \"type\": \"PRESIDIO_DETECTION\",\n        \"entity\": {\n            \"type\": \"US_SSN\",\n            \"scoreThreshold\": 0.7,\n            \"action\": \"MASK\"\n        },\n        \"enabledLegs\": {\n            \"requestFromSource\": true,\n            \"requestToSink\": true,\n            \"responseFromSink\": true,\n            \"responseToSource\": true\n        }\n    }\n}",
    "primaryUserId": "YOUR_USER_ID",
    "secondaryUserId": "BACKUP_USER_ID"
  }'
The Rego source is validated before saving:
  • Rego syntax is checked
  • Each middleware config is validated against its type-specific schema
  • Invalid configs are rejected with detailed error messages

Testing Before Saving

Use the evaluate endpoint to dry-run your Rego without creating a group:
curl -X POST https://api.swarmd.ai/relay/v1/policy-groups/evaluate \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SWARMD_TOKEN" \
  -d '{
    "regoSource": "YOUR_REGO_SOURCE_HERE"
  }'
For time-gated policies, pass a timestamp to test at a specific point in time. When omitted, the current server time is used:
curl -X POST https://api.swarmd.ai/relay/v1/policy-groups/evaluate \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SWARMD_TOKEN" \
  -d '{
    "regoSource": "YOUR_REGO_SOURCE_HERE",
    "timestamp": "2026-04-07T10:30:00Z"
  }'

Policy Bindings

Bindings attach a policy group to a scope. Three levels are available:
LevelScopeUse case
TENANTAll agents in your tenantOrganisation-wide compliance rules
AGENTA specific agentRules for a particular agent regardless of who calls it
SUBSCRIPTIONA specific source -> sink pairFine-grained control over a single communication path
When multiple bindings apply to a message, they are evaluated in priority order (lower number = higher priority). Within each binding’s policy group, middlewares execute in order field sequence. All matching middlewares from all applicable bindings are applied.

Evaluation Order

Binding Priority 0 (TENANT)     -> Group A Rego -> [middleware order 0, 1, 2]
Binding Priority 1 (AGENT)      -> Group B Rego -> [middleware order 0, 1]
Binding Priority 2 (SUBSCRIPTION) -> Group C Rego -> [middleware order 0]
All middlewares from all matching bindings execute in this sequence. If any middleware returns BLOCK, the chain short-circuits and the message is rejected.

Create Bindings

Tenant-level (applies to all agents):
curl -X POST https://api.swarmd.ai/relay/v1/policy-bindings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SWARMD_TOKEN" \
  -d '{
    "groupId": "GROUP_ID",
    "groupVersion": "1.0.0",
    "bindingLevel": "TENANT",
    "priority": 0
  }'
Agent-level (applies to a specific agent):
curl -X POST https://api.swarmd.ai/relay/v1/policy-bindings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SWARMD_TOKEN" \
  -d '{
    "groupId": "GROUP_ID",
    "groupVersion": "1.0.0",
    "bindingLevel": "AGENT",
    "agentId": "AGENT_ID",
    "priority": 10
  }'
Subscription-level (applies to a specific source -> sink path):
curl -X POST https://api.swarmd.ai/relay/v1/policy-bindings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SWARMD_TOKEN" \
  -d '{
    "groupId": "GROUP_ID",
    "groupVersion": "1.0.0",
    "bindingLevel": "SUBSCRIPTION",
    "sourceAgentId": "SOURCE_AGENT_ID",
    "sinkAgentId": "SINK_AGENT_ID",
    "priority": 20
  }'

Versioning

Both policy groups and bindings are versioned using semantic versioning (e.g. 1.0.0, 2.0.0).
  • Updating a group creates a new version. The old version is disabled automatically.
  • Upgrading a binding points it to the latest group version via POST /v1/policy-bindings/{id}/upgrade.
  • Version history is available via GET /v1/policy-groups/{id}/history.

Ownership

Every policy group has a primary owner and an optional secondary owner (escalation contact). Ownership can be transferred via POST /v1/policy-groups/{id}/transfer-ownership.

Example: Full Setup

# 1. Create a policy group with Rego
GROUP_ID=$(curl -s -X POST https://api.swarmd.ai/relay/v1/policy-groups \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SWARMD_TOKEN" \
  -d '{
    "name": "Security Baseline",
    "description": "Block dangerous tools and mask PII",
    "regoSource": "package swarmd.policy\n\nimport rego.v1\n\nmiddlewares contains {\n    \"name\": \"Block shell commands\",\n    \"type\": \"SKILL_RESTRICTION\",\n    \"order\": 0,\n    \"config\": {\n        \"type\": \"SKILL_RESTRICTION\",\n        \"mode\": \"BLOCKLIST\",\n        \"skillNames\": [\"shell_command\", \"execute_code\"],\n        \"skillIds\": [],\n        \"skillTags\": [],\n        \"action\": \"BLOCK\",\n        \"enabledLegs\": {\"requestFromSource\": true, \"requestToSink\": true, \"responseFromSink\": false, \"responseToSource\": false}\n    }\n}\n\nmiddlewares contains {\n    \"name\": \"Mask email addresses\",\n    \"type\": \"PRESIDIO_DETECTION\",\n    \"order\": 1,\n    \"config\": {\n        \"type\": \"PRESIDIO_DETECTION\",\n        \"entity\": {\"type\": \"EMAIL_ADDRESS\", \"scoreThreshold\": 0.7, \"action\": \"MASK\"},\n        \"enabledLegs\": {\"requestFromSource\": true, \"requestToSink\": true, \"responseFromSink\": true, \"responseToSource\": true}\n    }\n}",
    "primaryUserId": "YOUR_USER_ID"
  }' | jq -r '.groupId')

echo "Created group: $GROUP_ID"

# 2. Bind to your tenant
curl -X POST https://api.swarmd.ai/relay/v1/policy-bindings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SWARMD_TOKEN" \
  -d "{
    \"groupId\": \"$GROUP_ID\",
    \"groupVersion\": \"1.0.0\",
    \"bindingLevel\": \"TENANT\",
    \"priority\": 0
  }"
That’s it - two API calls instead of four. The Rego file contains all your rules.

Next Steps

Human-in-the-Loop

Set up manual approval workflows for sensitive operations.

Monitoring & Audit

View policy enforcement events and audit trails.