YAML Policy Engine
Govrix Scout includes a declarative policy engine that evaluates YAML rules against every request and response that flows through the proxy. Rules combine field conditions using boolean operators and trigger one of three actions: block, tag, or log.
Rule structure
A policy rule has three parts:
rules:
- name: <human-readable rule name>
conditions:
- field: <field>
operator: <operator>
value: <value>
# additional conditions are ANDed together
action: <action>
message: "<optional message for block responses>"All conditions within a single rule are evaluated with logical AND — every condition must match for the action to fire. To express OR logic, write multiple rules.
Fields
| Field | Type | Description |
|---|---|---|
agent_id | string | The agent identifier from the X-Agent-ID header |
model | string | The model name requested (e.g. gpt-4o, claude-3-5-sonnet-20241022) |
token_count | integer | Total tokens in the request + response |
cost_usd | float | Estimated cost of the request in USD |
pii_detected | boolean | Whether the PII scanner found any PII in the payload |
Operators
| Operator | Applies to | Description |
|---|---|---|
eq | string, boolean, integer, float | Equals |
neq | string, boolean, integer, float | Not equals |
gt | integer, float | Greater than |
lt | integer, float | Less than |
contains | string | String contains substring (case-sensitive) |
regex | string | Full regex match against the field value |
Actions
| Action | HTTP effect | Description |
|---|---|---|
block | Request rejected with HTTP 403 before forwarding | Stops the request; returns the rule’s message field in the response body |
tag | Request forwarded normally | Adds a tag to the event record; visible in the audit log |
log | Request forwarded normally | Emits a structured log line at WARN level; no effect on the event |
block is evaluated before the request is forwarded to the upstream LLM. tag and log are evaluated on the response path. A block action consumes zero LLM tokens.
Implementation
| File | Role |
|---|---|
crates/govrix-policy/src/engine.rs | Rule evaluator — 6 operators, 5 fields, 3 actions, 16 unit tests |
Full example policy
policy:
rules:
# Block any request that contains PII
- name: block-pii
conditions:
- field: pii_detected
operator: eq
value: true
action: block
message: "Request blocked: PII detected in payload."
# Block expensive models for agents that aren't whitelisted
- name: block-gpt4-for-standard-agents
conditions:
- field: model
operator: contains
value: "gpt-4"
- field: agent_id
operator: neq
value: "premium-agent"
action: block
message: "GPT-4 class models are restricted to premium agents."
# Tag any request that uses more than 50k tokens
- name: tag-high-token-usage
conditions:
- field: token_count
operator: gt
value: 50000
action: tag
message: "high-token-usage"
# Block any single request that would cost more than $1
- name: block-expensive-requests
conditions:
- field: cost_usd
operator: gt
value: 1.00
action: block
message: "Single request cost exceeds $1.00 limit."
# Log any agent whose name matches an internal pattern
- name: log-internal-agents
conditions:
- field: agent_id
operator: regex
value: "^internal-.*"
action: logApplying policy configuration
Policy rules live in govrix.toml under the [policy] key:
[policy]
rules = [
{ name = "block-pii", conditions = [{ field = "pii_detected", operator = "eq", value = true }], action = "block", message = "PII detected." }
]Alternatively, point to an external YAML file:
[policy]
rules_file = "/etc/govrix/policy.yaml"TOML inline
Inline rules in govrix.toml use TOML array-of-table syntax. They are loaded once at startup. To reload, restart the proxy.
[[policy.rules]]
name = "block-pii"
action = "block"
message = "PII detected in payload."
[[policy.rules.conditions]]
field = "pii_detected"
operator = "eq"
value = trueExecution order
Rules are evaluated in the order they are defined. The first matching block rule short-circuits evaluation — subsequent rules are not checked. For tag and log rules, all matching rules are executed (no short-circuit).
Put block rules before tag and log rules that cover the same conditions. If a log rule fires first, the event is logged before the block rule gets a chance to prevent the upstream call.
Actually: block rules run on the request path (pre-upstream); tag and log rules run on the response path (post-upstream). So a block rule always fires before any tag/log rule for the same request — ordering only matters within the same action type.