# Policy attributes and effects

Match the context the integration actually sends, with explicit surface and trigger scope.

Updated: 2026-09-22

Canonical: https://docs.kastra.ai/reference/policy-attributes

## Read a real request first

A rule can be valid and still never match the intended action. Inspect an observed decision's model, source, tool, target, environment, and attributes before choosing conditions. Do not invent a tool spelling from an agent's marketing name.

## Common context

| Context | How to use it |
| --- | --- |
| `x-kastra-attr-tool` | Canonical tool name supplied by a hook or gateway |
| `x-kastra-attr-tool-input` | Tool arguments/text in the integration's supported representation |
| File-path attributes | Prefer declared structured targets; inspect the actual key/value from your hook |
| `model` / model prefix | Scope to the integration's actual model identifier |
| `environment` | Policy scope uses the environment contract, often a name |
| `principal` / customer | Distinguish caller claims from server-bound identity |
| Proxy output fields | Use the supported output tool-call schema and post-inference trigger |

Supported ingress canonicalizes aliases, but this does not make all surfaces semantically identical. A field available to one hook may be absent on another. Unknown or contradictory attribute spellings must not silently become an assumed match.

## Effects and triggers

Deny and HOLD govern supported pre-action flows. Post-action hooks record events after execution. Proxy post-inference rules evaluate supported model output before the application dispatches tools. Content scan, hide, rate-limit, and spend-cap effects have their own surface and entitlement limits.

## Pattern boundaries

String matching does not parse shell semantics. A mention of a protected path is not necessarily a mutation of that path. Prefer structured file/tool targets where available, preserve editor exemptions, and test both a known blocked case and a benign near-match with the actual evaluator.

## Verify a policy change

Check the resulting decision's matched rule, policy revision, and outcome. Repeat in the intended environment and integration. Do not equate a generated rule's self-test with exhaustive threat detection.

## Policy file structure

A `.kastra` file wraps the policy in `apiVersion: kastra/v1`, `kind: Policy`, `metadata` and `spec`. `metadata.name` identifies the policy; `metadata.environment` is an environment **name**. `spec.deny` contains rules, including effects other than denial. `spec.default_decision` is `allow` or `deny` and applies when no applicable classic rule decides the request.

```yaml
apiVersion: kastra/v1
kind: Policy
metadata:
  name: review-docs-demo
  environment: docs-test
spec:
  default_decision: allow
  deny:
    - reason: Review the documentation demo action
      jurisdiction: "*"
      model_prefix: "*"
      priority: 100
      trigger: pre_inference
      effect: hold
      hold_timeout_seconds: 300
      hold_on_timeout: deny
      conditions:
        - attribute: x-kastra-attr-tool
          operator: eq
          value: Bash
        - attribute: x-kastra-attr-tool-input
          operator: contains
          value: kastra-docs-hold
```

All conditions on a rule must match, along with its top-level scope. Classic rules are evaluated by ascending `priority`; the first applicable decision wins. At equal priority, `hide` precedes other effects; otherwise source order is preserved. Use distinct priorities when ordering matters. Content-scan rules run through the dedicated scanning path, so this ordering is not a way to bypass a content guardrail.

`enabled: false` disables a rule. `trigger` defaults to `pre_inference`; use `post_inference` only on a surface that supplies supported output context. `required_attributes` at the spec level declares attributes the policy requires. [Validate the file before applying it](https://docs.kastra.ai/policies/policy-as-code).

## Condition operators

Condition values are strings, including numeric operands and comma-separated lists. Use canonical attribute names such as `x-kastra-attr-tool`.

| Operator | Match | Example value |
| --- | --- | --- |
| `eq` | Case-insensitive equality | `Bash` |
| `neq` | Case-insensitive inequality | `Read` |
| `in` | Equals an item in a comma-separated list, case-insensitive | `Bash,Write,Edit` |
| `not_in` | Equals none of those items, case-insensitive | `Read,Glob,Grep` |
| `contains` | Case-insensitive substring | `kastra-docs-deny` |
| `contains_any` | Contains any comma-separated substring, case-insensitive | `password,secret` |
| `matches` | Go regular expression; case-sensitive unless the pattern opts in | `(?i)kastra-docs-(deny\|hold)` |
| `lt` | Numeric less than | `10` |
| `lte` | Numeric less than or equal | `10` |
| `gt` | Numeric greater than | `100` |
| `gte` | Numeric greater than or equal | `100` |
| `exists` | Attribute key is present, even if its value is empty | Omit `value` |
| `not_exists` | Attribute key is absent | Omit `value` |

A missing attribute fails every operator except `not_exists`, including `neq` and `not_in`. Numeric comparisons fail when either value cannot be parsed as a number. List items are trimmed; comma is the separator, not an escaped literal. Regex lookarounds and backreferences are not supported; invalid patterns fail validation.

## Effect configuration

| Effect | Configuration and behavior |
| --- | --- |
| `deny` | Blocks a matching action; default effect when omitted |
| `hold` | Pauses for approval; `hold_timeout_seconds` defaults to 300, maximum 3,600; `hold_on_timeout` is `deny` by default or explicit `allow` |
| `monitor` | Allows and flags a match for review; does not block it |
| `hide` | Removes a tool from supported MCP discovery and denies a direct call |
| `redact` | Requires `content_scan`; masks matched spans on supported proxy content paths |
| `rate_limit` | `rate_limit.max_requests`, `window_seconds`, and optional `on_exceed` (`deny` or `hold`) |
| `spend_cap` | `spend_cap.max_cost_micros`, `window_seconds`, and optional `on_exceed`; estimated USD cost on supported BYOK proxy requests |

For metered effects, `scope_keys` can contain `tenant`, `environment`, `principal`, `api_key`, `model_prefix`, `customer_id`, and `rule`. The default scope is tenant, environment, and rule. A million USD micros represents one US dollar. Review [limits and spending controls](https://docs.kastra.ai/evidence/limits) for enforcement boundaries.

A HOLD can also set `requires_segregation_of_duties: true` to prevent the triggering user from approving their own action. Host wait time and checkpoint expiry are separate; see [approvals](https://docs.kastra.ai/approvals/overview).

Content scanning uses `content_scan.scanner`, `target` (`prompt` or `output`), optional `categories`, `min_confidence`, and `min_count`. Choose the supported scanner/category combinations from [content guardrails](https://docs.kastra.ai/proxy/content-guardrails), and test the complete integration.
