| # Policy engine |
|
|
| Gemini CLI includes a powerful policy engine that provides fine-grained control |
| over tool execution. It allows users and administrators to define rules that |
| determine whether a tool call should be allowed, denied, or require user |
| confirmation. |
|
|
| ## Quick start |
|
|
| To create your first policy: |
|
|
| 1. **Create the policy directory** if it doesn't exist: |
|
|
| **macOS/Linux** |
| |
| ```bash |
| mkdir -p ~/.gemini/policies |
| ``` |
| |
| **Windows (PowerShell)** |
| |
| ```powershell |
| New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.gemini\policies" |
| ``` |
| |
| 2. **Create a new policy file** (for example, |
| `~/.gemini/policies/my-rules.toml`). You can use any filename ending in |
| `.toml`; all such files in this directory will be loaded and combined: |
| ```toml |
| [[rule]] |
| toolName = "run_shell_command" |
| commandPrefix = "rm -rf" |
| decision = "deny" |
| priority = 100 |
| ``` |
| 3. **Run a command** that triggers the policy (for example, ask Gemini CLI to |
| `rm -rf /`). The tool will now be blocked automatically. |
| |
| ## Core concepts |
|
|
| The policy engine operates on a set of rules. Each rule is a combination of |
| conditions and a resulting decision. When a large language model wants to |
| execute a tool, the policy engine evaluates all rules to find the |
| highest-priority rule that matches the tool call. |
|
|
| A rule consists of the following main components: |
|
|
| - **Conditions**: Criteria that a tool call must meet for the rule to apply. |
| This can include the tool's name, the arguments provided to it, or the current |
| approval mode. |
| - **Decision**: The action to take if the rule matches (`allow`, `deny`, or |
| `ask_user`). |
| - **Priority**: A number that determines the rule's precedence. Higher numbers |
| win. |
|
|
| For example, this rule will ask for user confirmation before executing any `git` |
| command. |
|
|
| ```toml |
| [[rule]] |
| toolName = "run_shell_command" |
| commandPrefix = "git" |
| decision = "ask_user" |
| priority = 100 |
| ``` |
|
|
| ### Conditions |
|
|
| Conditions are the criteria that a tool call must meet for a rule to apply. The |
| primary conditions are the tool's name and its arguments. |
|
|
| #### Tool Name |
|
|
| The `toolName` in the rule must match the name of the tool being called. For a |
| complete list of built-in tool names, see the |
| [Tools reference](/docs/reference/tools#available-tools). |
|
|
| - **Wildcards**: You can use wildcards to match multiple tools. |
| - `*`: Matches **any tool** (built-in or MCP). |
| - `mcp_server_*`: Matches any tool from a specific MCP server. |
| - `mcp_*_toolName`: Matches a specific tool name across **all** MCP servers. |
| - `mcp_*`: Matches **any tool from any MCP server**. |
|
|
| > **Recommendation:** While FQN wildcards are supported, the recommended |
| > approach for MCP tools is to use the `mcpName` field in your TOML rules. See |
| > [Special syntax for MCP tools](#special-syntax-for-mcp-tools). |
|
|
| #### Arguments pattern |
|
|
| If `argsPattern` is specified, the tool's arguments are converted to a stable |
| JSON string, which is then tested against the provided regular expression. If |
| the arguments don't match the pattern, the rule does not apply. For a list of |
| argument keys available for each tool, see the **Parameters** in the |
| [Tools reference](/docs/reference/tools#available-tools). |
|
|
| #### Execution environment |
|
|
| If `interactive` is specified, the rule will only apply if the CLI's execution |
| environment matches the specified boolean value: |
|
|
| - `true`: The rule applies only in interactive mode. |
| - `false`: The rule applies only in non-interactive (headless) mode. |
|
|
| If omitted, the rule applies to both interactive and non-interactive |
| environments. |
|
|
| ### Decisions |
|
|
| There are three possible decisions a rule can enforce: |
|
|
| - `allow`: The tool call is executed automatically without user interaction. |
| - `deny`: The tool call is blocked and is not executed. For global rules (those |
| without an `argsPattern`), tools that are denied are **completely excluded |
| from the model's memory**. This means the model will not even see the tool as |
| an option, which is more secure and saves context window space. |
| - `ask_user`: The user is prompted to approve or deny the tool call. (In |
| non-interactive mode, this is treated as `deny`.) |
|
|
| <!-- prettier-ignore --> |
| > [!NOTE] |
| > The `deny` decision is the recommended way to exclude tools. The |
| > legacy `tools.exclude` setting in `settings.json` is deprecated in favor of |
| > policy rules with a `deny` decision. |
|
|
| ### Priority system and tiers |
|
|
| > [!WARNING] The **Workspace** tier (project-level policies) is currently |
| > non-functional. Defining policies in a workspace's `.gemini/policies` |
| > directory will not have any effect. See |
| > [issue #18186](https://github.com/google-gemini/gemini-cli/issues/18186). Use |
| > User or Admin policies instead. |
|
|
| The policy engine uses a sophisticated priority system to resolve conflicts when |
| multiple rules match a single tool call. The core principle is simple: **the |
| rule with the highest priority wins**. |
|
|
| To provide a clear hierarchy, policies are organized into three tiers. Each tier |
| has a designated number that forms the base of the final priority calculation. |
|
|
| | Tier | Base | Description | |
| | :-------- | :--- | :-------------------------------------------------------------------------------------------- | |
| | Default | 1 | Built-in policies that ship with Gemini CLI. | |
| | Extension | 2 | Policies defined in extensions. | |
| | Workspace | 3 | **(Currently disabled)** Policies defined in the current workspace's configuration directory. | |
| | User | 4 | Custom policies defined by the user. | |
| | Admin | 5 | Policies managed by an administrator (for example, in an enterprise environment). | |
|
|
| Within a TOML policy file, you assign a priority value from **0 to 999**. The |
| engine transforms this into a final priority using the following formula: |
|
|
| `final_priority = tier_base + (toml_priority / 1000)` |
|
|
| This system guarantees that: |
|
|
| - Admin policies always override User, Workspace, and Default policies (defined |
| in policy TOML files). |
| - User policies override Workspace and Default policies. |
| - Workspace policies override Default policies. |
| - You can still order rules within a single tier with fine-grained control. |
|
|
| For example: |
|
|
| - A `priority: 50` rule in a Default policy TOML becomes `1.050`. |
| - A `priority: 10` rule in a Workspace policy TOML becomes `2.010`. |
| - A `priority: 100` rule in a User policy TOML becomes `3.100`. |
| - A `priority: 20` rule in an Admin policy TOML becomes `4.020`. |
|
|
| ### Approval modes |
|
|
| Approval modes allow the policy engine to apply different sets of rules based on |
| the CLI's operational mode. A rule in a TOML policy file can be associated with |
| one or more modes (for example, `yolo`, `autoEdit`, `plan`). The rule will only |
| be active if the CLI is running in one of its specified modes. If a rule has no |
| modes specified, it is always active. |
|
|
| - `default`: The standard interactive mode where most write tools require |
| confirmation. |
| - `autoEdit`: Optimized for automated code editing; some write tools may be |
| auto-approved. |
| - `plan`: A strict, read-only mode for research and design. See |
| [Customizing Plan Mode Policies](../cli/plan-mode.md#customizing-policies). |
| - `yolo`: A mode where all tools are auto-approved (use with extreme caution). |
|
|
| To maintain the integrity of Plan Mode as a safe research environment, |
| persistent tool approvals are context-aware. When you select **"Allow for all |
| future sessions"**, the policy engine explicitly includes the current mode and |
| all more permissive modes in the hierarchy (`plan` < `default` < `autoEdit` < |
| `yolo`). |
|
|
| - **Approvals in `plan` mode**: These represent an intentional choice to trust a |
| tool globally. The resulting rule explicitly includes all modes (`plan`, |
| `default`, `autoEdit`, and `yolo`). |
| - **Approvals in other modes**: These only apply to the current mode and those |
| more permissive. For example: |
| - An approval granted in **`default`** mode applies to `default`, `autoEdit`, |
| and `yolo`. |
| - An approval granted in **`autoEdit`** mode applies to `autoEdit` and `yolo`. |
| - An approval granted in **`yolo`** mode applies only to `yolo`. This ensures |
| that trust flows correctly to more permissive environments while maintaining |
| the safety of more restricted modes like `plan`. |
| |
| ## Rule matching |
|
|
| When a tool call is made, the engine checks it against all active rules, |
| starting from the highest priority. The first rule that matches determines the |
| outcome. |
|
|
| A rule matches a tool call if all of its conditions are met: |
|
|
| 1. **Tool name**: The `toolName` in the TOML rule must match the name of the |
| tool being called. |
| - **Wildcards**: You can use wildcards like `*`, `mcp_server_*`, or |
| `mcp_*_toolName` to match multiple tools. See [Tool Name](#tool-name) for |
| details. |
| 2. **Arguments pattern**: If `argsPattern` is specified, the tool's arguments |
| are converted to a stable JSON string, which is then tested against the |
| provided regular expression. If the arguments don't match the pattern, the |
| rule does not apply. |
| |
| ## Configuration |
|
|
| Policies are defined in `.toml` files. The CLI loads these files from Default, |
| User, and (if configured) Admin directories. |
|
|
| ### Policy locations |
|
|
| | Tier | Type | Location | |
| | :------------ | :----- | :------------------------------------------------------- | |
| | **User** | Custom | `~/.gemini/policies/*.toml` | |
| | **Workspace** | Custom | **(Disabled)** `$WORKSPACE_ROOT/.gemini/policies/*.toml` | |
| | **Admin** | System | _See below (OS specific)_ | |
|
|
| #### System-wide policies (Admin) |
|
|
| Administrators can enforce system-wide policies (Tier 4) that override all user |
| and default settings. These policies can be loaded from standard system |
| locations or supplemental paths. |
|
|
| ##### Standard Locations |
|
|
| These are the default paths the CLI searches for admin policies: |
|
|
| | OS | Policy Directory Path | |
| | :---------- | :------------------------------------------------ | |
| | **Linux** | `/etc/gemini-cli/policies` | |
| | **macOS** | `/Library/Application Support/GeminiCli/policies` | |
| | **Windows** | `C:\ProgramData\gemini-cli\policies` | |
|
|
| ##### Supplemental Admin Policies |
|
|
| Administrators can also specify supplemental policy paths using: |
|
|
| - The `--admin-policy` command-line flag. |
| - The `adminPolicyPaths` setting in a system settings file. |
|
|
| These supplemental policies are assigned the same **Admin** tier (Base 4) as |
| policies in standard locations. |
|
|
| **Security Guard**: Supplemental admin policies are **ignored** if any `.toml` |
| policy files are found in the standard system location. This prevents flag-based |
| overrides when a central system policy has already been established. |
|
|
| #### Security Requirements |
|
|
| To prevent privilege escalation, the CLI enforces strict security checks on the |
| **standard system policy directory**. If checks fail, the policies in that |
| directory are **ignored**. |
|
|
| - **Linux / macOS:** Must be owned by `root` (UID 0) and NOT writable by group |
| or others (for example, `chmod 755`). |
| - **Windows:** Must be in `C:\ProgramData`. Standard users (`Users`, `Everyone`) |
| must NOT have `Write`, `Modify`, or `Full Control` permissions. If you see a |
| security warning, use the folder properties to remove write permissions for |
| non-admin groups. You may need to "Disable inheritance" in Advanced Security |
| Settings. |
|
|
| <!-- prettier-ignore --> |
| > [!NOTE] |
| > Supplemental admin policies (provided via `--admin-policy` or |
| > `adminPolicyPaths` settings) are **NOT** subject to these strict ownership |
| > checks, as they are explicitly provided by the user or administrator in their |
| > current execution context. |
|
|
| ### TOML rule schema |
|
|
| This section describes the fields available in a TOML policy rule. |
|
|
| For valid built-in `toolName` values and their argument structures (used by |
| `argsPattern`), see the |
| [Tools reference](/docs/reference/tools#available-tools). |
|
|
| ```toml |
| [[rule]] |
| # A unique name for the tool, or an array of names. |
| toolName = "run_shell_command" |
| |
| # (Optional) The name of a subagent. If provided, the rule only applies to tool |
| # calls made by this specific subagent. |
| subagent = "codebase_investigator" |
| |
| # (Optional) The name of an MCP server. Can be combined with toolName |
| # to form a composite FQN internally like "mcp_mcpName_toolName". |
| mcpName = "my-custom-server" |
| |
| # (Optional) Metadata hints provided by the tool. A rule matches if all |
| # key-value pairs provided here are present in the tool's annotations. |
| toolAnnotations = { readOnlyHint = true } |
| |
| # (Optional) A regex to match against the tool's arguments. |
| argsPattern = '"command":"(git|npm)' |
| |
| # (Optional) A string or array of strings that a shell command must start with. |
| # This is syntactic sugar for `toolName = "run_shell_command"` and an |
| # `argsPattern`. |
| commandPrefix = "git" |
| |
| # (Optional) A regex to match against the entire shell command. |
| # This is also syntactic sugar for `toolName = "run_shell_command"`. |
| # Note: This pattern is tested against the JSON representation of the arguments |
| # (e.g., `{"command":"<your_command>"}`). Because it prepends `"command":"`, |
| # it effectively matches from the start of the command. |
| # Anchors like `^` or `$` apply to the full JSON string, |
| # so `^` should usually be avoided here. |
| # You cannot use commandPrefix and commandRegex in the same rule. |
| commandRegex = "git (commit|push)" |
| |
| # The decision to take. Must be "allow", "deny", or "ask_user". |
| decision = "ask_user" |
| |
| # The priority of the rule, from 0 to 999. |
| priority = 10 |
| |
| # (Optional) A custom message to display when a tool call is denied by this |
| # rule. This message is returned to the model and user, |
| # useful for explaining *why* it was denied. |
| denyMessage = "Deletion is permanent" |
| |
| # (Optional) An array of approval modes where this rule is active. |
| # If omitted or empty, the rule applies to all modes. |
| modes = ["default", "autoEdit", "yolo"] |
| |
| # (Optional) A boolean to restrict the rule to interactive (true) or |
| # non-interactive (false) environments. |
| # If omitted, the rule applies to both. |
| interactive = true |
| |
| # (Optional) If true, lets shell commands use redirection operators |
| # (>, >>, <, <<, <<<). By default, the policy engine asks for confirmation |
| # when redirection is detected, even if a rule matches the command. |
| # This permission is granular; it only applies to the specific rule it's |
| # defined in. In chained commands (e.g., cmd1 > file && cmd2), each |
| # individual command rule must permit redirection if it's used. |
| allowRedirection = true |
| ``` |
|
|
| ### Using arrays (lists) |
|
|
| To apply the same rule to multiple tools or command prefixes, you can provide an |
| array of strings for the `toolName` and `commandPrefix` fields. |
|
|
| **Example:** |
|
|
| This single rule will apply to both the `write_file` and `replace` tools. |
|
|
| ```toml |
| [[rule]] |
| toolName = ["write_file", "replace"] |
| decision = "ask_user" |
| priority = 10 |
| ``` |
|
|
| ### Special syntax for `run_shell_command` |
|
|
| To simplify writing policies for `run_shell_command`, you can use |
| `commandPrefix` or `commandRegex` instead of the more complex `argsPattern`. |
| These are policy-rule shorthands, not arguments of the `run_shell_command` tool |
| itself. For the tool's invocation arguments, see [Shell tool](/docs/tools/shell) |
| and [Tools reference](/docs/reference/tools#available-tools). |
|
|
| - `commandPrefix`: Matches if the `command` argument starts with the given |
| string. |
| - `commandRegex`: Matches if the `command` argument matches the given regular |
| expression. |
|
|
| **Example:** |
|
|
| This rule will ask for user confirmation before executing any `git` command. |
|
|
| ```toml |
| [[rule]] |
| toolName = "run_shell_command" |
| commandPrefix = "git" |
| decision = "ask_user" |
| priority = 100 |
| ``` |
|
|
| ### Special syntax for MCP tools |
|
|
| You can create rules that target tools from Model Context Protocol (MCP) servers |
| using the `mcpName` field. **This is the recommended approach** for defining MCP |
| policies, as it is much more robust than manually writing Fully Qualified Names |
| (FQNs) or string wildcards. |
|
|
| <!-- prettier-ignore --> |
| > [!WARNING] |
| > Do not use underscores (`_`) in your MCP server names (for example, use |
| > `my-server` rather than `my_server`). The policy parser splits Fully Qualified |
| > Names (`mcp_server_tool`) on the _first_ underscore following the `mcp_` |
| > prefix. If your server name contains an underscore, the parser will |
| > misinterpret the server identity, which can cause wildcard rules and security |
| > policies to fail silently. |
| |
| **1. Targeting a specific tool on a server** |
| |
| Combine `mcpName` and `toolName` to target a single operation. When using |
| `mcpName`, the `toolName` field should strictly be the simple name of the tool |
| (for example, `search`), **not** the Fully Qualified Name (for example, |
| `mcp_server_search`). |
| |
| ```toml |
| # Allows the `search` tool on the `my-jira-server` MCP |
| [[rule]] |
| mcpName = "my-jira-server" |
| toolName = "search" |
| decision = "allow" |
| priority = 200 |
| ``` |
| |
| **2. Targeting all tools on a specific server** |
| |
| Specify only the `mcpName` to apply a rule to every tool provided by that |
| server. |
| |
| **Note:** This applies to all decision types (`allow`, `deny`, `ask_user`). |
|
|
| ```toml |
| # Denies all tools from the `untrusted-server` MCP |
| [[rule]] |
| mcpName = "untrusted-server" |
| decision = "deny" |
| priority = 500 |
| denyMessage = "This server is not trusted by the admin." |
| ``` |
|
|
| **3. Targeting all MCP servers** |
|
|
| Use `mcpName = "*"` to create a rule that applies to **all** tools from **any** |
| registered MCP server. This is useful for setting category-wide defaults. |
|
|
| ```toml |
| # Ask user for any tool call from any MCP server |
| [[rule]] |
| toolName = "*" |
| mcpName = "*" |
| decision = "ask_user" |
| priority = 10 |
| ``` |
|
|
| ### Special syntax for subagents |
|
|
| You can secure and govern subagents using standard policy rules by treating the |
| subagent's name as the `toolName`. |
|
|
| When the main agent invokes a subagent (e.g., using the unified `invoke_agent` |
| tool), the Policy Engine automatically treats the target `agent_name` as a |
| virtual tool alias for rule matching. |
|
|
| **Example:** |
|
|
| This rule denies access to the `codebase_investigator` subagent. |
|
|
| ```toml |
| [[rule]] |
| toolName = "codebase_investigator" |
| decision = "deny" |
| priority = 500 |
| deny_message = "Deep codebase analysis is restricted for this session." |
| ``` |
|
|
| - **Backward Compatibility**: Any rules written targeting historical 1:1 |
| subagent tool names will continue to match transparently. |
| - **Context differentiation**: To create rules based on **who** is calling a |
| tool, use the `subagent` field instead. See |
| [TOML rule schema](#toml-rule-schema). |
|
|
| ## Default policies |
|
|
| Gemini CLI ships with a set of default policies to provide a safe out-of-the-box |
| experience. |
|
|
| - **Read-only tools** (like `read_file`, `glob`) are generally **allowed**. |
| - **Agent delegation** defaults to **`ask_user`** to ensure remote agents can |
| prompt for confirmation, but local sub-agent actions are executed silently and |
| checked individually. |
| - **Write tools** (like `write_file`, `run_shell_command`) default to |
| **`ask_user`**. |
| - In **`yolo`** mode, a high-priority rule allows all tools. |
| - In **`autoEdit`** mode, rules allow certain write operations to happen without |
| prompting. |
|
|