Docs/Policy Language

Policy Language

Policies are JSON documents that define what your agents are allowed to do. They are the core enforcement mechanism — everything in PermitNetworks flows through policy evaluation.

Complete Policy Structure

Every field is annotated below. Required fields are marked with *.

JSON
{
  "id": "pol_billing_agent",          // auto-generated if omitted
  "name": "billing-agent-limits",     // * human-readable name
  "priority": 1,                      // lower = evaluated first (default: 100)
  "enabled": true,                    // false = policy is disabled but kept

  "agents": ["billing-bot"],          // null = applies to ALL agents
  "description": "Controls what the billing agent can do",

  "rules": [                          // * at least one rule required
    {
      "action": "payment.create",     // * exact, wildcard, or /regex/
      "resource": "account:*",        // null = matches any resource
      "effect": "allow",              // * "allow" | "deny" | "review" | "log"
      "conditions": {                 // null = no conditions (always matches)
        "context.amount": { "$lte": 5000 },
        "context.currency": { "$in": ["USD", "EUR"] }
      }
    }
  ],

  "rate_limit": {                     // null = no rate limiting
    "per_second": null,
    "per_minute": 60,
    "per_hour": 1000,
    "per_day": null
  },

  "budget": {                         // null = no budget constraint
    "limit": 50000,
    "unit": "USD",
    "period": "monthly",
    "threshold_alert": 0.8           // webhook at 80% consumed
  }
}

Action Matching

The action field in a rule supports three matching modes:

Exact
"payment.create"

Only matches the exact action string.

Namespace wildcard
"payment.*"

Matches any action in the payment namespace.

Global wildcard
"*"

Matches any action. Use carefully — typically for deny-all rules.

Regex
"/^(payment|transfer)\./"

Full regex support. Must be wrapped in forward slashes.

Effects

EffectSDK responseBudget consumed?Description
allowdecision.effect = "allow"On confirm()Action is permitted. Permit token issued.
denydecision.effect = "deny"NeverAction is blocked. No permit token.
reviewdecision.effect = "review"On confirm()Action is permitted but flagged for human review.
logdecision.effect = "log"On confirm()Action is permitted. Extra audit record created.

Condition Operators

Conditions evaluate fields from the context object passed in the authorization request. Use dot-notation to access nested fields: context.user.role.

OperatorMeaningExample
$eqEqual to{ "$eq": "admin" }
$neNot equal to{ "$ne": "guest" }
$gtGreater than{ "$gt": 500 }
$gteGreater than or equal{ "$gte": 1000 }
$ltLess than{ "$lt": 0.7 }
$lteLess than or equal{ "$lte": 5000 }
$inValue in array{ "$in": ["USD", "EUR"] }
$ninValue not in array{ "$nin": ["DELETE", "DROP"] }
$existsField is present{ "$exists": true }
$regexRegex match{ "$regex": "^prod-" }

Multiple conditions on the same rule are combined with AND logic. To express OR, create separate rules with the same effect.

Example Policies

Billing agent: create payments under $5,000

Allow payment creation with a hard dollar cap, then deny everything else.

JSON
{
  "name": "billing-agent-payments",
  "priority": 10,
  "agents": ["billing-bot"],
  "rules": [
    {
      "action": "payment.create",
      "effect": "allow",
      "conditions": {
        "context.amount": { "$lte": 5000 },
        "context.currency": { "$in": ["USD", "EUR", "GBP"] }
      }
    },
    {
      "action": "payment.*",
      "effect": "deny"
    }
  ]
}

Support bot: read customer data, no writes

Explicitly allow read actions, deny all write/delete operations.

JSON
{
  "name": "support-bot-readonly",
  "priority": 20,
  "agents": ["support-agent"],
  "rules": [
    {
      "action": "data.read",
      "resource": "customer:*",
      "effect": "allow"
    },
    {
      "action": "data.read",
      "resource": "order:*",
      "effect": "allow"
    },
    {
      "action": "data.write",
      "effect": "deny"
    },
    {
      "action": "data.delete",
      "effect": "deny"
    }
  ]
}

Block all actions outside business hours

Use a time context passed by your application to restrict access.

JSON
{
  "name": "business-hours-only",
  "priority": 5,
  "rules": [
    {
      "action": "*",
      "effect": "deny",
      "conditions": {
        "context.hour_utc": { "$lt": 8 }
      }
    },
    {
      "action": "*",
      "effect": "deny",
      "conditions": {
        "context.hour_utc": { "$gte": 20 }
      }
    }
  ]
}

// In your application, pass the current UTC hour in context:
// await permit.authorize({
//   action: "data.read",
//   context: { hour_utc: new Date().getUTCHours() }
// })

Large transfers require dual approval

Flag transfers over $10,000 for human review rather than auto-allowing or blocking.

JSON
{
  "name": "large-transfer-review",
  "priority": 8,
  "rules": [
    {
      "action": "payment.create",
      "effect": "review",
      "conditions": {
        "context.amount": { "$gt": 10000 }
      }
    },
    {
      "action": "payment.create",
      "effect": "allow",
      "conditions": {
        "context.amount": { "$lte": 10000 }
      }
    }
  ]
}

Monthly API call budget

Limit a data agent to 10,000 API calls per month with an alert at 80%.

JSON
{
  "name": "data-agent-api-budget",
  "priority": 30,
  "agents": ["data-processor"],
  "rules": [
    {
      "action": "api.*",
      "effect": "allow"
    }
  ],
  "budget": {
    "limit": 10000,
    "unit": "api_calls",
    "period": "monthly",
    "threshold_alert": 0.8
  }
}

Priority and Conflicts

Policies are evaluated in ascending priority order (1 before 100). The first matching rule wins — evaluation stops.

If two rules from different policies match the same request with conflicting effects, the lower-priority-number policy wins. This returns a policy.conflict warning in the response metadata.

If no rule matches at all, the default effect is deny with reason policy.not_found.