Loading Gatelion

Policy format

A policy is a JSON document with an ordered list of rules. This page describes every field, how the rules are evaluated, and what the console refuses when you save. For a guided introduction, see Write a policy.

Document shape

JSON
{
  "rules": [
    {
      "id": "large-orders",
      "match": {
        "agents": ["<agent id>"],
        "tools": ["erp.purchase_orders.create"],
        "environments": ["<environment id>"],
        "args": [{ "path": "max_total", "op": "gt", "value": 1000 }]
      },
      "effect": "require_approval",
      "approvers": "staff"
    }
  ]
}

Parsing is strict:

  • Field names are exactly as shown, in lowercase with underscores. An unknown field anywhere is an error.
  • Effects, operators and approvers are strings. Numbers in their place are an error.
  • rules is required. It may be empty, which denies everything.

Each environment has its own policy, saved in versions. See how versions work.

Rule fields

FieldRequiredTypeMeaning
idYesstringA name for the rule, unique in the document. It appears in denials, approval requests and the audit trail, so make it meaningful.
matchYesobjectWhich calls the rule applies to. {} matches every call.
effectYesstringallow, deny or require_approval.
approversNostringWho decides a require_approval call: staff, customer or both. Only allowed on require_approval rules.

Match fields

All match fields are optional. A call matches the rule only if it matches every field that is present.

FieldTypeMatches when
agentsarray of stringsThe calling agent's id is in the list. Ids are shown on Tools & agents → Agents.
toolsarray of stringsThe tool id is in the list, for example erp.purchase_orders.create. Tool ids are shown on Tools & agents → Tools.
environmentsarray of stringsThe environment's id is in the list. Each environment has its own policy, so you rarely need this.
argsarray of conditionsEvery condition holds for the call's arguments. See below.

Lists compare exact, case-sensitive strings; there are no wildcards or patterns. A missing field (or null) matches anything. An empty list ([]) matches nothing, so a rule with "agents": [] never applies.

Argument conditions

Each entry in args is an object:

FieldTypeMeaning
pathstringWhere the value is in the arguments.
opstringThe comparison: eq, in, gt, gte, lt or lte.
valuedepends on opWhat to compare with.

Paths

A path is a list of names separated by dots, for example max_total or delivery.country. A segment that is a number picks an item of an array, counting from 0: lines.0.quantity is the quantity of the first order line. Empty segments, such as a..b or a trailing dot, are refused when you save.

Arguments are named after the API's own fields; see OpenAPI extensions. Conditions see the arguments after Gatelion has checked and normalized them: text is trimmed and in Unicode NFC, and decimals are exact.

Operators

opConsole showsvalue must beTrue when
eqisa string, number, true, false or nullThe argument equals the value.
inis one ofan array of such valuesThe argument equals any item in the array.
gt>a number, or a string holding a decimalThe argument is greater than the value.
gte≥a number, or a string holding a decimalThe argument is greater than or equal to the value.
lt<a number, or a string holding a decimalThe argument is less than the value.
lte≤a number, or a string holding a decimalThe argument is less than or equal to the value.

How values compare:

  • gt, gte, lt and lte compare exact decimals. The argument may be a number or a decimal string such as "1500.25", which is how decimal fields arrive.
  • eq with a number value compares numerically, so {"op": "eq", "value": 1500} matches the decimal "1500.00". Prefer a number value for decimal fields. If the argument isn't a number or a decimal string, the condition can't be checked (see below).
  • eq with a string value compares the exact text, case-sensitively. It never equals a number argument: "5" doesn't match the integer 5.
  • eq with true, false or null matches only that value.
  • in applies the same rules to each item.

A decimal string is plain digits with an optional minus sign and one decimal point: "1000", "-5", "1000.50". Exponents, plus signs and thousands separators aren't decimals.

When a condition can't be checked

A condition can't be checked when the path doesn't exist in the arguments, or when a numeric comparison (gt, gte, lt, lte, or eq and in with number values) meets a value that isn't a number. Such a condition fails closed:

  • In an allow rule, the rule doesn't match, and evaluation goes on with the next rule.
  • In a deny or require_approval rule, the rule matches.

A condition that can be checked and is false never matches, whatever the effect. With several conditions, one false condition means the rule doesn't match.

Effects

effectWhat happens to a matching call
AllowIt runs at once, and the agent gets the result.
DenyIt's refused with policy_denied, and the message names the rule. Nothing is sent.
Needs approvalIt waits for a decision, and the agent gets pending_approval with an execution id.

Approvers

approvers is only allowed on require_approval rules.

approversWho decides
staff (or left out)One approver of the environment, subject to separation of duties.
customerThe customer the outside agent acts for, on a confirmation page after signing in with your login. Your staff can't decide.
bothThe customer and one approver, in either order.

