> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontext.security/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom policies

> Write your own rule when no preset covers what you want to block.

Open **New policy**, then **Write your own**. Choose **A Bash command** to match a program or command text. Choose **A Cedar rule** for other rules. Click **Write**. Kontext gives each custom policy one rule and its own row in **Observing**, like a preset.

## Block a Bash command

Fill in **Program**, **Command contains**, or both. For example, use `terraform` and `apply`. Enter a **Name** and set **Agents** and **Endpoints**. Kontext writes a Cedar forbid for shell commands that match the program and text in the tool input. Expand **Cedar** to read the rule, then click **Add**.

Kontext gives the rule an ID of `custom:bash:` followed by a slug of its name. To change the program, text or name, open the row and click **Edit rule**. You cannot edit a Bash rule's Cedar.

## Write a Cedar rule

Write one Cedar policy. The editor validates it as you type. Kontext sets its ID to `custom:cedar:` followed by a slug of the **Name**. The name is also the rule's description. Set its scope with **Agents** and **Endpoints**. You cannot scope a rule to endpoints if it already constrains `principal`.

This rule blocks `terraform apply`:

```cedar theme={"system"}
forbid (
  principal,
  action == Kontext::Action::"ToolUse",
  resource == Kontext::Tool::"shell"
)
when {
  context has shell &&
  context.shell.program == "terraform" &&
  context.inputJson like "*apply*"
};
```

The rule uses `like "*apply*"` to match text anywhere in the tool input. It also blocks commands that only mention `apply` in an argument. Check the sample calls in **Observing** before you enforce it.

## Request model

Kontext converts each tool call into a Cedar request:

| Field                | Value                                                                                                                                                                                                           |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `principal`          | The `Kontext::Endpoint` with the endpoint's ID. Use the **Endpoints** field to set it.                                                                                                                          |
| `action`             | `Kontext::Action::"ToolUse"`                                                                                                                                                                                    |
| `resource`           | Shell commands use `Kontext::Tool::"shell"`. Recognized GitHub MCP tools use `Kontext::Tool::"github-mcp/<tool>"`. Other tools keep the name the agent reports, such as `Write`, `Read` or `mcp__server__tool`. |
| `context.agent`      | Claude Code and Claude Cowork use `Kontext::Agent::"anthropic-claude-code"`. Codex uses `Kontext::Agent::"openai-codex"`.                                                                                       |
| `context.inputJson`  | The tool input as a JSON string.                                                                                                                                                                                |
| `context.session.id` | The agent's session ID.                                                                                                                                                                                         |
| `context.shell`      | `program`, `facts`, `features` and `parseComplete` for shell commands.                                                                                                                                          |

The schema makes `context.shell` optional. Check `context has shell` before you read it. Preset conditions also match requests that lack the attribute. Open a preset's drawer to read its Cedar and the facts it uses, such as `github/force-push=true`.

Kontext creates one request per command in a compound command such as `cd repo && git push --force`. A Deny from any request blocks the whole tool call.

## Test before you enforce

Kontext replays recorded calls to count matches for observing policies. Rules that read `context.inputJson` need full tool input without redaction for replay. Set payload capture in **Settings**. See [data collection](/deploy/data-collection).

Kontext validates syntax and schema, but you must review sample calls to check that the rule matches what you intend. To manage custom rules from a script, use `POST /policy/actions` in the [policy API](/api/policies).