With customer or both, a rejection by anyone rejects the request. A call by one of your own agents that matches a customer or both rule is denied with customer_confirmation_unavailable, because there's no customer to ask.

Evaluation order

For each call, the Gateway checks the rules from the first to the last:

  1. A rule is skipped if agents, tools or environments is present and doesn't contain the call's agent, tool or environment.
  2. Otherwise the args conditions are checked, failing closed as described above.
  3. The first rule that matches decides the call.
  4. If no rule matches, the call is denied with the message "No rule matched (default deny)".

Rule order is the only priority there is. A deny placed after an allow that matches the same call is never reached.

Which tools an agent sees

An agent's tool list contains only the tools it could possibly run. For each enabled tool, Gatelion walks the rules that apply to this agent and tool, in order:

  • The first such rule without argument conditions decides: the tool is visible unless that rule is deny.
  • Before that, an allow or require_approval rule with argument conditions makes the tool visible, because some arguments could pass.
  • A deny rule with argument conditions doesn't decide; the walk goes on.
  • If no rule applies, the tool is hidden.

A hidden tool answers unknown_tool if the agent calls it anyway. Visibility never authorizes a call; every call is evaluated in full.

Validation errors on save

The console checks the document twice. Nothing is saved while either check fails.

In the editor's JSON tab, these stop you from going back to Rules or clicking Review and save:

MessageCause
This isn't valid JSON. Check brackets, quotes and commas.The text isn't JSON.
Each rule needs an id, a match and an effect (allow, deny or require_approval).rules isn't a list, or a rule lacks a text id, a match object or a known effect.

When you save, Gatelion checks the document in full and refuses it with one of these:

MessageCause
Policy is not valid: …An unknown field, a missing required field, a wrong type, or an effect, operator or approvers value that doesn't exist. The rest of the message points to the place.
Rule ids must be non-empty and unique (…)A rule has an empty id, or two rules share one.
Rule "…": approvers only applies to require_approval rules.approvers is set on an allow or deny rule.
Rule "…": invalid argument path "…".A path is empty or has an empty segment.
Rule "…": value for "…" on "…" has the wrong type.For example, in without an array, eq with an object or array, or gt with text that isn't a decimal.

Saving doesn't check that agent, tool or environment ids exist. The editor warns about tool and agent ids it doesn't know, but a misspelled id still saves and never matches, so check rules that don't seem to apply.

A full example

A purchasing setup in an ERP, plus a flight-booking portal used by customers' own agents:

JSON
{
  "rules": [
    {
      "id": "purchasing-agent-suppliers",
      "match": {
        "agents": ["<id of the purchasing agent>"],
        "tools": ["erp.suppliers.list"]
      },
      "effect": "allow"
    },
    {
      "id": "reads",
      "match": { "tools": ["erp.inventory.list", "erp.purchase_orders.get"] },
      "effect": "allow"
    },
    {
      "id": "no-cancelling",
      "match": { "tools": ["erp.purchase_orders.cancel"] },
      "effect": "deny"
    },
    {
      "id": "large-orders",
      "match": {
        "tools": ["erp.purchase_orders.create"],
        "args": [{ "path": "max_total", "op": "gt", "value": 1000 }]
      },
      "effect": "require_approval"
    },
    {
      "id": "orders-main-warehouses",
      "match": {
        "tools": ["erp.purchase_orders.create"],
        "args": [{ "path": "warehouse", "op": "in", "value": ["WH-1", "WH-2"] }]
      },
      "effect": "allow"
    },
    {
      "id": "flight-search",
      "match": { "tools": ["portal.flights.search"] },
      "effect": "allow"
    },
    {
      "id": "expensive-bookings",
      "match": {
        "tools": ["portal.bookings.create"],
        "args": [{ "path": "total_price", "op": "gte", "value": 2000 }]
      },
      "effect": "require_approval",
      "approvers": "both"
    },
    {
      "id": "bookings",
      "match": { "tools": ["portal.bookings.create"] },
      "effect": "require_approval",
      "approvers": "customer"
    }
  ]
}

What it does:

  • Only the purchasing agent may list suppliers. Every agent may read stock and orders.
  • No agent may cancel orders, and the cancel tool is hidden.
  • Orders up to 1000 for warehouses WH-1 and WH-2 run at once. Orders above 1000 wait for an approver. Orders for any other warehouse match no rule and are denied.
  • Customers' agents may search flights freely. Every booking waits for the customer's confirmation, and bookings of 2000 or more also need one of your approvers. expensive-bookings comes first; placed after bookings, it would never be reached.
  • If one of your own agents tried to book, it would be denied with customer_confirmation_unavailable.

Still stuck? Contact support from the console.