Follow Discord
Sweep 23 Oct 2026 · 21:48Z Build v2.1.310 529 read Stable v2.1.308 Latest v2.1.310 Next v2.1.310 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One capture · claude-code

One read of Claude Code CLIclaude-code-20261023T190701Z

220 pages moved out of 228 read.

Pages moved 220 significant first
Pages read 228 in this capture
Captured 19:07 UTC
Corpus hash 7e3663570d29 corpus-hash

What this read moved

151-175 of 220, page 7 of 9

This capture is too large to show at once. Changes 151-175 of 220 are below, significant first; the rest are on the following screens.

plugins/overview Changed · +34 / -34 lines

from line 2
22 
33> Understand what a Claude Code plugin is, when you need one instead of a standalone skill or MCP server, and which page to read to install or create one.
44 
5A Claude Code plugin is a directory of skills, agents, hooks, MCP servers, or other components that Claude Code installs and loads as one unit. Most plugins come from a marketplace, which is a catalog that lists plugins and where to fetch each one. You can also load a plugin from a folder someone gives you, or [build your own](/docs/en/plugins/create.md).
5A Claude Code plugin is a directory of skills, agents, hooks, MCP servers, or other components that Claude Code installs and loads as one unit. Most plugins come from a marketplace, which is a catalog that lists plugins and where to fetch each one. You can also load a plugin from a folder someone gives you, or [build your own](https://code.claude.com/docs/en/plugins/create.md).
66 
77> [!NOTE]
88>
from line 10
1010>
1111> * **You use claude.ai chat or Cowork and not Claude Code**: see [Plugins on claude.ai and in Cowork](https://claude.com/docs/plugins/overview)
1212> * **You built an MCP server and want it in Anthropic's directory**: see [Publish to the directory](https://claude.com/docs/directory/publish)
13> * **You want Claude Code inside VS Code or a JetBrains IDE**: that's the VS Code extension or the JetBrains plugin, not a Claude Code plugin. See [Use Claude Code in VS Code](/docs/en/vs-code.md) or [JetBrains IDEs](/docs/en/jetbrains.md)
13> * **You want Claude Code inside VS Code or a JetBrains IDE**: that's the VS Code extension or the JetBrains plugin, not a Claude Code plugin. See [Use Claude Code in VS Code](https://code.claude.com/docs/en/vs-code.md) or [JetBrains IDEs](https://code.claude.com/docs/en/jetbrains.md)
1414 
1515To try a plugin now, run `/plugin` in a Claude Code terminal session and install one from the **Discover** tab, which lists the plugins from your marketplaces. From there:
1616 
17* [Install and manage plugins](/docs/en/plugins/install.md): the full install steps, scopes, and other surfaces
18* [Create a plugin](/docs/en/plugins/create.md): build your own
17* [Install and manage plugins](https://code.claude.com/docs/en/plugins/install.md): the full install steps, scopes, and other surfaces
18* [Create a plugin](https://code.claude.com/docs/en/plugins/create.md): build your own
1919* [Decide whether you need a plugin](#decide-whether-you-need-a-plugin): whether a plugin is the right tool for what you want
2020 
2121## Understand what a plugin is
2222 
23A plugin is a directory of components, usually with a manifest. The manifest, a JSON file at `.claude-plugin/plugin.json`, gives the plugin its name and can add a version, a description, and other [metadata](/docs/en/plugins/manifest-reference.md). The components are what the plugin adds to Claude Code, such as:
23A plugin is a directory of components, usually with a manifest. The manifest, a JSON file at `.claude-plugin/plugin.json`, gives the plugin its name and can add a version, a description, and other [metadata](https://code.claude.com/docs/en/plugins/manifest-reference.md). The components are what the plugin adds to Claude Code, such as:
2424 
25* [**Skills**](/docs/en/plugins/components.md#skills): `SKILL.md` instructions Claude loads when relevant, and that you can also run as a command
26* [**Agents**](/docs/en/plugins/components.md#agents): subagent definitions Claude can delegate to
27* [**Hooks**](/docs/en/plugins/components.md#hooks): commands Claude Code runs at points in its lifecycle, such as after every edit
28* [**A hooks module**](/docs/en/plugins/mods/overview.md): hooks written as JavaScript functions, which can also draw panes and add commands. A plugin that has one is called a mod. Some mods are [built into Claude Code](/docs/en/plugins/mods/overview.md#mods-built-into-claude-code).
29* [**MCP servers**](/docs/en/plugins/components.md#mcp-servers): tool servers Claude Code connects to while the plugin is enabled
25* [**Skills**](https://code.claude.com/docs/en/plugins/components.md#skills): `SKILL.md` instructions Claude loads when relevant, and that you can also run as a command
26* [**Agents**](https://code.claude.com/docs/en/plugins/components.md#agents): subagent definitions Claude can delegate to
27* [**Hooks**](https://code.claude.com/docs/en/plugins/components.md#hooks): commands Claude Code runs at points in its lifecycle, such as after every edit
28* [**A hooks module**](https://code.claude.com/docs/en/plugins/mods/overview.md): hooks written as JavaScript functions, which can also draw panes and add commands. A plugin that has one is called a mod. Some mods are [built into Claude Code](https://code.claude.com/docs/en/plugins/mods/overview.md#mods-built-into-claude-code).
29* [**MCP servers**](https://code.claude.com/docs/en/plugins/components.md#mcp-servers): tool servers Claude Code connects to while the plugin is enabled
3030 
3131This diagram shows a plugin named `my-plugin` that holds a skill, an agent, hooks, and an MCP server, and what you get from each file once the plugin loads.
3232 
3333![Diagram in two columns joined by five straight arrows. Left, the directory of a plugin named my-plugin, holding a manifest at .claude-plugin/plugin.json, skills/review/SKILL.md, agents/reviewer.md, hooks/hooks.json, .mcp.json, and other components. Right, what each file gives you in your session: the manifest sets the plugin name, my-plugin; the skill runs as /my-plugin:review; the agent file is a subagent Claude can delegate to; the hooks file holds hooks that run on lifecycle events; and .mcp.json adds an MCP server that gives Claude tools.](https://mintcdn.com/claude-code/2Q_GtOEovg5qaBem/images/plugin-directory.svg?fit=max\&auto=format\&n=2Q_GtOEovg5qaBem\&q=85\&s=f623b64e82713b830e48174f0a922888)
3434 
35For every component type a plugin can hold, with an example of each, see [Plugin components](/docs/en/plugins/components.md). To see where each piece is located in a plugin's directory, use the [plugin explorer](/docs/en/plugins/components.md#explore-the-plugin-directory) on that page.
35For every component type a plugin can hold, with an example of each, see [Plugin components](https://code.claude.com/docs/en/plugins/components.md). To see where each piece is located in a plugin's directory, use the [plugin explorer](https://code.claude.com/docs/en/plugins/components.md#explore-the-plugin-directory) on that page.
3636 
3737### Decide whether you need a plugin
3838 
39Skills, subagents, hooks, and MCP servers all work on their own, without a plugin. A skill you save in `~/.claude/skills/`, for example, is available in every project on your machine. To set one up on its own, see [Skills](/docs/en/skills.md), [Subagents](/docs/en/sub-agents.md), [Hooks](/docs/en/hooks-guide.md), or [MCP](/docs/en/mcp.md).
39Skills, subagents, hooks, and MCP servers all work on their own, without a plugin. A skill you save in `~/.claude/skills/`, for example, is available in every project on your machine. To set one up on its own, see [Skills](https://code.claude.com/docs/en/skills.md), [Subagents](https://code.claude.com/docs/en/sub-agents.md), [Hooks](https://code.claude.com/docs/en/hooks-guide.md), or [MCP](https://code.claude.com/docs/en/mcp.md).
4040 
4141Use a plugin when you want several skills, subagents, hooks, or MCP servers packaged as one unit. Install one to get a setup someone else built, with one command and updates from its marketplace. Make one to give your own setup to teammates, install it in many projects, or publish versioned releases.
4242 
from line 44
4444 
4545An enabled plugin is part of every session, not only the sessions where you use it. That has a few consequences worth knowing before you install one:
4646 
47* **Context and usage**: for each skill, agent, and command that [Claude can invoke on its own](/docs/en/skills.md#control-who-invokes-a-skill), the name and description are in Claude's context on every turn so that Claude knows it exists. Those tokens count toward your usage and leave less room in the [context window](/docs/en/context-window.md) even in sessions where nothing from the plugin runs. The full text of a skill or agent loads only when it's used. What the plugin's MCP servers add per turn follows [MCP tool search](/docs/en/mcp.md#scale-with-mcp-tool-search).
47* **Context and usage**: for each skill, agent, and command that [Claude can invoke on its own](https://code.claude.com/docs/en/skills.md#control-who-invokes-a-skill), the name and description are in Claude's context on every turn so that Claude knows it exists. Those tokens count toward your usage and leave less room in the [context window](https://code.claude.com/docs/en/context-window.md) even in sessions where nothing from the plugin runs. The full text of a skill or agent loads only when it's used. What the plugin's MCP servers add per turn follows [MCP tool search](https://code.claude.com/docs/en/mcp.md#scale-with-mcp-tool-search).
4848* **Processes**: MCP servers the plugin defines run alongside each session where it's enabled, and its hooks fire at their events.
49* **Permissions**: what the plugin runs, it runs as you. See [Plugin security and trust](/docs/en/plugins/security.md) for what to review first.
49* **Permissions**: what the plugin runs, it runs as you. See [Plugin security and trust](https://code.claude.com/docs/en/plugins/security.md) for what to review first.
5050 
5151You can check a plugin's footprint at each stage:
5252 
5353* **Before you install**: open the plugin from the **Marketplaces** tab in `/plugin`. Plugins in Anthropic's official marketplace show a **Context cost** estimate there.
54* **After you install**: [Measure what a plugin costs](/docs/en/plugins/measure.md#measure-what-a-plugin-costs) shows how to read a plugin's footprint, and the **Installed** tab's **Not used recently** group lists plugins you could turn off.
55* **To stop it without uninstalling**: disable the plugin with `/plugin` or, in your shell, `claude plugin disable`. See [Manage installed plugins](/docs/en/plugins/install.md#manage-installed-plugins).
54* **After you install**: [Measure what a plugin costs](https://code.claude.com/docs/en/plugins/measure.md#measure-what-a-plugin-costs) shows how to read a plugin's footprint, and the **Installed** tab's **Not used recently** group lists plugins you could turn off.
55* **To stop it without uninstalling**: disable the plugin with `/plugin` or, in your shell, `claude plugin disable`. See [Manage installed plugins](https://code.claude.com/docs/en/plugins/install.md#manage-installed-plugins).
5656 
5757## Get plugins from a marketplace
5858 
from line 62
6262>
6363> A plugin marketplace isn't [Claude Marketplace](https://claude.com/marketplace). Claude Marketplace is the website at claude.com/marketplace where you browse plugins, connectors, partner products, and service partners. It isn't a marketplace you add with `/plugin marketplace add`.
6464 
65Claude Code adds Anthropic's official marketplace the first time you start an interactive terminal session, unless a [managed policy](/docs/en/plugins/org.md#allow-the-official-marketplace-and-your-own) blocks it. Claude Code doesn't add Anthropic's community and demo marketplaces on its own. To distinguish the three Anthropic marketplaces, read [Anthropic's marketplaces](/docs/en/plugins/anthropic-marketplaces.md). To see what the official one lists, open the **Discover** tab of `/plugin` in a session or browse [Claude Marketplace](https://claude.com/marketplace/plugins).
65Claude Code adds Anthropic's official marketplace the first time you start an interactive terminal session, unless a [managed policy](https://code.claude.com/docs/en/plugins/org.md#allow-the-official-marketplace-and-your-own) blocks it. Claude Code doesn't add Anthropic's community and demo marketplaces on its own. To distinguish the three Anthropic marketplaces, read [Anthropic's marketplaces](https://code.claude.com/docs/en/plugins/anthropic-marketplaces.md). To see what the official one lists, open the **Discover** tab of `/plugin` in a session or browse [Claude Marketplace](https://claude.com/marketplace/plugins).
6666 
6767This diagram shows the path from a marketplace to your session. A marketplace lists a plugin, you install that plugin, and Claude Code loads its components.
6868 
6969![Diagram of the marketplace path in three boxes, left to right. A marketplace, a catalog of plugins, lists a plugin. The plugin is one directory installed as a unit, holding skills, agents, hooks, MCP servers, and other components. You install the plugin into Claude Code, which loads its components.](https://mintcdn.com/claude-code/2Q_GtOEovg5qaBem/images/plugins-model.svg?fit=max\&auto=format\&n=2Q_GtOEovg5qaBem\&q=85\&s=4196344954b7c2e27fc0bd6a9a1113a1)
7070 
71[Install and manage plugins](/docs/en/plugins/install.md#install-a-plugin) has the install steps for each place you run Claude Code. While you're developing a plugin, you don't need a marketplace: load it straight from its folder with `--plugin-dir`, as [Develop without a marketplace](/docs/en/plugins/create.md#develop-without-a-marketplace) shows.
71[Install and manage plugins](https://code.claude.com/docs/en/plugins/install.md#install-a-plugin) has the install steps for each place you run Claude Code. While you're developing a plugin, you don't need a marketplace: load it straight from its folder with `--plugin-dir`, as [Develop without a marketplace](https://code.claude.com/docs/en/plugins/create.md#develop-without-a-marketplace) shows.
7272 
7373### Make an installed plugin available in your session
7474 
from line 76
7676 
7777* **Settings**: your settings list the marketplaces you've added and the plugins that are enabled.
7878* **Disk**: `~/.claude/plugins/` holds what Claude Code has fetched and installed.
79* **Session**: plugins load at startup, or when you [reload plugins](/docs/en/plugins/loading.md#check-which-stage-a-plugin-reached).
79* **Session**: plugins load at startup, or when you [reload plugins](https://code.claude.com/docs/en/plugins/loading.md#check-which-stage-a-plugin-reached).
8080 
81Read [Plugin loading reference](/docs/en/plugins/loading.md) for the rules at each layer, including which settings file takes precedence and where the files are on disk.
81Read [Plugin loading reference](https://code.claude.com/docs/en/plugins/loading.md) for the rules at each layer, including which settings file takes precedence and where the files are on disk.
8282 
8383## Tell Anthropic's marketplaces from third-party ones
8484 
8585A marketplace's name places it in one of three tiers. Claude Code accepts the official and community names only for marketplaces sourced from `github.com/anthropics/` repositories:
8686 
87* **Official**: marketplaces with one of Anthropic's [official marketplace names](/docs/en/plugins/security.md#official-marketplace-names), including `claude-plugins-official` and the demo marketplace `claude-code-plugins`.
88* **Community**: marketplaces with one of Anthropic's community names, such as `claude-community`. [Identify Anthropic's marketplaces by name](/docs/en/plugins/security.md#marketplace-tiers) lists them.
87* **Official**: marketplaces with one of Anthropic's [official marketplace names](https://code.claude.com/docs/en/plugins/security.md#official-marketplace-names), including `claude-plugins-official` and the demo marketplace `claude-code-plugins`.
88* **Community**: marketplaces with one of Anthropic's community names, such as `claude-community`. [Identify Anthropic's marketplaces by name](https://code.claude.com/docs/en/plugins/security.md#marketplace-tiers) lists them.
8989* **Third-party**: every other marketplace. A marketplace your coworker or your organization publishes is third-party.
9090 
91Whatever the tier, a plugin you install can run code with your user privileges. Read [Plugin security and trust](/docs/en/plugins/security.md) for how to review a plugin before you install it.
91Whatever the tier, a plugin you install can run code with your user privileges. Read [Plugin security and trust](https://code.claude.com/docs/en/plugins/security.md) for how to review a plugin before you install it.
9292 
93Through [managed settings](/docs/en/settings.md#settings-files), an organization can allowlist or block marketplaces, force-install plugins, and turn off session-only loading. Read [Manage plugins for your organization](/docs/en/plugins/org.md) for those controls.
93Through [managed settings](https://code.claude.com/docs/en/settings.md#settings-files), an organization can allowlist or block marketplaces, force-install plugins, and turn off session-only loading. Read [Manage plugins for your organization](https://code.claude.com/docs/en/plugins/org.md) for those controls.
9494 
9595## Understand install scopes
9696 
from line 97
9797When you install a plugin, you pick a scope, and the scope decides who the plugin is enabled for:
9898 
9999* **User scope**: enabled for you in every project on this computer
100* **Project scope**: enabled for everyone who works in this repository, through the committed `.claude/settings.json`. Each collaborator still [installs it on their own machine](/docs/en/plugins/loading.md#enabled-in-project-settings-but-not-installed)
100* **Project scope**: enabled for everyone who works in this repository, through the committed `.claude/settings.json`. Each collaborator still [installs it on their own machine](https://code.claude.com/docs/en/plugins/loading.md#enabled-in-project-settings-but-not-installed)
101101* **Local scope**: enabled for you in this repository only
102102 
103A plugin you install at user scope in the terminal, the desktop app's local sessions, or the VS Code extension is available in the other two on that computer, because all three read the same settings files. See [Choose an install scope](/docs/en/plugins/install.md#choose-an-install-scope) for how to pick one.
103A plugin you install at user scope in the terminal, the desktop app's local sessions, or the VS Code extension is available in the other two on that computer, because all three read the same settings files. See [Choose an install scope](https://code.claude.com/docs/en/plugins/install.md#choose-an-install-scope) for how to pick one.
104104 
105A cloud session, including one in the browser at claude.ai/code, doesn't load the plugins in your local settings. For install steps in the terminal, VS Code, and the desktop app, and for what a cloud session loads, see [Install a plugin](/docs/en/plugins/install.md#install-a-plugin).
105A cloud session, including one in the browser at claude.ai/code, doesn't load the plugins in your local settings. For install steps in the terminal, VS Code, and the desktop app, and for what a cloud session loads, see [Install a plugin](https://code.claude.com/docs/en/plugins/install.md#install-a-plugin).
106106 
107107> [!NOTE]
108108>
from line 110
110110 
111111## Next steps
112112 
113Most people start by installing a plugin from Anthropic's official marketplace, which Claude Code adds the first time you start an interactive terminal session. Run `/plugin` in a terminal session to browse it, or follow [Install and manage plugins](/docs/en/plugins/install.md), which also covers the desktop app and VS Code. To see what's in that marketplace before you open Claude Code, browse [Claude Marketplace](https://claude.com/marketplace/plugins) on the web.
113Most people start by installing a plugin from Anthropic's official marketplace, which Claude Code adds the first time you start an interactive terminal session. Run `/plugin` in a terminal session to browse it, or follow [Install and manage plugins](https://code.claude.com/docs/en/plugins/install.md), which also covers the desktop app and VS Code. To see what's in that marketplace before you open Claude Code, browse [Claude Marketplace](https://claude.com/marketplace/plugins) on the web.
114114 
115To build your own, [Create a plugin](/docs/en/plugins/create.md) starts with an empty directory and ends with a working plugin.
115To build your own, [Create a plugin](https://code.claude.com/docs/en/plugins/create.md) starts with an empty directory and ends with a working plugin.
116116 
117117Once you've installed or built a plugin, these pages cover what comes next:
118118 
119* **Share what you built**: [Publish and distribute a plugin](/docs/en/plugins/publish.md), through your own marketplace or [Anthropic's directory](/docs/en/plugins/publish.md#submit-to-anthropics-directory)
120* **Check whether it works and is used**: [Test plugins with evals](/docs/en/plugin-evals.md) and [Measure plugin cost and usage](/docs/en/plugins/measure.md)
121* **Run a marketplace for your team**: [Create a marketplace](/docs/en/plugins/create-marketplace.md), then [Host and maintain a marketplace](/docs/en/plugins/host-marketplace.md)
122* **Set plugin policy for an organization**: [Manage plugins for your organization](/docs/en/plugins/org.md)
123* **Fix a problem**: [Troubleshoot plugins](/docs/en/plugins/troubleshooting.md)
119* **Share what you built**: [Publish and distribute a plugin](https://code.claude.com/docs/en/plugins/publish.md), through your own marketplace or [Anthropic's directory](https://code.claude.com/docs/en/plugins/publish.md#submit-to-anthropics-directory)
120* **Check whether it works and is used**: [Test plugins with evals](https://code.claude.com/docs/en/plugin-evals.md) and [Measure plugin cost and usage](https://code.claude.com/docs/en/plugins/measure.md)
121* **Run a marketplace for your team**: [Create a marketplace](https://code.claude.com/docs/en/plugins/create-marketplace.md), then [Host and maintain a marketplace](https://code.claude.com/docs/en/plugins/host-marketplace.md)
122* **Set plugin policy for an organization**: [Manage plugins for your organization](https://code.claude.com/docs/en/plugins/org.md)
123* **Fix a problem**: [Troubleshoot plugins](https://code.claude.com/docs/en/plugins/troubleshooting.md)
124124 

plugins/publish Changed · +28 / -28 lines

from line 10
1010>
1111> These cases are covered on other pages:
1212>
13> * **Your plugin isn't finished yet**: start with [Create a plugin](/docs/en/plugins/create.md)
14> * **You maintain a CLI or SDK with a plugin in an official marketplace**: see [Recommend your plugin from your CLI](/docs/en/plugins/cli-hints.md)
13> * **Your plugin isn't finished yet**: start with [Create a plugin](https://code.claude.com/docs/en/plugins/create.md)
14> * **You maintain a CLI or SDK with a plugin in an official marketplace**: see [Recommend your plugin from your CLI](https://code.claude.com/docs/en/plugins/cli-hints.md)
1515 
1616Start with [Choose how to distribute](#choose-how-to-distribute) to compare the distribution options. If you already know your route, go to [Prepare your plugin for release](#prepare-your-plugin-for-release), then follow your route's section for what to tell your users and how they receive your updates.
1717 
from line 23
2323| :- | :- | :- | :- |
2424| [No marketplace](#share-a-plugin-without-a-marketplace) | The people you send the plugin folder or a `.zip` of it | The plugin's folder | None. They load the copy you sent |
2525| [Your own marketplace](#publish-through-your-own-marketplace) | Anyone who can reach the repository, which can be a private one your team can clone | A git repository or other host with a `.claude-plugin/marketplace.json` that lists your plugin | Off |
26| [Anthropic's directory](#submit-to-anthropics-directory) | People who add it on claude.ai or in Cowork. It also loads in their Claude Code sessions through [account sync](/docs/en/plugins/loading.md#synced-plugins) | A GitHub repository holding the plugin and a paid claude.ai plan to submit from | Yes, after the version you push is published |
26| [Anthropic's directory](#submit-to-anthropics-directory) | People who add it on claude.ai or in Cowork. It also loads in their Claude Code sessions through [account sync](https://code.claude.com/docs/en/plugins/loading.md#synced-plugins) | A GitHub repository holding the plugin and a paid claude.ai plan to submit from | Yes, after the version you push is published |
2727 
2828Auto-update is a per-marketplace setting on the user's side that fetches new versions in the background.
2929 
from line 37
3737 
3838**2. Decide how you'll version**
3939 
40If you set `version` in `plugin.json` and later push commits without changing it, `claude plugin update` prints `<name> is already at the latest version (1.0.0).` and users keep the old copy. Either increment `version` on every release, or omit it in a git-hosted marketplace so Claude Code uses the commit SHA instead. See [Versions and updates](/docs/en/plugins/loading.md#versions-and-updates).
40If you set `version` in `plugin.json` and later push commits without changing it, `claude plugin update` prints `<name> is already at the latest version (1.0.0).` and users keep the old copy. Either increment `version` on every release, or omit it in a git-hosted marketplace so Claude Code uses the commit SHA instead. See [Versions and updates](https://code.claude.com/docs/en/plugins/loading.md#versions-and-updates).
4141 
4242**3. Validate**
4343 
from line 44
4444In your shell, run `claude plugin validate --strict ./your-plugin`. A clean run prints `✔ Validation passed`.
4545 
4646* **In CI**: keep `--strict`, which also fails the run with exit code 1 on warnings such as an unknown manifest field or a missing `version`. Drop `--strict` if you chose to omit `version` in the previous step.
47* **Paths**: validation reports component paths that don't start with `./`. Inside hook commands and MCP server configs, refer to files as `${CLAUDE_PLUGIN_ROOT}/...`. See [path rules](/docs/en/plugins/manifest-reference.md#path-rules).
47* **Paths**: validation reports component paths that don't start with `./`. Inside hook commands and MCP server configs, refer to files as `${CLAUDE_PLUGIN_ROOT}/...`. See [path rules](https://code.claude.com/docs/en/plugins/manifest-reference.md#path-rules).
4848 
4949**4. Install it from a local marketplace**
5050 
5151In your shell, add a local marketplace that lists the plugin with `claude plugin marketplace add ./path-to-marketplace`, install the plugin from it, and start a session to confirm it loads.
5252 
53* For the smallest marketplace that works, see [Create a marketplace](/docs/en/plugins/create-marketplace.md).
54* To know whether an install loads your source directory or a cached copy, see [In-place and copied plugins](/docs/en/plugins/loading.md#in-place-and-copied-plugins).
53* For the smallest marketplace that works, see [Create a marketplace](https://code.claude.com/docs/en/plugins/create-marketplace.md).
54* To know whether an install loads your source directory or a cached copy, see [In-place and copied plugins](https://code.claude.com/docs/en/plugins/loading.md#in-place-and-copied-plugins).
5555 
5656**5. Fill in the metadata users see**
5757 
58Set `description`, `author`, `homepage`, and `repository` in `plugin.json`, and add a `README.md` at the plugin root. `homepage` must parse as a URL. The [manifest reference](/docs/en/plugins/manifest-reference.md#fields) lists every field.
58Set `description`, `author`, `homepage`, and `repository` in `plugin.json`, and add a `README.md` at the plugin root. `homepage` must parse as a URL. The [manifest reference](https://code.claude.com/docs/en/plugins/manifest-reference.md#fields) lists every field.
5959 
6060**6. Run your eval suite**
6161 
62If you have an eval suite, run `claude plugin eval` in your shell. It runs the plugin's test cases and scores the results, which catches regressions when you change the plugin. See [Test plugins with evals](/docs/en/plugin-evals.md).
62If you have an eval suite, run `claude plugin eval` in your shell. It runs the plugin's test cases and scores the results, which catches regressions when you change the plugin. See [Test plugins with evals](https://code.claude.com/docs/en/plugin-evals.md).
6363 
6464## Share a plugin without a marketplace
6565 
6666If the plugin is in a git repository, people can clone it and load the checkout, or start Claude Code from their shell with `--plugin-url` pointed at a `.zip` you attach to a release. To get your next version they pull or download again. If it isn't in a repository, send them the directory or a `.zip` of it. They load it in one of two ways:
6767 
68* **For one session**: they start Claude Code from their shell with `claude --plugin-dir ./deploy-helper`, where the path is the clone, the unzipped folder, or the `.zip` itself. See [Flags that load a plugin for one session](/docs/en/plugins/cli-reference.md#flags-that-load-a-plugin-for-one-session).
69* **For every session**: they move the plugin directory, with its `.claude-plugin/plugin.json`, under `~/.claude/skills/`. From then on Claude Code loads it at startup, unless their administrator has [blocked skills-directory plugins](/docs/en/plugins/loading.md#skills-directory-plugins-blocked-by-managed-settings).
68* **For one session**: they start Claude Code from their shell with `claude --plugin-dir ./deploy-helper`, where the path is the clone, the unzipped folder, or the `.zip` itself. See [Flags that load a plugin for one session](https://code.claude.com/docs/en/plugins/cli-reference.md#flags-that-load-a-plugin-for-one-session).
69* **For every session**: they move the plugin directory, with its `.claude-plugin/plugin.json`, under `~/.claude/skills/`. From then on Claude Code loads it at startup, unless their administrator has [blocked skills-directory plugins](https://code.claude.com/docs/en/plugins/loading.md#skills-directory-plugins-blocked-by-managed-settings).
7070 
7171Adding a `.claude-plugin/marketplace.json` to that same repository is what lets people install by name and update with a command; see [Publish through your own marketplace](#publish-through-your-own-marketplace).
7272 
7373### Ship a plugin with your own tool
7474 
75If you maintain a CLI or SDK, publish the plugin in a marketplace and have your installer or post-install message run or print the two commands a user needs: `claude plugin marketplace add <source>`, then `claude plugin install <name>@<marketplace>`. For in-session discovery when someone uses your tool, see [Recommend your plugin from your CLI](/docs/en/plugins/cli-hints.md).
75If you maintain a CLI or SDK, publish the plugin in a marketplace and have your installer or post-install message run or print the two commands a user needs: `claude plugin marketplace add <source>`, then `claude plugin install <name>@<marketplace>`. For in-session discovery when someone uses your tool, see [Recommend your plugin from your CLI](https://code.claude.com/docs/en/plugins/cli-hints.md).
7676 
7777## Publish through your own marketplace
7878 
from line 80
8080 
8181### Add the marketplace file to your repository
8282 
83To publish from the plugin's own repository, save the marketplace file beside `plugin.json` in `.claude-plugin/`, with one entry whose `source` is `"./"`, the repository root. Give the entry the same `name` as `plugin.json`, per [Keep the entry name and the manifest name the same](/docs/en/plugins/create-marketplace.md#keep-the-entry-name-and-the-manifest-name-the-same):
83To publish from the plugin's own repository, save the marketplace file beside `plugin.json` in `.claude-plugin/`, with one entry whose `source` is `"./"`, the repository root. Give the entry the same `name` as `plugin.json`, per [Keep the entry name and the manifest name the same](https://code.claude.com/docs/en/plugins/create-marketplace.md#keep-the-entry-name-and-the-manifest-name-the-same):
8484 
8585```json .claude-plugin/marketplace.json theme={null}
8686{
from line 92
9292}
9393```
9494 
95In your shell, run `claude plugin validate .` in the repository before you push. For what the run checks, see [Validate a directory](/docs/en/plugins/cli-reference.md#validate-a-directory).
95In your shell, run `claude plugin validate .` in the repository before you push. For what the run checks, see [Validate a directory](https://code.claude.com/docs/en/plugins/cli-reference.md#validate-a-directory).
9696 
97[Create a marketplace](/docs/en/plugins/create-marketplace.md) covers the layout with several plugins in one repository.
97[Create a marketplace](https://code.claude.com/docs/en/plugins/create-marketplace.md) covers the layout with several plugins in one repository.
9898 
9999### Control who can install
100100 
101Anyone who can clone the repository can install from it, so if the repository is private, the marketplace is private too. For hosts other than a git repository, see [Host a marketplace](/docs/en/plugins/host-marketplace.md). To reach everyone at a company, including people who don't use git, see [Roll out to a whole company](/docs/en/plugins/host-marketplace.md#roll-out-to-a-whole-company).
101Anyone who can clone the repository can install from it, so if the repository is private, the marketplace is private too. For hosts other than a git repository, see [Host a marketplace](https://code.claude.com/docs/en/plugins/host-marketplace.md). To reach everyone at a company, including people who don't use git, see [Roll out to a whole company](https://code.claude.com/docs/en/plugins/host-marketplace.md#roll-out-to-a-whole-company).
102102 
103103### Tell users how to install
104104 
from line 106
106106 
107107* Add the marketplace once: `claude plugin marketplace add your-org/your-marketplace`, where the argument is a GitHub `owner/repo` shorthand, a URL, or a path
108108* Install the plugin: `claude plugin install deploy-helper@your-marketplace`
109* Or do both from inside a session: `/plugin install deploy-helper --marketplace your-org/your-marketplace`. Requires Claude Code v2.1.275 or later. See [Add a marketplace and install in one command](/docs/en/plugins/install.md#add-a-marketplace-and-install-in-one-command)
109* Or do both from inside a session: `/plugin install deploy-helper --marketplace your-org/your-marketplace`. Requires Claude Code v2.1.275 or later. See [Add a marketplace and install in one command](https://code.claude.com/docs/en/plugins/install.md#add-a-marketplace-and-install-in-one-command)
110110* Or do both from the shell in one command: `claude plugin install deploy-helper --marketplace your-org/your-marketplace`. Requires Claude Code v2.1.292 or later
111111 
112112### Ship updates to users
from line 114
114114Users receive a release when they ask for it or when auto-update is on for your marketplace:
115115 
116116* **On request**: `claude plugin update deploy-helper@your-marketplace` in the user's shell refreshes the marketplace and installs the new copy when your plugin's version has changed
117* **Auto-update**: off by default for your marketplace. See [Turn on auto-update](/docs/en/plugins/host-marketplace.md#turn-on-auto-update). Once on, it does the same as `claude plugin update` on a delay after the session starts
117* **Auto-update**: off by default for your marketplace. See [Turn on auto-update](https://code.claude.com/docs/en/plugins/host-marketplace.md#turn-on-auto-update). Once on, it does the same as `claude plugin update` on a delay after the session starts
118118 
119[Install plugins](/docs/en/plugins/install.md) covers the user-side commands, and [when auto-update runs](/docs/en/plugins/loading.md#when-auto-update-runs) covers the timing.
119[Install plugins](https://code.claude.com/docs/en/plugins/install.md) covers the user-side commands, and [when auto-update runs](https://code.claude.com/docs/en/plugins/loading.md#when-auto-update-runs) covers the timing.
120120 
121121## Submit to Anthropic's directory
122122 
from line 132
132132 
133133**2. Validate the plugin locally**
134134 
135Run `claude plugin validate ./your-plugin --strict` in your shell. Replace `./your-plugin` with the path to your plugin directory. The command catches manifest errors locally; [plugin validate](/docs/en/plugins/cli-reference.md#plugin-validate) lists which files each run reads. The portal applies additional directory rules that the CLI doesn't check, so a clean local run doesn't guarantee a clean portal validation.
135Run `claude plugin validate ./your-plugin --strict` in your shell. Replace `./your-plugin` with the path to your plugin directory. The command catches manifest errors locally; [plugin validate](https://code.claude.com/docs/en/plugins/cli-reference.md#plugin-validate) lists which files each run reads. The portal applies additional directory rules that the CLI doesn't check, so a clean local run doesn't guarantee a clean portal validation.
136136 
137137The [plugin pre-submission checklist](https://claude.com/docs/plugins/pre-submission-checklist#run-the-checks-before-you-submit) on claude.com has the checks to run and fix before you submit.
138138 
from line 153
153153 
154154### How a listed plugin reaches Claude Code users
155155 
156A person who installs your plugin from the directory on claude.ai has it on their account, and Claude Code loads it as `<name>@synced`. [Plugins synced from claude.ai](/docs/en/plugins/loading.md#synced-plugins) covers what they see and how they turn it off.
156A person who installs your plugin from the directory on claude.ai has it on their account, and Claude Code loads it as `<name>@synced`. [Plugins synced from claude.ai](https://code.claude.com/docs/en/plugins/loading.md#synced-plugins) covers what they see and how they turn it off.
157157 
158158## Ship updates, renames, and removals
159159 
from line 165
165165 
166166Tag the release in git when other plugins declare a version range on yours, because those ranges resolve against tags. Otherwise you don't need a tag.
167167 
168To tag, run `claude plugin tag` in your shell from the plugin directory. It creates a `{name}--v{version}` tag. Add `--push` to send the tag to `origin`. The [`plugin tag` reference](/docs/en/plugins/cli-reference#plugin-tag) lists its flags.
168To tag, run `claude plugin tag` in your shell from the plugin directory. It creates a `{name}--v{version}` tag. Add `--push` to send the tag to `origin`. The [`plugin tag` reference](https://code.claude.com/docs/en/plugins/cli-reference#plugin-tag) lists its flags.
169169 
170170### Rename or remove a plugin
171171 
172172Never change a published plugin's `name`. After a rename, users who already installed it lose the plugin, because their install is recorded under the old name. A `renames` entry in your marketplace file migrates them instead. Change `displayName` when you want a different label.
173173 
174If a rename is unavoidable, use the marketplace file's `renames` map so that existing installs migrate instead of failing with [`Plugin "<name>" not found in marketplace`](/docs/en/plugins/troubleshooting#plugin-not-found-in-marketplace). To remove a plugin from the marketplace, or for the full `renames` details, see [Rename or remove a plugin](/docs/en/plugins/host-marketplace.md#rename-or-remove-a-plugin) on the hosting page. The [marketplace reference](/docs/en/plugins/marketplace-reference.md#top-level-fields) has the field.
174If a rename is unavoidable, use the marketplace file's `renames` map so that existing installs migrate instead of failing with [`Plugin "<name>" not found in marketplace`](https://code.claude.com/docs/en/plugins/troubleshooting#plugin-not-found-in-marketplace). To remove a plugin from the marketplace, or for the full `renames` details, see [Rename or remove a plugin](https://code.claude.com/docs/en/plugins/host-marketplace.md#rename-or-remove-a-plugin) on the hosting page. The [marketplace reference](https://code.claude.com/docs/en/plugins/marketplace-reference.md#top-level-fields) has the field.
175175 
176176## Declare dependencies
177177 
178178If your plugin needs another plugin from the same marketplace to be enabled, list it in the `dependencies` array of `plugin.json`. Each entry is a bare name or an object with a semver `version` range. When a user installs your plugin, Claude Code installs and enables the dependency too.
179179 
180[Plugin dependencies](/docs/en/plugins/dependencies.md) covers the range syntax, cross-marketplace dependencies, and how users prune dependencies they no longer need.
180[Plugin dependencies](https://code.claude.com/docs/en/plugins/dependencies.md) covers the range syntax, cross-marketplace dependencies, and how users prune dependencies they no longer need.
181181 
182182## Next steps
183183 
184* [Host and maintain a marketplace](/docs/en/plugins/host-marketplace.md): release new versions and keep users up to date
185* [Plugin dependencies](/docs/en/plugins/dependencies.md): declare and version the plugins yours relies on
186* [Recommend your plugin from your CLI](/docs/en/plugins/cli-hints.md): prompt Claude Code users of your CLI to install the plugin
187* [Measure plugin cost and usage](/docs/en/plugins/measure.md): see what your plugin costs in context and whether people use it
184* [Host and maintain a marketplace](https://code.claude.com/docs/en/plugins/host-marketplace.md): release new versions and keep users up to date
185* [Plugin dependencies](https://code.claude.com/docs/en/plugins/dependencies.md): declare and version the plugins yours relies on
186* [Recommend your plugin from your CLI](https://code.claude.com/docs/en/plugins/cli-hints.md): prompt Claude Code users of your CLI to install the plugin
187* [Measure plugin cost and usage](https://code.claude.com/docs/en/plugins/measure.md): see what your plugin costs in context and whether people use it
188188 

plugins/relevance Changed · +8 / -8 lines

from line 10
1010>
1111> These cases are covered on other pages:
1212>
13> * **You want to install plugins**: see [Install and manage plugins](/docs/en/plugins/install.md)
13> * **You want to install plugins**: see [Install and manage plugins](https://code.claude.com/docs/en/plugins/install.md)
1414> * **You want to turn suggestions off**: see [Understand how plugin relevance works](#understand-how-plugin-relevance-works)
1515 
1616Start with the sections for your role:
from line 34
3434 
3535Claude Code never installs the plugin automatically. The user always confirms.
3636 
37The spinner tip and the session-start notification both stop appearing when the user or project sets [`spinnerTipsEnabled`](/docs/en/settings-reference#spinnertipsenabled) to `false`, or when a [`spinnerTipsOverride`](/docs/en/settings-reference#spinnertipsoverride) with `excludeDefault` replaces the built-in tips. The Discover-tab pin isn't affected by either setting.
37The spinner tip and the session-start notification both stop appearing when the user or project sets [`spinnerTipsEnabled`](https://code.claude.com/docs/en/settings-reference#spinnertipsenabled) to `false`, or when a [`spinnerTipsOverride`](https://code.claude.com/docs/en/settings-reference#spinnertipsoverride) with `excludeDefault` replaces the built-in tips. The Discover-tab pin isn't affected by either setting.
3838 
3939## Add relevance to a plugin entry
4040 
from line 154
154154 
155155## Enable suggestions in managed settings
156156 
157Users see no suggestions from a marketplace until an administrator allowlists it in [managed settings](/docs/en/plugins/org.md), even when its `marketplace.json` declares `relevance`.
157Users see no suggestions from a marketplace until an administrator allowlists it in [managed settings](https://code.claude.com/docs/en/plugins/org.md), even when its `marketplace.json` declares `relevance`.
158158 
159159To allowlist a marketplace, edit your managed settings as follows:
160160 
161161* Add the marketplace name to `pluginSuggestionMarketplaces`.
162* For any marketplace other than the official Anthropic marketplace, also declare the marketplace source, either as that name's entry in [`extraKnownMarketplaces`](/docs/en/plugins/org#require-a-marketplace-and-its-plugins) or as an entry in [`strictKnownMarketplaces`](/docs/en/plugins/org#allowlist-with-strictknownmarketplaces).
162* For any marketplace other than the official Anthropic marketplace, also declare the marketplace source, either as that name's entry in [`extraKnownMarketplaces`](https://code.claude.com/docs/en/plugins/org#require-a-marketplace-and-its-plugins) or as an entry in [`strictKnownMarketplaces`](https://code.claude.com/docs/en/plugins/org#allowlist-with-strictknownmarketplaces).
163163 
164164On a machine where the marketplace isn't registered, or is registered under the allowlisted name from a different source, no suggestions from it appear. The source check stops an unrelated source from registering under an allowlisted name to get its plugins suggested across your org.
165165 
from line 213
213213 
214214## See also
215215 
216* [Host a marketplace](/docs/en/plugins/host-marketplace.md): run the marketplace that hosts your plugins
217* [Marketplace reference](/docs/en/plugins/marketplace-reference.md#plugin-entries): every field a plugin entry accepts
218* [Recommend your plugin from your CLI](/docs/en/plugins/cli-hints.md): prompt users from your own CLI instead of from Claude Code's session signals
219* [Manage plugins for your organization](/docs/en/plugins/org.md): `extraKnownMarketplaces`, `strictKnownMarketplaces`, and the rest of the plugin policy keys
216* [Host a marketplace](https://code.claude.com/docs/en/plugins/host-marketplace.md): run the marketplace that hosts your plugins
217* [Marketplace reference](https://code.claude.com/docs/en/plugins/marketplace-reference.md#plugin-entries): every field a plugin entry accepts
218* [Recommend your plugin from your CLI](https://code.claude.com/docs/en/plugins/cli-hints.md): prompt users from your own CLI instead of from Claude Code's session signals
219* [Manage plugins for your organization](https://code.claude.com/docs/en/plugins/org.md): `extraKnownMarketplaces`, `strictKnownMarketplaces`, and the rest of the plugin policy keys
220220 

plugins/security Changed · +32 / -32 lines

from line 12
1212>
1313> These cases are covered on other pages:
1414>
15> * **Claude Code's own security model**: see [Security](/docs/en/security.md)
16> * **Restricting or requiring plugins for an organization**: see [Manage plugins for your organization](/docs/en/plugins/org.md)
17> * **The `security-guidance` or `claude-security` plugins**: this page isn't about them. See [`security-guidance`](/docs/en/security-guidance) and [`claude-security`](/docs/en/claude-security)
15> * **Claude Code's own security model**: see [Security](https://code.claude.com/docs/en/security.md)
16> * **Restricting or requiring plugins for an organization**: see [Manage plugins for your organization](https://code.claude.com/docs/en/plugins/org.md)
17> * **The `security-guidance` or `claude-security` plugins**: this page isn't about them. See [`security-guidance`](https://code.claude.com/docs/en/security-guidance) and [`claude-security`](https://code.claude.com/docs/en/claude-security)
1818 
1919Start with [what a plugin can do](#understand-what-a-plugin-can-do) and [which marketplaces are Anthropic's](#marketplace-tiers), then [review the plugin before you install it](#review-a-plugin-before-you-install).
2020 
from line 22
2222 
2323A plugin can carry content that runs code on your machine with your user privileges and content that enters Claude's context as instructions, so [review a plugin before you install it](#review-a-plugin-before-you-install). Here's what an installed plugin can do:
2424 
25* **Hooks**: a plugin's [hooks](/docs/en/hooks.md) run as shell commands at points in Claude Code's lifecycle, such as before or after a tool call.
26* **Monitors**: a plugin's [monitors](/docs/en/plugins/components.md#monitors) run as background shell commands that Claude Code starts on its own when the session starts, when you reload plugins, or the first time a named skill runs.
27* **Mods**: a plugin's [mod](/docs/en/plugins/mods/overview.md) runs JavaScript inside Claude Code with your permissions. To list what a mod does before you install it, see [Decide whether to trust a mod](/docs/en/plugins/mods/overview.md#decide-whether-to-trust-a-mod).
28* **MCP and LSP servers**: Claude Code connects to the [MCP servers](/docs/en/mcp.md) an enabled plugin declares and gives Claude their tools. A stdio MCP server runs as a process that Claude Code starts on your machine. Claude Code also starts the language servers the plugin declares.
29* **`bin/` directory**: Claude Code adds each enabled plugin's `bin/` directory to the `PATH` of the Bash tool's shell, so Claude's Bash commands can run any executable there. A command that Claude runs with the [PowerShell tool](/docs/en/tools-reference.md#powershell-tool) gets those directories on its `PATH` when it runs outside the [sandbox](/docs/en/sandboxing.md), and not when it runs inside it.
25* **Hooks**: a plugin's [hooks](https://code.claude.com/docs/en/hooks.md) run as shell commands at points in Claude Code's lifecycle, such as before or after a tool call.
26* **Monitors**: a plugin's [monitors](https://code.claude.com/docs/en/plugins/components.md#monitors) run as background shell commands that Claude Code starts on its own when the session starts, when you reload plugins, or the first time a named skill runs.
27* **Mods**: a plugin's [mod](https://code.claude.com/docs/en/plugins/mods/overview.md) runs JavaScript inside Claude Code with your permissions. To list what a mod does before you install it, see [Decide whether to trust a mod](https://code.claude.com/docs/en/plugins/mods/overview.md#decide-whether-to-trust-a-mod).
28* **MCP and LSP servers**: Claude Code connects to the [MCP servers](https://code.claude.com/docs/en/mcp.md) an enabled plugin declares and gives Claude their tools. A stdio MCP server runs as a process that Claude Code starts on your machine. Claude Code also starts the language servers the plugin declares.
29* **`bin/` directory**: Claude Code adds each enabled plugin's `bin/` directory to the `PATH` of the Bash tool's shell, so Claude's Bash commands can run any executable there. A command that Claude runs with the [PowerShell tool](https://code.claude.com/docs/en/tools-reference.md#powershell-tool) gets those directories on its `PATH` when it runs outside the [sandbox](https://code.claude.com/docs/en/sandboxing.md), and not when it runs inside it.
3030* **Skills, commands, and agents**: these enter Claude's context as instructions, so they influence what Claude does with the tools it already has.
31* **Updates**: when auto-update is on for the marketplace you installed a plugin from, Claude Code updates that plugin in the background, so the files you reviewed can change on disk. [When auto-update runs](/docs/en/plugins/loading.md#when-auto-update-runs) has the timing. To turn auto-update on or off per marketplace, see [Keep plugins updated](/docs/en/plugins/install.md#keep-plugins-updated).
31* **Updates**: when auto-update is on for the marketplace you installed a plugin from, Claude Code updates that plugin in the background, so the files you reviewed can change on disk. [When auto-update runs](https://code.claude.com/docs/en/plugins/loading.md#when-auto-update-runs) has the timing. To turn auto-update on or off per marketplace, see [Keep plugins updated](https://code.claude.com/docs/en/plugins/install.md#keep-plugins-updated).
3232 
33Claude Code's [permission rules](/docs/en/permissions.md) and [sandbox](/docs/en/sandboxing.md) cover the tool calls Claude makes, not the code a plugin runs by itself:
33Claude Code's [permission rules](https://code.claude.com/docs/en/permissions.md) and [sandbox](https://code.claude.com/docs/en/sandboxing.md) cover the tool calls Claude makes, not the code a plugin runs by itself:
3434 
35* **Hooks, monitors, and server processes**: command hooks and monitors are shell commands that run with your full user permissions. Claude Code runs hooks, monitors, MCP servers, LSP servers, and the processes a [mod](/docs/en/plugins/mods/overview.md#what-a-mod-can-reach) starts outside the sandbox.
36* **Claude's tool calls**: a call to one of the plugin's MCP tools, and a Bash command that runs an executable from the plugin's `bin/`, are tool calls, so your permission rules apply to them. For what a mod can do to a tool call, see [Decide whether to trust a mod](/docs/en/plugins/mods/overview.md#decide-whether-to-trust-a-mod).
35* **Hooks, monitors, and server processes**: command hooks and monitors are shell commands that run with your full user permissions. Claude Code runs hooks, monitors, MCP servers, LSP servers, and the processes a [mod](https://code.claude.com/docs/en/plugins/mods/overview.md#what-a-mod-can-reach) starts outside the sandbox.
36* **Claude's tool calls**: a call to one of the plugin's MCP tools, and a Bash command that runs an executable from the plugin's `bin/`, are tool calls, so your permission rules apply to them. For what a mod can do to a tool call, see [Decide whether to trust a mod](https://code.claude.com/docs/en/plugins/mods/overview.md#decide-whether-to-trust-a-mod).
3737 
38Installing a plugin also enables it, unless its manifest or marketplace entry sets [`defaultEnabled: false`](/docs/en/plugins/install#choose-an-install-scope) and you haven't enabled it yourself.
38Installing a plugin also enables it, unless its manifest or marketplace entry sets [`defaultEnabled: false`](https://code.claude.com/docs/en/plugins/install#choose-an-install-scope) and you haven't enabled it yourself.
3939 
4040To remove a plugin you no longer trust, see [Remove a plugin you no longer trust](#remove-a-plugin-you-no-longer-trust).
4141 
from line 72
7272* `first-party-plugins`
7373* `claude-tag-plugins`
7474 
75For how the official, community, and demo marketplaces differ and where to browse what each one lists, see [Anthropic's marketplaces](/docs/en/plugins/anthropic-marketplaces.md).
75For how the official, community, and demo marketplaces differ and where to browse what each one lists, see [Anthropic's marketplaces](https://code.claude.com/docs/en/plugins/anthropic-marketplaces.md).
7676 
7777## Review a plugin before you install
7878 
from line 102
102102 
103103### Remove a plugin you no longer trust
104104 
105In your shell, run [`claude plugin uninstall <plugin>`](/docs/en/plugins/cli-reference#plugin-uninstall) with the `--scope` you installed it at. Then check what the uninstall removed and what it left:
105In your shell, run [`claude plugin uninstall <plugin>`](https://code.claude.com/docs/en/plugins/cli-reference#plugin-uninstall) with the `--scope` you installed it at. Then check what the uninstall removed and what it left:
106106 
107* **Persistent data**: by default, when that was the last scope the plugin was installed at, uninstalling also deletes the plugin's persistent data directory. For `--keep-data` and the other cases where it stays, see [plugin uninstall](/docs/en/plugins/cli-reference.md#plugin-uninstall).
108* **Cached files**: the plugin's files stay on disk under `~/.claude/plugins/cache/` for 14 days before a [background sweep removes them](/docs/en/plugins/loading.md#cleanup-of-previous-versions). After you uninstall your last plugin, orphaned directories stay until you install another. To delete the files now, remove the plugin's directory under `~/.claude/plugins/cache/<marketplace>/<plugin>/` yourself.
109* **The marketplace**: if you don't trust the marketplace's owner either, [remove the marketplace](/docs/en/plugins/install.md#manage-marketplaces) too, which uninstalls every plugin you installed from it.
107* **Persistent data**: by default, when that was the last scope the plugin was installed at, uninstalling also deletes the plugin's persistent data directory. For `--keep-data` and the other cases where it stays, see [plugin uninstall](https://code.claude.com/docs/en/plugins/cli-reference.md#plugin-uninstall).
108* **Cached files**: the plugin's files stay on disk under `~/.claude/plugins/cache/` for 14 days before a [background sweep removes them](https://code.claude.com/docs/en/plugins/loading.md#cleanup-of-previous-versions). After you uninstall your last plugin, orphaned directories stay until you install another. To delete the files now, remove the plugin's directory under `~/.claude/plugins/cache/<marketplace>/<plugin>/` yourself.
109* **The marketplace**: if you don't trust the marketplace's owner either, [remove the marketplace](https://code.claude.com/docs/en/plugins/install.md#manage-marketplaces) too, which uninstalls every plugin you installed from it.
110110 
111111## Recognize when Claude Code refuses or warns
112112 
from line 120
120120Make sure you trust a plugin before installing, updating, or using it. Anthropic does not control what MCP servers, files, or other software are included in plugins and cannot verify that they will work as intended or that they won't change. See each plugin's homepage for more information.
121121```
122122 
123If your organization sets `pluginTrustMessage` in [managed settings](/docs/en/plugins/org.md), Claude Code appends that text to the warning.
123If your organization sets `pluginTrustMessage` in [managed settings](https://code.claude.com/docs/en/plugins/org.md), Claude Code appends that text to the warning.
124124 
125125### Untrusted marketplace sources and failed integrity checks
126126 
127127Claude Code refuses to load a marketplace or to install a plugin in these cases, each with its own error message:
128128 
129* **Untrusted marketplace source**: when a marketplace uses an official or community name but its source is outside `github.com/anthropics/`, Claude Code stops loading the marketplace and the plugins you installed from it. The error is [Marketplace is registered from an untrusted source](/docs/en/errors.md#marketplace-is-registered-from-an-untrusted-source).
130* **Archive integrity**: when a marketplace entry pins an [`archive` source](/docs/en/plugins/marketplace-reference#archive-plugin-source) to a `sha256` digest and the downloaded file's digest doesn't match it, Claude Code refuses the install. The error is [Plugin archive integrity check failed](/docs/en/errors.md#plugin-archive-integrity-check-failed).
129* **Untrusted marketplace source**: when a marketplace uses an official or community name but its source is outside `github.com/anthropics/`, Claude Code stops loading the marketplace and the plugins you installed from it. The error is [Marketplace is registered from an untrusted source](https://code.claude.com/docs/en/errors.md#marketplace-is-registered-from-an-untrusted-source).
130* **Archive integrity**: when a marketplace entry pins an [`archive` source](https://code.claude.com/docs/en/plugins/marketplace-reference#archive-plugin-source) to a `sha256` digest and the downloaded file's digest doesn't match it, Claude Code refuses the install. The error is [Plugin archive integrity check failed](https://code.claude.com/docs/en/errors.md#plugin-archive-integrity-check-failed).
131131 
132132The `sha256` pin is separate from the community catalog's commit SHA pin, which selects the git commit to check out.
133133 
134134## Enforce plugin controls for your organization
135135 
136With [managed settings](/docs/en/plugins/org.md), an administrator can enforce these plugin controls:
136With [managed settings](https://code.claude.com/docs/en/plugins/org.md), an administrator can enforce these plugin controls:
137137 
138138* Allowlist or blocklist marketplace sources
139139* Force-enable plugins
140140* Turn off the `--plugin-dir` and `--plugin-url` flags and the `CLAUDE_CODE_PLUGIN_DIRS` variable
141141* Limit hooks to those from managed settings and force-enabled plugins
142* Stop plugins from members' claude.ai accounts from loading in Claude Code, with [`syncClaudeAiPlugins`](/docs/en/plugins/org#control-matrix)
142* Stop plugins from members' claude.ai accounts from loading in Claude Code, with [`syncClaudeAiPlugins`](https://code.claude.com/docs/en/plugins/org#control-matrix)
143143 
144The [control matrix](/docs/en/plugins/org.md#control-matrix) says what each key does and doesn't cover.
144The [control matrix](https://code.claude.com/docs/en/plugins/org.md#control-matrix) says what each key does and doesn't cover.
145145 
146146## Find plugins in telemetry
147147 
148If your organization exports Claude Code's [OpenTelemetry events](/docs/en/monitoring-usage.md) to its own backend, the [marketplace tiers](#marketplace-tiers) decide which plugin names appear there:
148If your organization exports Claude Code's [OpenTelemetry events](https://code.claude.com/docs/en/monitoring-usage.md) to its own backend, the [marketplace tiers](#marketplace-tiers) decide which plugin names appear there:
149149 
150* **[Plugin loaded event](/docs/en/monitoring-usage.md#plugin-loaded-event)**: the event reports official-tier plugin and marketplace names as they are. For the community and third-party tiers, `plugin.name` and `marketplace.name` are the literal string `third-party` unless you set `OTEL_LOG_TOOL_DETAILS=1`.
151* **Plugin scope**: the loaded event's `plugin.scope` still reports where the plugin came from, such as `org` for a plugin your managed settings enable or `user-local` for any other third-party plugin. The [plugin loaded event](/docs/en/monitoring-usage.md#plugin-loaded-event) lists every value.
152* **[Plugin installed event](/docs/en/monitoring-usage.md#plugin-installed-event)**: unless you set `OTEL_LOG_TOOL_DETAILS=1`, the event omits the name fields for non-official plugins instead of reporting `third-party`.
150* **[Plugin loaded event](https://code.claude.com/docs/en/monitoring-usage.md#plugin-loaded-event)**: the event reports official-tier plugin and marketplace names as they are. For the community and third-party tiers, `plugin.name` and `marketplace.name` are the literal string `third-party` unless you set `OTEL_LOG_TOOL_DETAILS=1`.
151* **Plugin scope**: the loaded event's `plugin.scope` still reports where the plugin came from, such as `org` for a plugin your managed settings enable or `user-local` for any other third-party plugin. The [plugin loaded event](https://code.claude.com/docs/en/monitoring-usage.md#plugin-loaded-event) lists every value.
152* **[Plugin installed event](https://code.claude.com/docs/en/monitoring-usage.md#plugin-installed-event)**: unless you set `OTEL_LOG_TOOL_DETAILS=1`, the event omits the name fields for non-official plugins instead of reporting `third-party`.
153153* **[Claude Code Analytics API](https://platform.claude.com/docs/en/api/admin/analytics/plugins/list)**: Claude Code reports plugins from the official and community tiers by name and reports every other plugin as `third-party`.
154154 
155155## Next steps
156156 
157* [Manage plugins for your organization](/docs/en/plugins/org.md): restrict which marketplaces users can install from and require the ones you trust
158* [Install and manage plugins](/docs/en/plugins/install.md): review a plugin's details pane before you choose a scope
159* [Anthropic's marketplaces](/docs/en/plugins/anthropic-marketplaces.md): which marketplace names are Anthropic's
160* [Security](/docs/en/security.md): Claude Code's own security model
157* [Manage plugins for your organization](https://code.claude.com/docs/en/plugins/org.md): restrict which marketplaces users can install from and require the ones you trust
158* [Install and manage plugins](https://code.claude.com/docs/en/plugins/install.md): review a plugin's details pane before you choose a scope
159* [Anthropic's marketplaces](https://code.claude.com/docs/en/plugins/anthropic-marketplaces.md): which marketplace names are Anthropic's
160* [Security](https://code.claude.com/docs/en/security.md): Claude Code's own security model
161161 

plugins/troubleshooting Changed · +95 / -95 lines

from line 12
1212>
1313> These cases are covered on other pages:
1414>
15> * **Why scopes, the cache, and precedence behave the way they do**: read [Plugin loading reference](/docs/en/plugins/loading.md)
16> * **Looking up a flag, field, or command**: use the [plugin commands reference](/docs/en/plugins/cli-reference.md), the [manifest reference](/docs/en/plugins/manifest-reference.md), or the [marketplace reference](/docs/en/plugins/marketplace-reference.md)
17> * **A `hooks module not loaded` or `hooks module did not load` message**: the plugin is a [mod](/docs/en/plugins/mods/overview.md), so read [The mod doesn't load](/docs/en/plugins/mods/troubleshoot.md#the-mod-doesn’t-load)
15> * **Why scopes, the cache, and precedence behave the way they do**: read [Plugin loading reference](https://code.claude.com/docs/en/plugins/loading.md)
16> * **Looking up a flag, field, or command**: use the [plugin commands reference](https://code.claude.com/docs/en/plugins/cli-reference.md), the [manifest reference](https://code.claude.com/docs/en/plugins/manifest-reference.md), or the [marketplace reference](https://code.claude.com/docs/en/plugins/marketplace-reference.md)
17> * **A `hooks module not loaded` or `hooks module did not load` message**: the plugin is a [mod](https://code.claude.com/docs/en/plugins/mods/overview.md), so read [The mod doesn't load](https://code.claude.com/docs/en/plugins/mods/troubleshoot.md#the-mod-doesn’t-load)
1818 
1919Search for the exact message you saw. Each message is listed under the stage that produces it, which isn't always the command you ran. For example, an install can fail because a marketplace is missing, so that message is under [Add a marketplace](#add-a-marketplace).
2020 
from line 26
2626 
2727You typed `/plugin` somewhere other than a Claude Code terminal session, and Claude replied with this line instead of opening anything.
2828 
29You get this reply in a session that has no terminal to draw the `/plugin` panel in: [non-interactive mode](/docs/en/headless.md) with `claude -p`, the Agent SDK, the Claude desktop app's Code tab, the VS Code extension panel, and the browser at claude.ai/code.
29You get this reply in a session that has no terminal to draw the `/plugin` panel in: [non-interactive mode](https://code.claude.com/docs/en/headless.md) with `claude -p`, the Agent SDK, the Claude desktop app's Code tab, the VS Code extension panel, and the browser at claude.ai/code.
3030 
3131In the VS Code extension panel, only a `/plugin` line with something after it, such as `/plugin install <plugin>@<marketplace>`, gets this reply. `/plugin` or `/plugins` typed alone opens the **Manage plugins** dialog.
3232 
3333Install the plugin from the surface you're on instead:
3434 
35* **Claude desktop app, local or SSH session**: use the **Desktop app** tab under [Install a plugin](/docs/en/plugins/install.md#install-a-plugin)
36* **VS Code extension**: use the **VS Code** tab under [Install a plugin](/docs/en/plugins/install.md#install-a-plugin)
37* **Claude Code on the web, or a desktop cloud session**: a cloud session has no plugin browser. See the **Cloud session** tab under [Install a plugin](/docs/en/plugins/install.md#install-a-plugin) for what a cloud session loads
35* **Claude desktop app, local or SSH session**: use the **Desktop app** tab under [Install a plugin](https://code.claude.com/docs/en/plugins/install.md#install-a-plugin)
36* **VS Code extension**: use the **VS Code** tab under [Install a plugin](https://code.claude.com/docs/en/plugins/install.md#install-a-plugin)
37* **Claude Code on the web, or a desktop cloud session**: a cloud session has no plugin browser. See the **Cloud session** tab under [Install a plugin](https://code.claude.com/docs/en/plugins/install.md#install-a-plugin) for what a cloud session loads
3838* **A terminal you have access to**: run `claude` and type `/plugin` there, or run `claude plugin install <plugin>@<marketplace>` in your shell without starting a session
3939 
4040When a terminal install works, `/plugin` prints an install summary that starts with `✓ Installed <plugin>.` and `claude plugin install` prints `Successfully installed plugin: <plugin>@<marketplace>`.
from line 72
7272 
7373You ran `claude plugin install ...` in your shell, and the shell couldn't find `claude` at all. On Windows the message is `'claude' is not recognized as the name of a cmdlet` or `'claude' is not recognized as an internal or external command`.
7474 
75The cause isn't the plugin command. Follow [Verify your PATH](/docs/en/troubleshoot-install.md#verify-your-path), then retry the plugin command.
75The cause isn't the plugin command. Follow [Verify your PATH](https://code.claude.com/docs/en/troubleshoot-install.md#verify-your-path), then retry the plugin command.
7676 
7777### `Unknown command` and command spellings that don't exist
7878 
7979You typed a plugin command you saw somewhere and got `Unknown command: /<name>` in a session, or `error: unknown command '<name>'` or `error: unknown option '<flag>'` from the `claude` binary in your shell.
8080 
81Several command spellings are in use that Claude Code doesn't have. The table below maps each one to the real command. The [plugin commands reference](/docs/en/plugins/cli-reference.md) lists the subcommands and their flags.
81Several command spellings are in use that Claude Code doesn't have. The table below maps each one to the real command. The [plugin commands reference](https://code.claude.com/docs/en/plugins/cli-reference.md) lists the subcommands and their flags.
8282 
8383| You typed | What Claude Code says | Use instead |
8484| :- | :- | :- |
from line 127
127127You ran `/plugin install` in a session and Claude Code reported that it has no marketplace by that name. Two forms of the command reach this message:
128128 
129129* **`/plugin install <plugin>@<name>`**: the install line, often one someone sent you, names a marketplace you haven't added. The rest of this entry covers finding and adding it.
130* **`/plugin install <source>` with a path, URL, or `owner/repo`**: this form reports the message instead of installing, even for a source you've already added. To install from a source in one command, see [Add a marketplace and install in one command](/docs/en/plugins/install.md#add-a-marketplace-and-install-in-one-command).
130* **`/plugin install <source>` with a path, URL, or `owner/repo`**: this form reports the message instead of installing, even for a source you've already added. To install from a source in one command, see [Add a marketplace and install in one command](https://code.claude.com/docs/en/plugins/install.md#add-a-marketplace-and-install-in-one-command).
131131 
132If the name starts with `claudeai-`, the marketplace is hosted on claude.ai, and you add it by name from your shell with `claude plugin marketplace add --claudeai <name>`. See [Add a marketplace from claude.ai](/docs/en/plugins/install.md#add-from-claude-ai).
132If the name starts with `claudeai-`, the marketplace is hosted on claude.ai, and you add it by name from your shell with `claude plugin marketplace add --claudeai <name>`. See [Add a marketplace from claude.ai](https://code.claude.com/docs/en/plugins/install.md#add-from-claude-ai).
133133 
134For any other name, an install line names a marketplace but doesn't say where the marketplace is hosted, and Claude Code has no index to look a marketplace name up in. Ask whoever sent the line for the marketplace's source, which is a GitHub `owner/repo`, a git URL, or a path. Then [add the marketplace](/docs/en/plugins/install.md#add-a-marketplace) and run the install line again.
134For any other name, an install line names a marketplace but doesn't say where the marketplace is hosted, and Claude Code has no index to look a marketplace name up in. Ask whoever sent the line for the marketplace's source, which is a GitHub `owner/repo`, a git URL, or a path. Then [add the marketplace](https://code.claude.com/docs/en/plugins/install.md#add-a-marketplace) and run the install line again.
135135 
136A marketplace someone sends you is third-party, so [review the plugin before you install it](/docs/en/plugins/security.md#review-a-plugin-before-you-install).
136A marketplace someone sends you is third-party, so [review the plugin before you install it](https://code.claude.com/docs/en/plugins/security.md#review-a-plugin-before-you-install).
137137 
138138If you already added the marketplace, check the spelling against `/plugin marketplace list`.
139139 
from line 198
198198 
199199Claude Code cloned or downloaded the marketplace but found no `marketplace.json` at the expected path inside it. The add command reports it as `Failed to add marketplace: Marketplace file not found at ...`.
200200 
201The default location is `.claude-plugin/marketplace.json` at the repository root, and the [marketplace reference](/docs/en/plugins/marketplace-reference.md) lists the accepted locations.
201The default location is `.claude-plugin/marketplace.json` at the repository root, and the [marketplace reference](https://code.claude.com/docs/en/plugins/marketplace-reference.md) lists the accepted locations.
202202 
203203The fix differs for the owner and for everyone else:
204204 
from line 207
207207 
208208### `Cannot add marketplace "<name>": Claude Code cannot install plugins from a marketplace with this name`
209209 
210You added a marketplace, and the [`name`](/docs/en/plugins/marketplace-reference#top-level-fields) in its `marketplace.json` isn't valid as the part after `@` in a [plugin id](/docs/en/plugins/loading.md#find-where-a-plugin-came-from) such as `my-plugin@my-marketplace`. Claude Code refuses the add and registers nothing.
210You added a marketplace, and the [`name`](https://code.claude.com/docs/en/plugins/marketplace-reference#top-level-fields) in its `marketplace.json` isn't valid as the part after `@` in a [plugin id](https://code.claude.com/docs/en/plugins/loading.md#find-where-a-plugin-came-from) such as `my-plugin@my-marketplace`. Claude Code refuses the add and registers nothing.
211211 
212212The rest of the message states the rule for the name. In this example, `_internal` breaks the rule by starting with `_`:
213213 
from line 224
224224 
225225### `Cannot add marketplace "<name>": Claude Code reserves this name and cannot register a marketplace under it`
226226 
227You added a marketplace, and the [`name`](/docs/en/plugins/marketplace-reference#top-level-fields) in its `marketplace.json` is one of the member names that every JavaScript object has, such as `constructor`, `toString`, or `valueOf`. Claude Code reserves those names, so it refuses the add and registers nothing. [Reserved names](/docs/en/plugins/marketplace-reference.md#reserved-names) lists them.
227You added a marketplace, and the [`name`](https://code.claude.com/docs/en/plugins/marketplace-reference#top-level-fields) in its `marketplace.json` is one of the member names that every JavaScript object has, such as `constructor`, `toString`, or `valueOf`. Claude Code reserves those names, so it refuses the add and registers nothing. [Reserved names](https://code.claude.com/docs/en/plugins/marketplace-reference.md#reserved-names) lists them.
228228 
229229In this example, the marketplace is named `constructor`:
230230 
from line 232
232232Cannot add marketplace "constructor": Claude Code reserves this name and cannot register a marketplace under it. The name is set by "name" in the marketplace's marketplace.json; ask its maintainer to change it.
233233```
234234 
235If a settings file declares the marketplace under [`extraKnownMarketplaces`](/docs/en/settings-reference#extraknownmarketplaces), Claude Code's attempt to add it at startup fails the same way, and the message appears on the **Errors** tab in `/plugin`.
235If a settings file declares the marketplace under [`extraKnownMarketplaces`](https://code.claude.com/docs/en/settings-reference#extraknownmarketplaces), Claude Code's attempt to add it at startup fails the same way, and the message appears on the **Errors** tab in `/plugin`.
236236 
237237Give the marketplace another name, then add it again:
238238 
from line 256
256256 
257257To make Claude Code skip SSH for GitHub `owner/repo` sources, set `CLAUDE_CODE_PLUGIN_PREFER_HTTPS=1`. Without it, Claude Code clones those sources over SSH when an SSH key for `github.com` looks configured, and falls back to HTTPS when the SSH clone fails.
258258 
259For what background auto-updates can and can't do with your credentials, see [What background auto-update does with credentials](/docs/en/plugins/host-marketplace.md#what-background-auto-update-does-with-credentials).
259For what background auto-updates can and can't do with your credentials, see [What background auto-update does with credentials](https://code.claude.com/docs/en/plugins/host-marketplace.md#what-background-auto-update-does-with-credentials).
260260 
261261### `SSH host key is not in your known_hosts file`
262262 
from line 318
318318 
319319You work in an environment where the marketplace's git host is unreachable, and every session repeats a failed refresh in the background. Your existing checkout of the marketplace stays in place and startup isn't delayed.
320320 
321Each session, for a marketplace with [auto-update on](/docs/en/plugins/loading.md#which-marketplaces-and-plugins-auto-update), Claude Code checks the marketplace's git host for new commits in the background. When that check can't reach the host, it tries to clone the marketplace again, and offline that clone fails too.
321Each session, for a marketplace with [auto-update on](https://code.claude.com/docs/en/plugins/loading.md#which-marketplaces-and-plugins-auto-update), Claude Code checks the marketplace's git host for new commits in the background. When that check can't reach the host, it tries to clone the marketplace again, and offline that clone fails too.
322322 
323323Set this variable to skip the re-clone attempt and keep using the existing checkout when the check can't reach the host:
324324 
from line 336
336336 
337337With the variable set, Claude Code skips the re-clone only for a checkout that already contains `.claude-plugin/marketplace.json`. A marketplace that was never cloned or whose clone stopped partway still gets the clone attempt, so add it once while online.
338338 
339For a fully offline deployment, pre-populate the plugins directory at image build time with `CLAUDE_CODE_PLUGIN_SEED_DIR` instead, following [Seed containers and CI](/docs/en/plugins/org.md#seed-containers-and-ci).
339For a fully offline deployment, pre-populate the plugins directory at image build time with `CLAUDE_CODE_PLUGIN_SEED_DIR` instead, following [Seed containers and CI](https://code.claude.com/docs/en/plugins/org.md#seed-containers-and-ci).
340340 
341341### Marketplace add fails on a GitHub Enterprise Server host
342342 
from line 344
344344 
345345Both cases are on the GHES page:
346346 
347* [A policy error](/docs/en/github-enterprise-server.md#marketplace-add-fails-with-a-policy-error) means your organization restricted marketplace sources and an admin needs to add a `hostPattern` for the host
348* [A GitHub access error on claude.ai](/docs/en/github-enterprise-server.md#marketplace-add-on-claude-ai-fails-with-a-github-access-error) means your own GitHub Enterprise account isn't connected yet
347* [A policy error](https://code.claude.com/docs/en/github-enterprise-server.md#marketplace-add-fails-with-a-policy-error) means your organization restricted marketplace sources and an admin needs to add a `hostPattern` for the host
348* [A GitHub access error on claude.ai](https://code.claude.com/docs/en/github-enterprise-server.md#marketplace-add-on-claude-ai-fails-with-a-github-access-error) means your own GitHub Enterprise account isn't connected yet
349349 
350350## Install a plugin
351351 
from line 365
365365/plugin marketplace update <marketplace>
366366```
367367 
368`claude plugin marketplace update` prints `Successfully updated marketplace: <name>`, and `/plugin marketplace update` shows `✔ Updated 1 marketplace`. If the retried install prints the same message, check the name as [`not found in marketplace` with no hint](#the-message-has-no-hint) describes. [When Claude Code refreshes a marketplace before an install](/docs/en/plugins/loading.md#when-claude-code-refreshes-a-marketplace-before-an-install) lists the other cases where the refresh doesn't run.
368`claude plugin marketplace update` prints `Successfully updated marketplace: <name>`, and `/plugin marketplace update` shows `✔ Updated 1 marketplace`. If the retried install prints the same message, check the name as [`not found in marketplace` with no hint](#the-message-has-no-hint) describes. [When Claude Code refreshes a marketplace before an install](https://code.claude.com/docs/en/plugins/loading.md#when-claude-code-refreshes-a-marketplace-before-an-install) lists the other cases where the refresh doesn't run.
369369 
370370#### `not found in marketplace` with no hint
371371 
from line 391
391391 
392392You ran `/plugin install` for a plugin that's already installed at user scope or by managed settings, and Claude Code refused with `Use '/plugin' to manage existing plugins.` If you typed the plugin name without `@<marketplace>`, the message omits `globally`.
393393 
394The plugin is already available in every project, so there's nothing to add. To change its [scope](/docs/en/plugins/install.md), enable or disable it, or configure it, open `/plugin` and go to **Installed**.
394The plugin is already available in every project, so there's nothing to add. To change its [scope](https://code.claude.com/docs/en/plugins/install.md), enable or disable it, or configure it, open `/plugin` and go to **Installed**.
395395 
396396A plugin installed only at project or local scope doesn't trigger this message. Claude Code lets you install it at user scope as well, so it's available in other projects.
397397 
from line 405
405405 
406406The message names the way out:
407407 
408* **The other plugin is installed**: the message says `Only one of the two can be installed.` and names the `claude plugin uninstall` command, or the uninstall step in `/plugin`, that removes the other plugin. Run it, then install again. For what the uninstall removes, see [What an uninstall deletes and keeps](/docs/en/plugins/cli-reference.md#what-an-uninstall-deletes-and-keeps).
408* **The other plugin is installed**: the message says `Only one of the two can be installed.` and names the `claude plugin uninstall` command, or the uninstall step in `/plugin`, that removes the other plugin. Run it, then install again. For what the uninstall removes, see [What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference.md#what-an-uninstall-deletes-and-keeps).
409409* **Both ids arrive in one install**, such as a plugin and a dependency it needs: no install order helps. Only a maintainer of the marketplace that lists the two plugins can fix it, by renaming one of them. When the two come from different marketplaces, a maintainer of either one can.
410410 
411411### `This plugin uses a source type your Claude Code version does not support`
from line 412
412412 
413413You installed a plugin whose marketplace entry uses a source type this version of Claude Code can't fetch, and Claude Code stopped with this message and `Update Claude Code and try again.`
414414 
415Update Claude Code, then retry the install. Source types are on the [marketplace reference](/docs/en/plugins/marketplace-reference.md).
415Update Claude Code, then retry the install. Source types are on the [marketplace reference](https://code.claude.com/docs/en/plugins/marketplace-reference.md).
416416 
417417### `Plugin archive integrity check failed`
418418 
419You installed a plugin that's distributed as a zip archive, and Claude Code refused it with this line and `The archive was not installed.` The plugin's marketplace entry uses an [`archive` source](/docs/en/plugins/marketplace-reference) with a `sha256` pin, and the downloaded file's digest doesn't match the pin.
419You installed a plugin that's distributed as a zip archive, and Claude Code refused it with this line and `The archive was not installed.` The plugin's marketplace entry uses an [`archive` source](https://code.claude.com/docs/en/plugins/marketplace-reference) with a `sha256` pin, and the downloaded file's digest doesn't match the pin.
420420 
421421The full message looks like this:
422422 
from line 431
431431 
432432### `An npm plugin source must name a registry package`
433433 
434A plugin whose marketplace entry uses an [`npm` source](/docs/en/plugins/marketplace-reference#npm-plugin-source) failed to install, update, or load, and the message includes this sentence. Claude Code checked the entry's `package` value before fetching anything and refused it. The message names the value and the reason:
434A plugin whose marketplace entry uses an [`npm` source](https://code.claude.com/docs/en/plugins/marketplace-reference#npm-plugin-source) failed to install, update, or load, and the message includes this sentence. Claude Code checked the entry's `package` value before fetching anything and refused it. The message names the value and the reason:
435435 
436436```text theme={null}
437437"github:acme/formatter" was not installed: it is not an http or https link. An npm plugin source must name a registry package (name or name@version) or link to a tarball file. For a plugin in a git repository, use a "github", "url" or "git-subdir" source.
from line 439
439439 
440440The marketplace's owner has to change the entry:
441441 
442* **If that's you**: change `package` to a value the [npm plugin source reference](/docs/en/plugins/marketplace-reference.md#npm-plugin-source) accepts, or switch the entry to a `github`, `url`, or `git-subdir` source
442* **If that's you**: change `package` to a value the [npm plugin source reference](https://code.claude.com/docs/en/plugins/marketplace-reference.md#npm-plugin-source) accepts, or switch the entry to a `github`, `url`, or `git-subdir` source
443443* **If it isn't you**: report the message to the marketplace owner
444444 
445445### `Marketplace "<name>" is registered from an untrusted source`
from line 446
446446 
447447A marketplace you added earlier stopped loading, and so did its plugins. This line appears in the `/plugin` **Errors** tab or on the next refresh.
448448 
449The marketplace is registered under a name that is [reserved for official Anthropic marketplaces](/docs/en/plugins/marketplace-reference.md), but its registered source isn't an `anthropics` GitHub repository. Reserved names are re-checked every time a marketplace loads or refreshes, so the marketplace and the plugins installed from it stop loading.
449The marketplace is registered under a name that is [reserved for official Anthropic marketplaces](https://code.claude.com/docs/en/plugins/marketplace-reference.md), but its registered source isn't an `anthropics` GitHub repository. Reserved names are re-checked every time a marketplace loads or refreshes, so the marketplace and the plugins installed from it stop loading.
450450 
451451The full message names the reserved name and the fix:
452452 
from line 475
475475 
476476* `Its location is on a network drive, has "." or ".." in its path, or couldn't be checked`, or the same sentence about `The folder or file it was added from`: the marketplace's directory, or the local path it was added from, is on a network location, has a `.` or `..` segment in its path, or couldn't be checked
477477* `Its git URL can't be used: <reason>` or `Its URL can't be read as an https:// or http:// address`: the entry's recorded source URL is one Claude Code refuses to clone or fetch from
478* `Its source doesn't match its extraKnownMarketplaces entry in user or managed settings`: the entry doesn't match the [`extraKnownMarketplaces`](/docs/en/settings-reference#extraknownmarketplaces) declaration of the same name
478* `Its source doesn't match its extraKnownMarketplaces entry in user or managed settings`: the entry doesn't match the [`extraKnownMarketplaces`](https://code.claude.com/docs/en/settings-reference#extraknownmarketplaces) declaration of the same name
479479 
480When `(see the debug log)` follows `is added but ignored` in place of a reason, Claude Code refuses the marketplace's name, such as [another spelling of a reserved name](/docs/en/errors.md#marketplace-name-is-another-spelling-of-a-reserved-name). The [debug log](/docs/en/debug-your-config.md) names the entry.
480When `(see the debug log)` follows `is added but ignored` in place of a reason, Claude Code refuses the marketplace's name, such as [another spelling of a reserved name](https://code.claude.com/docs/en/errors.md#marketplace-name-is-another-spelling-of-a-reserved-name). The [debug log](https://code.claude.com/docs/en/debug-your-config.md) names the entry.
481481 
482482**What to do:**
483483 
484484* Follow the fix in the message. In your shell, run `claude plugin marketplace remove <name>`, then add the marketplace again from a supported source or a local path and reinstall its plugins, which the remove command uninstalls. The remove command works on an ignored entry
485* To keep a marketplace on a network location, declare it under [`extraKnownMarketplaces`](/docs/en/settings-reference#extraknownmarketplaces) in your user or managed settings; a declaration in a repository's `.claude/settings.json` or `.claude/settings.local.json` doesn't count
485* To keep a marketplace on a network location, declare it under [`extraKnownMarketplaces`](https://code.claude.com/docs/en/settings-reference#extraknownmarketplaces) in your user or managed settings; a declaration in a repository's `.claude/settings.json` or `.claude/settings.local.json` doesn't count
486486* For a source that differs from its settings declaration, re-add the marketplace from the declared source or change the declaration. `claude plugin marketplace add` refuses the same mismatch; see [the matching `Cannot add marketplace` entry](#cannot-add-marketplace-source-doesnt-match)
487487* For a refused name, remove the marketplace, using the command after `Remove it:` when the line gives one; adding it again under the same name is refused again
488488 
from line 518
518518/plugin marketplace add anthropics/claude-plugins-official
519519```
520520 
521Claude Code prints `Successfully added marketplace: claude-plugins-official`, and **Discover** lists its plugins. The [Anthropic marketplaces](/docs/en/plugins/anthropic-marketplaces.md) page lists the other marketplaces you can add.
521Claude Code prints `Successfully added marketplace: claude-plugins-official`, and **Discover** lists its plugins. The [Anthropic marketplaces](https://code.claude.com/docs/en/plugins/anthropic-marketplaces.md) page lists the other marketplaces you can add.
522522 
523523### `Marketplace "<name>" is already added from a different source`
524524 
525You named a new marketplace source with [`--marketplace <source>` on the install command](/docs/en/plugins/install#add-a-marketplace-and-install-in-one-command), in a session or from your shell. The catalog Claude Code fetched from that source has the same name as a marketplace you already added from a different source. Claude Code keeps the existing marketplace instead of replacing it, and the plugin isn't installed.
525You named a new marketplace source with [`--marketplace <source>` on the install command](https://code.claude.com/docs/en/plugins/install#add-a-marketplace-and-install-in-one-command), in a session or from your shell. The catalog Claude Code fetched from that source has the same name as a marketplace you already added from a different source. Claude Code keeps the existing marketplace instead of replacing it, and the plugin isn't installed.
526526 
527527The full message looks like this:
528528 
from line 537
537537 
538538### `Cannot add marketplace "<name>": its source doesn't match its extraKnownMarketplaces entry in user or managed settings`
539539 
540You added a marketplace, and the `name` in its `marketplace.json` already has an [`extraKnownMarketplaces`](/docs/en/settings-reference#extraknownmarketplaces) entry in your user settings or managed settings. The source you gave differs from the one that entry lists, so Claude Code refuses the add and registers nothing.
540You added a marketplace, and the `name` in its `marketplace.json` already has an [`extraKnownMarketplaces`](https://code.claude.com/docs/en/settings-reference#extraknownmarketplaces) entry in your user settings or managed settings. The source you gave differs from the one that entry lists, so Claude Code refuses the add and registers nothing.
541541 
542Two sources match when they have the same type and the same value in every field. An entry that sets a `ref` you didn't pass counts as different. A `github` entry also counts as different when you gave the repository as an `https://github.com/` URL, because Claude Code records that URL as a [`git` source](/docs/en/plugins/marketplace-reference#marketplace-sources). Do one of these:
542Two sources match when they have the same type and the same value in every field. An entry that sets a `ref` you didn't pass counts as different. A `github` entry also counts as different when you gave the repository as an `https://github.com/` URL, because Claude Code records that URL as a [`git` source](https://code.claude.com/docs/en/plugins/marketplace-reference#marketplace-sources). Do one of these:
543543 
544* **Use the declared source**: Claude Code [registers marketplaces declared in settings](/docs/en/settings-reference.md#extraknownmarketplaces) on its own, so first run `/plugin marketplace list` in a session. If the list shows the name, the marketplace is already registered and there's nothing to add.
544* **Use the declared source**: Claude Code [registers marketplaces declared in settings](https://code.claude.com/docs/en/settings-reference.md#extraknownmarketplaces) on its own, so first run `/plugin marketplace list` in a session. If the list shows the name, the marketplace is already registered and there's nothing to add.
545545 
546546 If the list doesn't show it, add it with the source typed the way the entry writes it. For an entry whose `source` object is `{ "source": "github", "repo": "acme-corp/claude-plugins", "ref": "v1.2.0" }`, run this:
547547 
from line 566
566566 
567567### `Could not move the new copy of this plugin version into <path>`
568568 
569When you install a plugin, Claude Code downloads a fresh copy of its files and moves it into that version's folder in the [plugin cache](/docs/en/plugins/loading.md#find-plugins-on-disk). This message means the move failed, usually because another program was using the folder while the install ran. The file-system code appears in parentheses:
569When you install a plugin, Claude Code downloads a fresh copy of its files and moves it into that version's folder in the [plugin cache](https://code.claude.com/docs/en/plugins/loading.md#find-plugins-on-disk). This message means the move failed, usually because another program was using the folder while the install ran. The file-system code appears in parentheses:
570570 
571571```text theme={null}
572572Could not move the new copy of this plugin version into /home/user/.claude/plugins/cache/acme-tools/formatter/1.2.0: the new copy or the version folder stayed busy while the install ran (ENOTEMPTY) — usually a scanner still reading the freshly downloaded files, another program using that folder, or another process re-creating it. The previously installed copy was moved back. Run the install again once other Claude Code sessions or programs using that folder have finished.
from line 594
594594* **During install**: the refusal comes back as the install's error message
595595* **When the plugin loads**: the problem appears in `claude plugin list` and the `/plugin` **Errors** tab, and Claude Code keeps the affected plugin disabled until you resolve it
596596 
597The table lists each message and its fix. To declare dependencies as an author, see [Plugin dependencies](/docs/en/plugins/dependencies.md).
597The table lists each message and its fix. To declare dependencies as an author, see [Plugin dependencies](https://code.claude.com/docs/en/plugins/dependencies.md).
598598 
599599| Message | Meaning | How to resolve |
600600| :- | :- | :- |
from line 640
640640 
641641The install summary in `/plugin` ended with `Run /reload-plugins to apply.` instead of `Plugin is now active.` A `Plugins changed. Run /reload-plugins to activate.` notice can appear above the prompt at the same time.
642642 
643Claude Code didn't activate the plugin during the install, either because activating it would [invalidate the prompt cache](/docs/en/prompt-caching.md#enabling-or-disabling-a-plugin) or because the activation attempt failed.
643Claude Code didn't activate the plugin during the install, either because activating it would [invalidate the prompt cache](https://code.claude.com/docs/en/prompt-caching.md#enabling-or-disabling-a-plugin) or because the activation attempt failed.
644644 
645645You don't need to type the command. The panel closes and Claude Code runs `/reload-plugins` for you, or queues it until the response that's streaming finishes.
646646 
from line 705
705705 
706706An `installed_plugins.json` that isn't valid JSON, or isn't a list of plugins, can't say what you installed.
707707 
708Open the `.kept` file to see what the old file recorded, and reinstall the plugins you're missing. Claude Code never reads the file back, and the file ages out on the [`cleanupPeriodDays`](/docs/en/settings-reference#cleanupperioddays) schedule.
708Open the `.kept` file to see what the old file recorded, and reinstall the plugins you're missing. Claude Code never reads the file back, and the file ages out on the [`cleanupPeriodDays`](https://code.claude.com/docs/en/settings-reference#cleanupperioddays) schedule.
709709 
710710### `install records under names that no version of Claude Code can use were removed from installed_plugins.json`
711711 
from line 713
713713 
714714A record in `installed_plugins.json` sat under a key that isn't a valid plugin id, so no version of Claude Code can use it. The rest of the file loads normally.
715715 
716Claude Code copies the unusable records into the `.set-aside` file and drops them from the list. Claude Code never reads the copies back, and the copies age out on the [`cleanupPeriodDays`](/docs/en/settings-reference#cleanupperioddays) schedule.
716Claude Code copies the unusable records into the `.set-aside` file and drops them from the list. Claude Code never reads the copies back, and the copies age out on the [`cleanupPeriodDays`](https://code.claude.com/docs/en/settings-reference#cleanupperioddays) schedule.
717717 
718718### `does not load (...), so Claude Code ignores the whole file`
719719 
720720The command worked. The settings file the warning names has an error, so Claude Code ignores the whole file, including anything the command wrote there, until you fix it.
721721 
722Fix the error the warning names. For a value Claude Code doesn't accept, [Fix a broken settings file](/docs/en/settings.md#fix-a-broken-settings-file) says how. Then run the command again if its change is no longer in the file.
722Fix the error the warning names. For a value Claude Code doesn't accept, [Fix a broken settings file](https://code.claude.com/docs/en/settings.md#fix-a-broken-settings-file) says how. Then run the command again if its change is no longer in the file.
723723 
724724The warning follows the success line of `claude plugin install`, `enable`, `disable`, or `claude plugin marketplace add` in your shell:
725725 
from line 729
729729 
730730The text in parentheses names the error:
731731 
732* **`its "<key>" is not valid`**: the quoted setting holds a value Claude Code doesn't accept. Look up the setting in the [settings reference](/docs/en/settings-reference.md) for the values it takes. When more than one value fails, the text names the first setting and counts the others, as in `its "permissions" and 1 other value are not valid`.
732* **`its "<key>" is not valid`**: the quoted setting holds a value Claude Code doesn't accept. Look up the setting in the [settings reference](https://code.claude.com/docs/en/settings-reference.md) for the values it takes. When more than one value fails, the text names the first setting and counts the others, as in `its "permissions" and 1 other value are not valid`.
733733* **`it is not a JSON object`**: the file's top level isn't a JSON object, such as a file whose top level is an array.
734734 
735735### `Disabled in ~/.claude/settings.json but still loads`
from line 736
736736 
737737You set a plugin to `false` in `~/.claude/settings.json`, and its row in `claude plugin list` or `/plugin` shows this message followed by the source that enables it, such as `— project settings enable it, which overrides your user setting`. A `true` in that higher-precedence source is overriding your user setting.
738738 
739To opt out of a project-enabled plugin on your machine, set the id to `false` in `.claude/settings.local.json`, which has higher precedence than the project file. For the other sources the message can name, see [Disabled in user settings but still loads](/docs/en/plugins/loading.md#disabled-in-user-settings-but-still-loads).
739To opt out of a project-enabled plugin on your machine, set the id to `false` in `.claude/settings.local.json`, which has higher precedence than the project file. For the other sources the message can name, see [Disabled in user settings but still loads](https://code.claude.com/docs/en/plugins/loading.md#disabled-in-user-settings-but-still-loads).
740740 
741If `claude plugin list` instead marks the plugin `required by your org`, no settings file is involved: your organization marks that synced plugin as required on claude.ai, and it loads even if you disabled it earlier. See [Plugins synced from claude.ai](/docs/en/plugins/loading.md#synced-plugins).
741If `claude plugin list` instead marks the plugin `required by your org`, no settings file is involved: your organization marks that synced plugin as required on claude.ai, and it loads even if you disabled it earlier. See [Plugins synced from claude.ai](https://code.claude.com/docs/en/plugins/loading.md#synced-plugins).
742742 
743743### `Plugin "<name>" is enabled in project settings but isn't installed here`
744744 
from line 752
752752 
753753After you run `/reload-plugins` in your session, the **Errors** tab entry is gone and the plugin is listed under **Installed**.
754754 
755If your organization pre-installs plugins for you, it does so through managed settings instead. See [Pre-install and require plugins](/docs/en/plugins/org.md#pre-install-and-require-plugins).
755If your organization pre-installs plugins for you, it does so through managed settings instead. See [Pre-install and require plugins](https://code.claude.com/docs/en/plugins/org.md#pre-install-and-require-plugins).
756756 
757757### A plugin stays installed after `plugin uninstall` on Windows
758758 
from line 778
778778 
779779**1. Update Claude Code**
780780 
781[Update Claude Code](/docs/en/setup.md#update-claude-code) to v2.1.302 or later, which clones in a format it can read the commit from:
781[Update Claude Code](https://code.claude.com/docs/en/setup.md#update-claude-code) to v2.1.302 or later, which clones in a format it can read the commit from:
782782 
783783```shell theme={null}
784784claude update
from line 802
802802 
803803**4. Check the version**
804804 
805Check the `Version:` line for a value such as a [12-character commit ID](/docs/en/plugins/loading.md#how-claude-code-computes-the-version):
805Check the `Version:` line for a value such as a [12-character commit ID](https://code.claude.com/docs/en/plugins/loading.md#how-claude-code-computes-the-version):
806806 
807807```shell theme={null}
808808claude plugin list
809809```
810810 
811`claude plugin update` [changes one scope's block per run](/docs/en/plugins/cli-reference.md#which-scope-the-command-updates). For a block still showing `unknown`, rerun it with that block's `Scope:` value:
811`claude plugin update` [changes one scope's block per run](https://code.claude.com/docs/en/plugins/cli-reference.md#which-scope-the-command-updates). For a block still showing `unknown`, rerun it with that block's `Scope:` value:
812812 
813813```shell theme={null}
814814claude plugin update formatter@my-marketplace --scope project
815815```
816816 
817If the block still shows `unknown` after the rerun, see [Get more help](/docs/en/troubleshooting.md#get-more-help).
817If the block still shows `unknown` after the rerun, see [Get more help](https://code.claude.com/docs/en/troubleshooting.md#get-more-help).
818818 
819819### `Failed to load hooks from <path>` and hooks that don't fire
820820 
from line 826
826826 
827827* **`Failed to load hooks from <path>: <reason>`**: `hooks/hooks.json` isn't valid JSON or fails the hooks schema. The reason names the parse or validation error. Fix the file. To catch a JSON syntax problem in `hooks/hooks.json` before you publish the plugin, run `claude plugin validate <plugin-directory>` in your shell
828828* **`hooks path not found: <path>`**: the manifest's `hooks` field names a file that doesn't exist at that path relative to the plugin root. Fix the path or add the file
829* **`hooks path escapes plugin directory: <path>`**: see [Path escapes plugin directory](/docs/en/errors.md#path-escapes-plugin-directory)
829* **`hooks path escapes plugin directory: <path>`**: see [Path escapes plugin directory](https://code.claude.com/docs/en/errors.md#path-escapes-plugin-directory)
830830 
831831#### `hook error` notices in the transcript
832832 
833833A notice of the form `... hook error: Failed with non-blocking status code: <stderr>` means the hook ran and its command failed. For example, `Stop hook error: Failed with non-blocking status code: /bin/sh: node: command not found` means the shell Claude Code spawned couldn't find `node`. Install it, or make sure it's on the `PATH` of the terminal you start `claude` from.
834834 
835If the stderr shows the plugin's path cut off at a space, the hook's shell-form command uses `${CLAUDE_PLUGIN_ROOT}` outside quotes and the install path contains a space. Wrap the variable in double quotes or use [exec form](/docs/en/hooks.md#exec-form-and-shell-form). To find the unquoted variable, run `claude plugin validate` on the plugin's directory and look for its [quoting warning](/docs/en/plugins/manifest-reference.md#quoting-and-path-separators).
835If the stderr shows the plugin's path cut off at a space, the hook's shell-form command uses `${CLAUDE_PLUGIN_ROOT}` outside quotes and the install path contains a space. Wrap the variable in double quotes or use [exec form](https://code.claude.com/docs/en/hooks.md#exec-form-and-shell-form). To find the unquoted variable, run `claude plugin validate` on the plugin's directory and look for its [quoting warning](https://code.claude.com/docs/en/plugins/manifest-reference.md#quoting-and-path-separators).
836836 
837837If the notice reads `Failed to run: Plugin directory does not exist: <path>`, see [`Plugin directory does not exist`](#plugin-directory-does-not-exist).
838838 
839For any other error, run the hook's command yourself from the plugin directory to see the full output, or capture the full stderr with [debug logging](/docs/en/hooks.md#debug-hooks).
839For any other error, run the hook's command yourself from the plugin directory to see the full output, or capture the full stderr with [debug logging](https://code.claude.com/docs/en/hooks.md#debug-hooks).
840840 
841841#### A plugin hook blocks a tool call or prompt
842842 
843A hook that exits with code 2 [blocks the action it ran for](/docs/en/hooks.md#exit-code-2). When a plugin's hook blocks this way and its stderr is the blocking message, the error ends with `This hook comes from the <plugin> plugin.` so that you know which plugin to disable or fix. Before v2.1.281, the error didn't name the plugin.
843A hook that exits with code 2 [blocks the action it ran for](https://code.claude.com/docs/en/hooks.md#exit-code-2). When a plugin's hook blocks this way and its stderr is the blocking message, the error ends with `This hook comes from the <plugin> plugin.` so that you know which plugin to disable or fix. Before v2.1.281, the error didn't name the plugin.
844844 
845845If that message shows the plugin's path cut off at a space, apply the [unquoted `${CLAUDE_PLUGIN_ROOT}` fix](#hook-error-notices-in-the-transcript).
846846 
847847#### Hook loads but never fires
848848 
849If a hook loads without error but never fires, first run `/plugin` in your session and open the plugin's details. A note there that begins `Its hooks.json hooks do not run` means another enabled plugin with the same name registered its hooks instead, and [Hooks when two enabled plugins share a name](/docs/en/plugins/loading.md#hooks-when-two-enabled-plugins-share-a-name) says which copy that is and how to switch. Otherwise, check the hook's definition and then watch it run:
849If a hook loads without error but never fires, first run `/plugin` in your session and open the plugin's details. A note there that begins `Its hooks.json hooks do not run` means another enabled plugin with the same name registered its hooks instead, and [Hooks when two enabled plugins share a name](https://code.claude.com/docs/en/plugins/loading.md#hooks-when-two-enabled-plugins-share-a-name) says which copy that is and how to switch. Otherwise, check the hook's definition and then watch it run:
850850 
851851**1. Check the event name**
852852 
from line 862
862862 
863863**4. Read the debug log**
864864 
865Open the [debug log](/docs/en/hooks.md#debug-hooks), which records which hooks matched. A hook that ran shows up there with its exit code.
865Open the [debug log](https://code.claude.com/docs/en/hooks.md#debug-hooks), which records which hooks matched. A hook that ran shows up there with its exit code.
866866 
867867### `Plugin directory does not exist: <path>`
868868 
from line 884
884884 
885885* **`Missing environment variables: <names>`**: set those variables in the shell you start Claude Code from, then start a new session
886886* **`URL is unset or invalid`**: a `${user_config.*}` option that the URL uses isn't set. Run `/plugin configure <plugin>` to set it
887* **`has an invalid MCP url`** or **`headersHelper for MCP server '<server>' references ${user_config.*}`**: the plugin's own configuration is at fault. Fix the `url` or `headersHelper` in your plugin's MCP configuration, or report it to the plugin's author if the plugin isn't yours. The `headersHelper` case has its own entry under [plugin command references user\_config](/docs/en/errors.md#plugin-command-references-user-config)
887* **`has an invalid MCP url`** or **`headersHelper for MCP server '<server>' references ${user_config.*}`**: the plugin's own configuration is at fault. Fix the `url` or `headersHelper` in your plugin's MCP configuration, or report it to the plugin's author if the plugin isn't yours. The `headersHelper` case has its own entry under [plugin command references user\_config](https://code.claude.com/docs/en/errors.md#plugin-command-references-user-config)
888888 
889889#### `Bundled MCP server "<name>" was not started: it needs configuration`
890890 
891The plugin includes the server as an [MCPB bundle](/docs/en/plugins/components.md#include-a-packaged-mcpb-server) that declares `user_config`, and a required setting has no saved value yet or a saved value fails the bundle's own validation, so Claude Code skips starting the server. The rest of the plugin works.
891The plugin includes the server as an [MCPB bundle](https://code.claude.com/docs/en/plugins/components.md#include-a-packaged-mcpb-server) that declares `user_config`, and a required setting has no saved value yet or a saved value fails the bundle's own validation, so Claude Code skips starting the server. The rest of the plugin works.
892892 
893Select the plugin on `/plugin`'s **Installed** tab and choose **Configure** to supply the values. After you save, `/plugin` shows `Configuration saved.` and closes, and Claude Code reloads plugins as described under [Manage installed plugins](/docs/en/plugins/install.md#manage-installed-plugins). The server starts once that reload applies. Before v2.1.285, Claude Code skipped the server without showing this line.
893Select the plugin on `/plugin`'s **Installed** tab and choose **Configure** to supply the values. After you save, `/plugin` shows `Configuration saved.` and closes, and Claude Code reloads plugins as described under [Manage installed plugins](https://code.claude.com/docs/en/plugins/install.md#manage-installed-plugins). The server starts once that reload applies. Before v2.1.285, Claude Code skipped the server without showing this line.
894894 
895895#### Server is configured but never connects
896896 
from line 900
900900 
901901> [!NOTE]
902902>
903> When a connection to a [plugin's stdio server](/docs/en/mcp.md#plugin-provided-mcp-servers) failed less than 15 minutes before you ran `claude --debug`, Claude Code [doesn't start the server](/docs/en/errors.md#skipping-connection-recent-failure-cached). In the new session, select **Reconnect** from the server's menu in `/mcp` to start it, then open the log.
903> When a connection to a [plugin's stdio server](https://code.claude.com/docs/en/mcp.md#plugin-provided-mcp-servers) failed less than 15 minutes before you ran `claude --debug`, Claude Code [doesn't start the server](https://code.claude.com/docs/en/errors.md#skipping-connection-recent-failure-cached). In the new session, select **Reconnect** from the server's menu in `/mcp` to start it, then open the log.
904904 
905905A server entry in `.mcp.json` that fails the schema doesn't appear in the **Errors** tab. Claude Code drops that server and records `Invalid MCP server config for <server> in <path>` only in that debug log. To find the entry without loading the plugin, run `claude plugin validate` in your shell on the plugin directory, which reports it as an error.
906906 
from line 916
916916 
917917### Language server doesn't start, uses too much memory, or reports wrong diagnostics
918918 
919You installed a [code intelligence plugin](/docs/en/plugins/code-intelligence.md) and Claude isn't seeing diagnostics, or the language server is using too much memory or reporting errors that aren't real.
919You installed a [code intelligence plugin](https://code.claude.com/docs/en/plugins/code-intelligence.md) and Claude isn't seeing diagnostics, or the language server is using too much memory or reporting errors that aren't real.
920920 
921921#### Language server doesn't start
922922 
from line 936
936936 
937937## Build a plugin
938938 
939You're developing a plugin and loading it with `--plugin-dir` or installing it from a local marketplace. These entries cover the failures you hit while developing a plugin. For the checks to run after each change, see [Test and debug](/docs/en/plugins/create.md#test-and-debug).
939You're developing a plugin and loading it with `--plugin-dir` or installing it from a local marketplace. These entries cover the failures you hit while developing a plugin. For the checks to run after each change, see [Test and debug](https://code.claude.com/docs/en/plugins/create.md#test-and-debug).
940940 
941941Two failures that also reach a plugin's users have their entries under [Plugin installed but not working](#plugin-installed-but-not-working):
942942 
from line 951
951951 
952952> [!NOTE]
953953>
954> Before v2.1.299, this message could appear at startup on a row labeled with a tag such as `synced[0]`. That row is for a plugin [synced from claude.ai](/docs/en/plugins/loading.md#synced-plugins). It appeared when another Claude Code session on your machine had updated the plugin moments before and moved its files. To load the plugin, restart Claude Code. To keep the message from appearing for this reason, update to v2.1.299 or later.
954> Before v2.1.299, this message could appear at startup on a row labeled with a tag such as `synced[0]`. That row is for a plugin [synced from claude.ai](https://code.claude.com/docs/en/plugins/loading.md#synced-plugins). It appeared when another Claude Code session on your machine had updated the plugin moments before and moved its files. To load the plugin, restart Claude Code. To keep the message from appearing for this reason, update to v2.1.299 or later.
955955 
956956Paths in the manifest are relative to the plugin root and start with `./`. A path that resolves outside the plugin root is reported as `<component> path escapes plugin directory` instead and is dropped.
957957 
from line 971
971971 
972972A plugin works from its source directory with `--plugin-dir` but fails after install, with errors about a path such as `../shared-utils`.
973973 
974Claude Code copies an installed plugin into its cache and loads it from there, so a path that reaches outside the plugin's own directory points at nothing in the cache. Move the shared files inside the plugin directory, or reference them through a symlink inside it. For where the cache is and how paths resolve, see [Find plugins on disk](/docs/en/plugins/loading.md#find-plugins-on-disk).
974Claude Code copies an installed plugin into its cache and loads it from there, so a path that reaches outside the plugin's own directory points at nothing in the cache. Move the shared files inside the plugin directory, or reference them through a symlink inside it. For where the cache is and how paths resolve, see [Find plugins on disk](https://code.claude.com/docs/en/plugins/loading.md#find-plugins-on-disk).
975975 
976976### `${CLAUDE_PLUGIN_ROOT}` shows forward slashes on Windows
977977 
from line 979
979979 
980980Claude Code runs shell-form hooks through Git Bash on Windows and substitutes the plugin root in the forward-slash Win32 form on purpose. Bash builtins, MSYS tools, and native Windows binaries all accept that form.
981981 
982If your script needs backslashes, switch the hook to one of the forms that keep native paths, described under [exec form and shell form](/docs/en/hooks.md#exec-form-and-shell-form):
982If your script needs backslashes, switch the hook to one of the forms that keep native paths, described under [exec form and shell form](https://code.claude.com/docs/en/hooks.md#exec-form-and-shell-form):
983983 
984984* An exec-form hook, which spawns the process directly with an `args` array
985985* A hook with `"shell": "powershell"`
from line 998
998998 
999999Check these causes in order:
10001000 
1001* **The skill sets `disable-model-invocation: true`**: the template skill in [Create your first plugin](/docs/en/plugins/create.md#create-your-first-plugin) sets it. Remove the line from a skill you want Claude to invoke on its own. [Control who invokes a skill](/docs/en/skills.md#control-who-invokes-a-skill) covers the field
1002* **The description doesn't match how people ask**: work through the checks in [Skill not triggering](/docs/en/skills.md#skill-not-triggering)
1003* **The description is truncated**: when many skills are installed, Claude Code shortens descriptions to fit the listing's character budget, which can strip the keywords Claude needs to match a request. See [Skill descriptions are cut short](/docs/en/skills.md#skill-descriptions-are-cut-short)
1001* **The skill sets `disable-model-invocation: true`**: the template skill in [Create your first plugin](https://code.claude.com/docs/en/plugins/create.md#create-your-first-plugin) sets it. Remove the line from a skill you want Claude to invoke on its own. [Control who invokes a skill](https://code.claude.com/docs/en/skills.md#control-who-invokes-a-skill) covers the field
1002* **The description doesn't match how people ask**: work through the checks in [Skill not triggering](https://code.claude.com/docs/en/skills.md#skill-not-triggering)
1003* **The description is truncated**: when many skills are installed, Claude Code shortens descriptions to fit the listing's character budget, which can strip the keywords Claude needs to match a request. See [Skill descriptions are cut short](https://code.claude.com/docs/en/skills.md#skill-descriptions-are-cut-short)
10041004 
1005To measure how often the skill triggers across realistic prompts rather than checking one at a time, write an eval case with a [`tool_used: Skill` grader](/docs/en/plugin-evals#create-your-first-eval-suite) and run it with `claude plugin eval` after each description change.
1005To measure how often the skill triggers across realistic prompts rather than checking one at a time, write an eval case with a [`tool_used: Skill` grader](https://code.claude.com/docs/en/plugin-evals#create-your-first-eval-suite) and run it with `claude plugin eval` after each description change.
10061006 
10071007### `<directory> is not a plugin or skill folder` from `claude plugin eval init`
10081008 
10091009You ran `claude plugin eval init` from a directory that isn't a plugin's root, such as your home directory or the root of a repository that keeps the plugin in a subdirectory. `init` writes the suite under the working directory, so it stops instead of creating an `evals/` directory the plugin would never see.
10101010 
1011Change to the plugin's root, the directory that holds `.claude-plugin/plugin.json` or the skill's `SKILL.md`, and run the command again. To scaffold the suite somewhere else on purpose, pass `--eval-dir`. See [Test plugins with evals](/docs/en/plugin-evals.md).
1011Change to the plugin's root, the directory that holds `.claude-plugin/plugin.json` or the skill's `SKILL.md`, and run the command again. To scaffold the suite somewhere else on purpose, pass `--eval-dir`. See [Test plugins with evals](https://code.claude.com/docs/en/plugin-evals.md).
10121012 
10131013### The `userConfig` dialog never appears
10141014 
from line 1028
10281028 
10291029When every option is set, the install output carries no `not yet set` line.
10301030 
1031To open the dialog afterwards instead, run `/plugin configure my-plugin@my-marketplace` in a session. From the shell, [`claude plugin configure`](/docs/en/plugins/cli-reference#plugin-configure) shows which options are still unset and saves values piped in on stdin. It requires Claude Code v2.1.285 or later.
1031To open the dialog afterwards instead, run `/plugin configure my-plugin@my-marketplace` in a session. From the shell, [`claude plugin configure`](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) shows which options are still unset and saves values piped in on stdin. It requires Claude Code v2.1.285 or later.
10321032 
10331033If you pass a `--config` key the manifest doesn't declare, the plugin still installs, and the command prints `⚠ Installed, but --config not applied: --config key "<key>" isn't declared in this plugin's userConfig.` followed by the keys the plugin does declare.
10341034 
1035For a plugin that ships an [MCPB bundle file](/docs/en/plugins/components.md#include-a-packaged-mcpb-server) declaring `user_config` of its own, the message reads `isn't declared in this plugin's userConfig or by its bundled MCP servers.` instead, and the known keys include that server's keys, written `<server>.<key>`. A bundle the manifest references by URL isn't read at install time, so its keys aren't listed and the message says to configure it in `/plugin`. Setting `<server>.<key>` keys requires Claude Code v2.1.285 or later.
1035For a plugin that ships an [MCPB bundle file](https://code.claude.com/docs/en/plugins/components.md#include-a-packaged-mcpb-server) declaring `user_config` of its own, the message reads `isn't declared in this plugin's userConfig or by its bundled MCP servers.` instead, and the known keys include that server's keys, written `<server>.<key>`. A bundle the manifest references by URL isn't read at install time, so its keys aren't listed and the message says to configure it in `/plugin`. Setting `<server>.<key>` keys requires Claude Code v2.1.285 or later.
10361036 
10371037### `claude plugin validate` reports errors
10381038 
from line 1051
10511051| `Path contains ".." which could be a path traversal attempt: <path>` | A component path escapes the plugin directory. | Use paths inside the plugin root. |
10521052| `Path is a file; skills entries must be directories containing SKILL.md` | A `skills` entry points at `SKILL.md` instead of its directory. | Point at the parent directory, or `.` for a root-level `SKILL.md`. |
10531053| `No frontmatter block found` or `YAML frontmatter failed to parse: <error>` | A skill, agent, or command file has missing or invalid YAML frontmatter. | Add or fix the frontmatter between `---` delimiters. Reported when validating a plugin directory. |
1054| `Plugin name "<name>" is reserved: it passes as one of Anthropic's own` | The plugin's `name` is one of the [reserved names](/docs/en/plugins/manifest-reference.md#name). | Rename the plugin for what it does. |
1055| `Unknown field '<key>'` | The manifest has a field the schema doesn't define. | Remove it, or use the name the message suggests. Claude Code ignores unknown fields at load time. For `privacyPolicyUrl` and the other directory listing fields in `plugin.json`, see [Directory listing fields](/docs/en/plugins/manifest-reference.md#directory-listing-fields). |
1054| `Plugin name "<name>" is reserved: it passes as one of Anthropic's own` | The plugin's `name` is one of the [reserved names](https://code.claude.com/docs/en/plugins/manifest-reference.md#name). | Rename the plugin for what it does. |
1055| `Unknown field '<key>'` | The manifest has a field the schema doesn't define. | Remove it, or use the name the message suggests. Claude Code ignores unknown fields at load time. For `privacyPolicyUrl` and the other directory listing fields in `plugin.json`, see [Directory listing fields](https://code.claude.com/docs/en/plugins/manifest-reference.md#directory-listing-fields). |
10561056 
10571057Run the command again after each fix until it prints no errors.
10581058 
1059`plugin.json` fields are on the [manifest reference](/docs/en/plugins/manifest-reference.md), and marketplace-level messages are under [Marketplace validation errors](#marketplace-validation-errors).
1059`plugin.json` fields are on the [manifest reference](https://code.claude.com/docs/en/plugins/manifest-reference.md), and marketplace-level messages are under [Marketplace validation errors](#marketplace-validation-errors).
10601060 
10611061### `Plugin <name> has conflicting manifests`
10621062 
10631063The plugin fails to load with `Plugin <name> has conflicting manifests: both plugin.json and marketplace entry specify components.`
10641064 
1065The plugin has its own `plugin.json`, and its marketplace entry sets `strict: false` while also declaring any of `commands`, `agents`, `skills`, `hooks`, `outputStyles`, or `themes`. Remove those fields from the entry, or set `strict: true` in the entry so Claude Code appends them to `plugin.json`. See [Strict mode](/docs/en/plugins/marketplace-reference.md#strict-mode).
1065The plugin has its own `plugin.json`, and its marketplace entry sets `strict: false` while also declaring any of `commands`, `agents`, `skills`, `hooks`, `outputStyles`, or `themes`. Remove those fields from the entry, or set `strict: true` in the entry so Claude Code appends them to `plugin.json`. See [Strict mode](https://code.claude.com/docs/en/plugins/marketplace-reference.md#strict-mode).
10661066 
10671067### `Warning: No commands found in plugin <name> custom directory`
10681068 
from line 1076
10761076 
10771077### Plugins with relative paths fail in URL-based marketplaces
10781078 
1079Users added your marketplace with an `https://example.com/marketplace.json` URL. Installs of plugins whose `source` is a relative path, such as `./plugins/my-plugin`, fail with `its marketplace entry path does not stay inside the marketplace directory`. Already-installed plugins fail to load with `Plugin source path refused`. Both messages have an [error reference entry](/docs/en/errors.md#marketplace-entry-path-does-not-stay-inside-the-marketplace-directory).
1079Users added your marketplace with an `https://example.com/marketplace.json` URL. Installs of plugins whose `source` is a relative path, such as `./plugins/my-plugin`, fail with `its marketplace entry path does not stay inside the marketplace directory`. Already-installed plugins fail to load with `Plugin source path refused`. Both messages have an [error reference entry](https://code.claude.com/docs/en/errors.md#marketplace-entry-path-does-not-stay-inside-the-marketplace-directory).
10801080 
10811081When a user adds a URL-based marketplace, Claude Code downloads only the `marketplace.json` file itself. It doesn't fetch plugin files by relative path from that server, so a relative path in an entry points at a directory that was never fetched. Give each entry a source Claude Code can fetch on its own, such as a GitHub repository:
10821082 
from line 1084
10841084{ "name": "my-plugin", "source": { "source": "github", "repo": "owner/repo" } }
10851085```
10861086 
1087Alternatively, host the marketplace in a git repository and tell users to add it with the repository URL. For a git source, Claude Code clones the whole repository, so relative paths resolve. Source types are on the [marketplace reference](/docs/en/plugins/marketplace-reference.md).
1087Alternatively, host the marketplace in a git repository and tell users to add it with the repository URL. For a git source, Claude Code clones the whole repository, so relative paths resolve. Source types are on the [marketplace reference](https://code.claude.com/docs/en/plugins/marketplace-reference.md).
10881088 
10891089### Marketplace validation errors
10901090 
from line 1113
11131113 
11141114## Blocked by your organization
11151115 
1116Your organization deploys managed settings that restrict plugins, and a command was refused with a policy message. These entries name the setting behind each refusal so you know what to ask your administrator for. For the admin side, see [Manage plugins for your organization](/docs/en/plugins/org.md).
1116Your organization deploys managed settings that restrict plugins, and a command was refused with a policy message. These entries name the setting behind each refusal so you know what to ask your administrator for. For the admin side, see [Manage plugins for your organization](https://code.claude.com/docs/en/plugins/org.md).
11171117 
11181118### `Marketplace source '<source>' is blocked by enterprise policy`
11191119 
from line 1167
11671167 
11681168An operator pre-populated this marketplace through `CLAUDE_CODE_PLUGIN_SEED_DIR`, and Claude Code treats a seed-managed marketplace as read-only. A bulk `marketplace update` skips it and updates the others.
11691169 
1170To change the marketplace's content, ask the person who maintains the seed image to update it. For the procedure, see [Seed containers and CI](/docs/en/plugins/org.md#seed-containers-and-ci).
1170To change the marketplace's content, ask the person who maintains the seed image to update it. For the procedure, see [Seed containers and CI](https://code.claude.com/docs/en/plugins/org.md#seed-containers-and-ci).
11711171 
11721172## Next steps
11731173 
1174* [Plugin loading reference](/docs/en/plugins/loading.md): why scopes, the cache, and precedence behave the way they do
1175* [Plugin commands reference](/docs/en/plugins/cli-reference.md): flags, defaults, output, and exit codes for the `claude plugin` commands
1176* [Install and manage plugins](/docs/en/plugins/install.md): the install steps from the start
1177* [Manage plugins for your organization](/docs/en/plugins/org.md#troubleshoot-policy): policy-side troubleshooting for administrators
1174* [Plugin loading reference](https://code.claude.com/docs/en/plugins/loading.md): why scopes, the cache, and precedence behave the way they do
1175* [Plugin commands reference](https://code.claude.com/docs/en/plugins/cli-reference.md): flags, defaults, output, and exit codes for the `claude plugin` commands
1176* [Install and manage plugins](https://code.claude.com/docs/en/plugins/install.md): the install steps from the start
1177* [Manage plugins for your organization](https://code.claude.com/docs/en/plugins/org.md#troubleshoot-policy): policy-side troubleshooting for administrators
11781178 

prompt-caching Changed · +80 / -80 lines

from line 24
2424 
2525A change to the conversation layer leaves the system prompt and project context cached. A change to the system prompt invalidates everything, because all later content now sits behind a different prefix. The third column gives common triggers rather than an exhaustive list, and the sections below cover the full set.
2626 
27The prefix-match rule explains most of the behaviors on this page. [Plan mode](/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode) and [skill loading](/docs/en/skills.md), for example, append their instructions as conversation messages, so the cached prefix stays intact.
27The prefix-match rule explains most of the behaviors on this page. [Plan mode](https://code.claude.com/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode) and [skill loading](https://code.claude.com/docs/en/skills.md), for example, append their instructions as conversation messages, so the cached prefix stays intact.
2828 
2929Two settings don't appear in the layer table but still affect what stays cached:
3030 
from line 39
3939 
4040Caching happens server-side, in whichever infrastructure serves your model. Where that is depends on how you authenticate:
4141 
42* **API key, Claude subscription, or [Claude Platform on AWS](/docs/en/claude-platform-on-aws.md)**: the cache lives in Anthropic's infrastructure, accessed through the [Claude API](https://platform.claude.com/docs)
42* **API key, Claude subscription, or [Claude Platform on AWS](https://code.claude.com/docs/en/claude-platform-on-aws.md)**: the cache lives in Anthropic's infrastructure, accessed through the [Claude API](https://platform.claude.com/docs)
4343* **Amazon Bedrock or Google Cloud's Agent Platform**: the cache lives in your cloud provider's serving infrastructure
4444* **Microsoft Foundry**: depends on the deployment's [hosting option](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options). Hosted on Azure deployments are served on Azure infrastructure; Hosted on Anthropic deployments are served on Anthropic's infrastructure
45* **Custom `ANTHROPIC_BASE_URL` or [LLM gateway](/docs/en/llm-gateway)**: the cache lives wherever your requests are forwarded, and whether caching works depends on the gateway
45* **Custom `ANTHROPIC_BASE_URL` or [LLM gateway](https://code.claude.com/docs/en/llm-gateway)**: the cache lives wherever your requests are forwarded, and whether caching works depends on the gateway
4646 
47Claude Code also appends system context mid-conversation, such as file-change notices, and marks that block for caching on every provider and connection unless you set [`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS`](/docs/en/llm-gateway-protocol#disable-pre-release-capabilities), in which case that block is sent uncached.
47Claude Code also appends system context mid-conversation, such as file-change notices, and marks that block for caching on every provider and connection unless you set [`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS`](https://code.claude.com/docs/en/llm-gateway-protocol#disable-pre-release-capabilities), in which case that block is sent uncached.
4848 
49At the provider's own endpoint, Amazon Bedrock and its [Mantle endpoint](/docs/en/amazon-bedrock.md#use-the-mantle-endpoint), Google Cloud's Agent Platform, and Microsoft Foundry cache the block the same way the Claude API does.
49At the provider's own endpoint, Amazon Bedrock and its [Mantle endpoint](https://code.claude.com/docs/en/amazon-bedrock.md#use-the-mantle-endpoint), Google Cloud's Agent Platform, and Microsoft Foundry cache the block the same way the Claude API does.
5050 
51When your requests pass through an [LLM gateway](/docs/en/llm-gateway), a custom `ANTHROPIC_BASE_URL`, or a cloud provider base-URL override such as [`ANTHROPIC_BEDROCK_BASE_URL`](/docs/en/env-vars), what stays cached depends on how the gateway handles the [`cache_control` markers](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#explicit-cache-breakpoints) Claude Code sends:
51When your requests pass through an [LLM gateway](https://code.claude.com/docs/en/llm-gateway), a custom `ANTHROPIC_BASE_URL`, or a cloud provider base-URL override such as [`ANTHROPIC_BEDROCK_BASE_URL`](https://code.claude.com/docs/en/env-vars), what stays cached depends on how the gateway handles the [`cache_control` markers](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#explicit-cache-breakpoints) Claude Code sends:
5252 
5353* **Forwards them unchanged**: the block and your conversation cache the same as at the provider's own endpoint.
5454* **Rejects the marked request with a `400` error naming `cache_control`**: Claude Code re-sends the request with the marker moved off the block and onto your last conversation message, and keeps it there until Claude Code exits. The block bills as uncached input; your conversation stays cached.
5555* **Removes the markers while returning success**: your entire conversation history bills as uncached input on every turn. A gateway that converts block-form system content to a plain string drops the marker the same way.
5656 
57For what each provider stores and processes, see [data usage](/docs/en/data-usage.md). Wherever the cache lives, entries expire after a period of inactivity, and [Cache lifetime](#cache-lifetime) below covers the TTL and how to extend it.
57For what each provider stores and processes, see [data usage](https://code.claude.com/docs/en/data-usage.md). Wherever the cache lives, entries expire after a period of inactivity, and [Cache lifetime](#cache-lifetime) below covers the TTL and how to extend it.
5858 
5959## Actions that invalidate the cache
6060 
from line 72
7272 
7373### Switching models
7474 
75Each model has its own cache. Switching with [`/model`](/docs/en/model-config#setting-your-model) means the next request reads the entire conversation history with no cache hits, even though the content is identical.
75Each model has its own cache. Switching with [`/model`](https://code.claude.com/docs/en/model-config#setting-your-model) means the next request reads the entire conversation history with no cache hits, even though the content is identical.
7676 
7777When you run `/model` at the terminal, Claude Code asks you to confirm the switch only while the cache is still warm and the new model isn't the one that produced the last response. The cache stays warm for one [cache TTL](#cache-lifetime) after Claude Code last sent a request in this conversation or Claude last responded. Once that time passes, the cache has expired, so Claude Code switches without asking.
7878 
7979Before v2.1.238, Claude Code didn't check the cache TTL and asked even after the cache had expired.
8080 
81You can also require this confirmation or skip it with a [PreModelSwitch hook](/docs/en/hooks.md#premodelswitch-decision-control).
81You can also require this confirmation or skip it with a [PreModelSwitch hook](https://code.claude.com/docs/en/hooks.md#premodelswitch-decision-control).
8282 
83The [`opusplan` model setting](/docs/en/model-config#opusplan-model-setting) resolves to Opus during plan mode and Sonnet during execution, so each plan-mode toggle is a model switch and starts a fresh cache.
83The [`opusplan` model setting](https://code.claude.com/docs/en/model-config#opusplan-model-setting) resolves to Opus during plan mode and Sonnet during execution, so each plan-mode toggle is a model switch and starts a fresh cache.
8484 
85[Automatic model fallback](/docs/en/model-config.md#automatic-model-fallback) on Fable models, Opus 5.5, Sonnet 5.5, and Opus 5 is also a model switch. When a safety classifier flags a request in a category that has a fallback model and Claude Code re-runs the request on that model, the session continues there.
85[Automatic model fallback](https://code.claude.com/docs/en/model-config.md#automatic-model-fallback) on Fable models, Opus 5.5, Sonnet 5.5, and Opus 5 is also a model switch. When a safety classifier flags a request in a category that has a fallback model and Claude Code re-runs the request on that model, the session continues there.
8686 
87When a skill or command's frontmatter names a [`model`](/docs/en/skills#frontmatter-reference) other than the session's current model, that turn is also a model switch: the next request reads the entire conversation history with no cache hits. The session model resumes on your next prompt. A `context: fork` skill sets the [forked subagent's model](/docs/en/skills.md#run-skills-in-a-subagent) instead.
87When a skill or command's frontmatter names a [`model`](https://code.claude.com/docs/en/skills#frontmatter-reference) other than the session's current model, that turn is also a model switch: the next request reads the entire conversation history with no cache hits. The session model resumes on your next prompt. A `context: fork` skill sets the [forked subagent's model](https://code.claude.com/docs/en/skills.md#run-skills-in-a-subagent) instead.
8888 
8989### Changing effort level
9090 
91On most models, changing the [effort level](/docs/en/model-config.md#adjust-effort-level) mid-session means the next request reads the entire conversation history with no cache hits. While the cache is still warm, Claude Code asks you to confirm the change first.
91On most models, changing the [effort level](https://code.claude.com/docs/en/model-config.md#adjust-effort-level) mid-session means the next request reads the entire conversation history with no cache hits. While the cache is still warm, Claude Code asks you to confirm the change first.
9292 
93On Opus 5.5, Sonnet 5.5, Haiku 5.5, and Fable 5.1 with an API key or a Claude subscription, changing effort keeps the cache, and Claude Code applies the new level without asking. This doesn't apply on Amazon Bedrock, Google Cloud's Agent Platform, or a [Claude apps gateway](/docs/en/claude-apps-gateway), or when you set [`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS`](/docs/en/llm-gateway-protocol#disable-pre-release-capabilities) or your organization has a HIPAA configuration.
93On Opus 5.5, Sonnet 5.5, Haiku 5.5, and Fable 5.1 with an API key or a Claude subscription, changing effort keeps the cache, and Claude Code applies the new level without asking. This doesn't apply on Amazon Bedrock, Google Cloud's Agent Platform, or a [Claude apps gateway](https://code.claude.com/docs/en/claude-apps-gateway), or when you set [`CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS`](https://code.claude.com/docs/en/llm-gateway-protocol#disable-pre-release-capabilities) or your organization has a HIPAA configuration.
9494 
9595Before v2.1.260, changing effort on Fable 5.1 with an API key or a Claude subscription also invalidated the cache.
9696 
9797### Turning on fast mode
9898 
99Enabling [fast mode](/docs/en/fast-mode.md) adds a request header that is part of the cache key, so the first request Claude Code sends with fast mode on reads the entire conversation history with no cache hits. Claude Code sets that header once when a turn starts and keeps it for the whole turn, so when you turn fast mode on while Claude is working, the cache miss from the header happens on the first request of your next turn. Those uncached input tokens are billed at [fast mode rates](/docs/en/fast-mode.md#understand-the-cost-tradeoff), which is why turning it on at the start of a session costs less than turning it on deep into a long one. If your current model doesn't support fast mode, enabling fast mode also [switches your model](#switching-models), and that switch starts a fresh cache on its own from the next request in the running turn.
99Enabling [fast mode](https://code.claude.com/docs/en/fast-mode.md) adds a request header that is part of the cache key, so the first request Claude Code sends with fast mode on reads the entire conversation history with no cache hits. Claude Code sets that header once when a turn starts and keeps it for the whole turn, so when you turn fast mode on while Claude is working, the cache miss from the header happens on the first request of your next turn. Those uncached input tokens are billed at [fast mode rates](https://code.claude.com/docs/en/fast-mode.md#understand-the-cost-tradeoff), which is why turning it on at the start of a session costs less than turning it on deep into a long one. If your current model doesn't support fast mode, enabling fast mode also [switches your model](#switching-models), and that switch starts a fresh cache on its own from the next request in the running turn.
100100 
101The cost applies once per conversation. After the first fast mode turn, Claude Code keeps sending the header and varies only the request's speed setting, which is not part of the cache key. Turning fast mode off, the [automatic fallback to standard speed](/docs/en/fast-mode.md#handle-rate-limits) after a rate limit, and turning it back on later all keep the cache. If you [run out of usage credits](/docs/en/fast-mode.md#handle-rate-limits) mid-session, Claude Code retries each rejected fast mode request at standard speed the same way, so this fallback also keeps the cache. `/clear` and `/compact` reset this, since they rebuild the cache at those points anyway.
101The cost applies once per conversation. After the first fast mode turn, Claude Code keeps sending the header and varies only the request's speed setting, which is not part of the cache key. Turning fast mode off, the [automatic fallback to standard speed](https://code.claude.com/docs/en/fast-mode.md#handle-rate-limits) after a rate limit, and turning it back on later all keep the cache. If you [run out of usage credits](https://code.claude.com/docs/en/fast-mode.md#handle-rate-limits) mid-session, Claude Code retries each rejected fast mode request at standard speed the same way, so this fallback also keeps the cache. `/clear` and `/compact` reset this, since they rebuild the cache at those points anyway.
102102 
103103### Connecting or removing an MCP server
104104 
105Tool definitions sit in the system prompt layer, so the cache invalidates when the set of tool definitions in the request changes between turns. Toggling the [advisor tool](/docs/en/advisor.md) is an exception: its definition sits after the cache breakpoint, so enabling or disabling `/advisor` keeps the cached prefix intact. Whether an [MCP server](/docs/en/mcp.md) change does this depends on whether [tool search](/docs/en/mcp.md#scale-with-mcp-tool-search) defers the session's MCP tools, the default on supported models:
105Tool definitions sit in the system prompt layer, so the cache invalidates when the set of tool definitions in the request changes between turns. Toggling the [advisor tool](https://code.claude.com/docs/en/advisor.md) is an exception: its definition sits after the cache breakpoint, so enabling or disabling `/advisor` keeps the cached prefix intact. Whether an [MCP server](https://code.claude.com/docs/en/mcp.md) change does this depends on whether [tool search](https://code.claude.com/docs/en/mcp.md#scale-with-mcp-tool-search) defers the session's MCP tools, the default on supported models:
106106 
107107* **Tools deferred**: Claude Code keeps the tool list from the conversation's first request for the whole conversation, so a server connecting or disconnecting mid-session doesn't disturb anything already cached. A server that finishes connecting after the first request supplies its tools as deferred definitions that Claude loads on demand.
108* **Tools loaded upfront**: adding a definition invalidates the cache, and so does removing one on purpose. This applies when tool search is [off or unavailable](/docs/en/mcp.md#configure-tool-search), such as on:
108* **Tools loaded upfront**: adding a definition invalidates the cache, and so does removing one on purpose. This applies when tool search is [off or unavailable](https://code.claude.com/docs/en/mcp.md#configure-tool-search), such as on:
109109 * Google Cloud's Agent Platform models earlier than Opus 4.5, Sonnet 4.5, and Haiku 4.5
110110 * A Microsoft Foundry [deployment hosted on Azure](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#hosting-options) once Claude Code detects that the deployment rejects tool search
111111 
from line 113
113113 
114114| Mid-session change | Cache | Tool definitions in the next request |
115115| - | - | - |
116| A server connects, or a [dynamic tool update](/docs/en/mcp.md#dynamic-tool-updates) adds tools | Invalidated | The new definitions are added |
117| A server drops out with no action on your part, such as a stdio server's process exiting | Kept | The server's definitions stay unchanged. A call to one of its tools returns an error instead of running, unless Claude Code [starts the server again](/docs/en/mcp.md#stdio-servers-that-exit-or-fail-to-start) for that call |
118| A remote server [reconnects automatically](/docs/en/mcp.md#automatic-reconnection) after its connection drops | Kept, unless a request sent while the server reconnects adds the `WaitForMcpServers` tool, which invalidates the cache once | The server's definitions stay unchanged. A request sent while the server reconnects can add `WaitForMcpServers` when the conversation hasn't listed it yet, and the tool then stays listed for the rest of the conversation |
116| A server connects, or a [dynamic tool update](https://code.claude.com/docs/en/mcp.md#dynamic-tool-updates) adds tools | Invalidated | The new definitions are added |
117| A server drops out with no action on your part, such as a stdio server's process exiting | Kept | The server's definitions stay unchanged. A call to one of its tools returns an error instead of running, unless Claude Code [starts the server again](https://code.claude.com/docs/en/mcp.md#stdio-servers-that-exit-or-fail-to-start) for that call |
118| A remote server [reconnects automatically](https://code.claude.com/docs/en/mcp.md#automatic-reconnection) after its connection drops | Kept, unless a request sent while the server reconnects adds the `WaitForMcpServers` tool, which invalidates the cache once | The server's definitions stay unchanged. A request sent while the server reconnects can add `WaitForMcpServers` when the conversation hasn't listed it yet, and the tool then stays listed for the rest of the conversation |
119119| You remove a tool on purpose, such as with a [deny rule](#denying-an-entire-tool) or by disabling its server in `/mcp` | Invalidated | The definition is removed |
120120 
121121When you resume a conversation whose tools load into the prefix, one of its MCP servers can still be connecting as the first request goes out. If the transcript recorded that server's tool definitions, that request includes them as recorded, so it doesn't change when the server finishes connecting with the same tools.
from line 124
124124 
125125### Enabling or disabling a plugin
126126 
127When you enable or disable a [plugin](/docs/en/plugins/overview.md), what the change costs depends on which component types the plugin provides. The cases below cover each component type, when Claude Code applies the change, and what happens when you disable a plugin again in the same session.
127When you enable or disable a [plugin](https://code.claude.com/docs/en/plugins/overview.md), what the change costs depends on which component types the plugin provides. The cases below cover each component type, when Claude Code applies the change, and what happens when you disable a plugin again in the same session.
128128 
129129#### Plugin components that keep the cache
130130 
from line 132
132132 
133133#### Plugins that provide MCP servers
134134 
135When you enable or disable a plugin that provides [MCP servers](/docs/en/plugins/components.md#mcp-servers), Claude Code follows the same rules as when you [connect or remove an MCP server](#connecting-or-removing-an-mcp-server).
135When you enable or disable a plugin that provides [MCP servers](https://code.claude.com/docs/en/plugins/components.md#mcp-servers), Claude Code follows the same rules as when you [connect or remove an MCP server](#connecting-or-removing-an-mcp-server).
136136 
137137#### Code intelligence plugins
138138 
139When you enable a [code intelligence plugin](/docs/en/plugins/code-intelligence.md), Claude gets the [LSP tool](/docs/en/tools-reference.md#lsp-tool-behavior).
139When you enable a [code intelligence plugin](https://code.claude.com/docs/en/plugins/code-intelligence.md), Claude gets the [LSP tool](https://code.claude.com/docs/en/tools-reference.md#lsp-tool-behavior).
140140 
141141#### When plugin changes apply
142142 
143A change you make in the `/plugin` menu goes through [`/reload-plugins`](/docs/en/plugins/cli-reference#reload-plugins), which Claude Code runs for you when you close the menu. You pay the cost, whether appended announcements or a full re-read, on the first turn after the change applies. Claude Code can also apply a change on its own:
143A change you make in the `/plugin` menu goes through [`/reload-plugins`](https://code.claude.com/docs/en/plugins/cli-reference#reload-plugins), which Claude Code runs for you when you close the menu. You pay the cost, whether appended announcements or a full re-read, on the first turn after the change applies. Claude Code can also apply a change on its own:
144144 
145* For a plugin with a `command` source, Claude Code [can reload the plugin itself](/docs/en/plugins/loading.md#when-a-command-source-re-runs).
146* When you [install a plugin from the `/plugin` interface](/docs/en/plugins/install#install-a-plugin), Claude Code can activate it during the install. The install summary tells you whether it did.
147* When you [move the session with `/cd`](/docs/en/permissions#move-the-session-to-another-directory) on v2.1.246 or later, Claude Code applies the plugins the new directory's settings enable as part of the move, without the full re-read warning that holds a `/reload-plugins`.
148* In interactive sessions, when you add or remove a plugin in a [folder of plugins](/docs/en/plugins/create.md#load-a-directory-or-archive-for-one-session) you passed with `--plugin-dir`, the change applies right away. If applying it would trigger a full re-read, Claude Code holds the change instead and shows a notice to run `/reload-plugins`. Requires Claude Code v2.1.265 or later.
145* For a plugin with a `command` source, Claude Code [can reload the plugin itself](https://code.claude.com/docs/en/plugins/loading.md#when-a-command-source-re-runs).
146* When you [install a plugin from the `/plugin` interface](https://code.claude.com/docs/en/plugins/install#install-a-plugin), Claude Code can activate it during the install. The install summary tells you whether it did.
147* When you [move the session with `/cd`](https://code.claude.com/docs/en/permissions#move-the-session-to-another-directory) on v2.1.246 or later, Claude Code applies the plugins the new directory's settings enable as part of the move, without the full re-read warning that holds a `/reload-plugins`.
148* In interactive sessions, when you add or remove a plugin in a [folder of plugins](https://code.claude.com/docs/en/plugins/create.md#load-a-directory-or-archive-for-one-session) you passed with `--plugin-dir`, the change applies right away. If applying it would trigger a full re-read, Claude Code holds the change instead and shows a notice to run `/reload-plugins`. Requires Claude Code v2.1.265 or later.
149149 
150150When `/reload-plugins` runs and the reload would trigger a full re-read, Claude Code shows a warning and doesn't apply the reload. Run `/reload-plugins --force` to apply it anyway.
151151 
152`/reload-plugins` also runs in sessions without an interactive terminal, such as the desktop app, the Agent SDK, and [non-interactive mode](/docs/en/headless.md) with `-p`, when you type it into the session directly. Requires Claude Code v2.1.260 or later.
152`/reload-plugins` also runs in sessions without an interactive terminal, such as the desktop app, the Agent SDK, and [non-interactive mode](https://code.claude.com/docs/en/headless.md) with `-p`, when you type it into the session directly. Requires Claude Code v2.1.260 or later.
153153 
154In those sessions the reload applies everything except plugin MCP server changes, which [take effect in your next session](/docs/en/plugins/cli-reference.md#reload-plugins) and so never cost a full re-read mid-session.
154In those sessions the reload applies everything except plugin MCP server changes, which [take effect in your next session](https://code.claude.com/docs/en/plugins/cli-reference.md#reload-plugins) and so never cost a full re-read mid-session.
155155 
156156#### Plugins you enable and then disable in one session
157157 
from line 159
159159 
160160### Denying an entire tool
161161 
162If you add a bare tool name like `Bash` or `WebFetch` as a [deny rule](/docs/en/permissions.md#manage-permissions), Claude can't call that tool from your next request on, whether you add the rule through `/permissions` or by [editing a settings file directly](/docs/en/settings.md#when-edits-take-effect). That includes a rule you add through `/permissions` in the middle of a turn.
162If you add a bare tool name like `Bash` or `WebFetch` as a [deny rule](https://code.claude.com/docs/en/permissions.md#manage-permissions), Claude can't call that tool from your next request on, whether you add the rule through `/permissions` or by [editing a settings file directly](https://code.claude.com/docs/en/settings.md#when-edits-take-effect). That includes a rule you add through `/permissions` in the middle of a turn.
163163 
164When [tool search](/docs/en/mcp.md#scale-with-mcp-tool-search) is active, which is the default on supported models, the request's tool definitions don't change and the cached prefix survives. When tool search is unavailable or disabled, Claude Code removes the definition from the next request, which invalidates the cache, and so does removing the rule later.
164When [tool search](https://code.claude.com/docs/en/mcp.md#scale-with-mcp-tool-search) is active, which is the default on supported models, the request's tool definitions don't change and the cached prefix survives. When tool search is unavailable or disabled, Claude Code removes the definition from the next request, which invalidates the cache, and so does removing the rule later.
165165 
166Only a deny rule that matches in the tool-name position blocks a tool this way: a bare tool name, the equivalent `Bash(*)` form, or a [tool-name glob](/docs/en/permissions.md#tool-name-wildcards) like `"*"`. A glob that matches only MCP tools, such as `"mcp__*"`, blocks those tools the same way. Scoped deny rules like `Bash(rm *)`, and all allow and ask rules, don't change which tools Claude sees. Claude Code checks them when Claude attempts a call, leaving the prefix intact.
166Only a deny rule that matches in the tool-name position blocks a tool this way: a bare tool name, the equivalent `Bash(*)` form, or a [tool-name glob](https://code.claude.com/docs/en/permissions.md#tool-name-wildcards) like `"*"`. A glob that matches only MCP tools, such as `"mcp__*"`, blocks those tools the same way. Scoped deny rules like `Bash(rm *)`, and all allow and ask rules, don't change which tools Claude sees. Claude Code checks them when Claude attempts a call, leaving the prefix intact.
167167 
168168### Compacting the conversation
169169 
170[Compaction](/docs/en/context-window.md#what-survives-compaction) replaces your message history with a summary. By design, this invalidates the conversation layer: the next request has a new, shorter history that doesn't share a prefix with the old one. The system prompt and project context layers can stay cached:
170[Compaction](https://code.claude.com/docs/en/context-window.md#what-survives-compaction) replaces your message history with a summary. By design, this invalidates the conversation layer: the next request has a new, shorter history that doesn't share a prefix with the old one. The system prompt and project context layers can stay cached:
171171 
172172* **System prompt**: the next request reads this layer from the cache, unless a [resumed conversation](#resuming-a-session) kept a system prompt that is now out of date. The first time that conversation is compacted, Claude Code switches it to the current prompt, and the next request rebuilds this layer's cache once.
173173* **Project context**: Claude Code reloads this layer from disk. The next request reads it from the cache only if CLAUDE.md and memory are unchanged since the session started.
from line 174
174174 
175175To produce the summary, Claude Code sends a separate request. The request repeats your conversation's system prompt, tools, and the history being summarized. It ends with a summarization instruction. While the cache is warm, that request reads your prefix from the cache. A mid-session `/compact` then costs a fraction of what the context size suggests and spends most of its time generating the summary.
176176 
177After a break longer than the [cache lifetime](#cache-lifetime), there is no cache left to read, so the summarization request reprocesses that history as uncached input. This is why `/compact` costs the most when you [resume an old session](/docs/en/sessions.md#resume-a-session). In both the warm and cold cases, the turn after compaction rebuilds the conversation cache for only the much shorter summary, so that turn isn't the slow part.
177After a break longer than the [cache lifetime](#cache-lifetime), there is no cache left to read, so the summarization request reprocesses that history as uncached input. This is why `/compact` costs the most when you [resume an old session](https://code.claude.com/docs/en/sessions.md#resume-a-session). In both the warm and cold cases, the turn after compaction rebuilds the conversation cache for only the much shorter summary, so that turn isn't the slow part.
178178 
179179> [!TIP]
180180>
181181> Compaction works in your favor when the context you discard is content you no longer need. To choose when its overhead happens, run `/compact` at a natural break in your work, such as between tasks, instead of waiting for auto-compaction to trigger mid-task. If you've gone down a path you want to abandon entirely, [`/rewind`](#rewinding-the-conversation) to an earlier turn instead. Rewinding truncates back to a prefix that is already cached, rather than building a new one as compaction does.
182182 
183If you leave a long conversation idle in a terminal session while it is on the [one-hour TTL](#which-ttl-each-request-gets), Claude Code can compact it shortly before the cache expires. To turn idle compaction off, set [`idleCompaction`](/docs/en/settings-reference#idlecompaction) to `false`, which requires Claude Code v2.1.290 or later.
183If you leave a long conversation idle in a terminal session while it is on the [one-hour TTL](#which-ttl-each-request-gets), Claude Code can compact it shortly before the cache expires. To turn idle compaction off, set [`idleCompaction`](https://code.claude.com/docs/en/settings-reference#idlecompaction) to `false`, which requires Claude Code v2.1.290 or later.
184184 
185185### Accumulating many images
186186 
from line 192
192192 
193193### Upgrading Claude Code
194194 
195A new Claude Code version typically updates the system prompt or tool definitions, so the first conversation you start after an upgrade builds its cache from the top. [Auto-update](/docs/en/setup.md#auto-updates) downloads new versions in the background but applies them on the next launch, never mid-session, so you see this as an uncached first turn after restarting rather than a surprise during a session. Set `DISABLE_AUTOUPDATER=1` to control when upgrades apply.
195A new Claude Code version typically updates the system prompt or tool definitions, so the first conversation you start after an upgrade builds its cache from the top. [Auto-update](https://code.claude.com/docs/en/setup.md#auto-updates) downloads new versions in the background but applies them on the next launch, never mid-session, so you see this as an uncached first turn after restarting rather than a surprise during a session. Set `DISABLE_AUTOUPDATER=1` to control when upgrades apply.
196196 
197197> [!NOTE]
198198>
from line 213
213213 
214214### Editing files in your repository
215215 
216File contents enter context only when Claude reads them, and reads append to the conversation. Editing a file Claude previously read does not retroactively change the earlier read in history. Instead, Claude Code appends a [`<system-reminder>`](/docs/en/glossary#system-reminder) noting the file changed, and Claude re-reads it if needed.
216File contents enter context only when Claude reads them, and reads append to the conversation. Editing a file Claude previously read does not retroactively change the earlier read in history. Instead, Claude Code appends a [`<system-reminder>`](https://code.claude.com/docs/en/glossary#system-reminder) noting the file changed, and Claude re-reads it if needed.
217217 
218218### Editing CLAUDE.md mid-session
219219 
220220Your project-root and user-level CLAUDE.md files are read once at session start and held in memory. Editing them mid-session does not invalidate the cache, but the edit also doesn't apply. Claude keeps working with the version that was loaded at session start. The new content loads on the next `/clear`, `/compact`, or restart.
221221 
222[Nested CLAUDE.md files in subdirectories](/docs/en/memory.md) and [rules with `paths:` frontmatter](/docs/en/memory#path-specific-rules) load later, on demand. Editing one yourself before it loads does take effect. After it loads, the content is part of the conversation history, so a mid-session edit doesn't retroactively change it.
222[Nested CLAUDE.md files in subdirectories](https://code.claude.com/docs/en/memory.md) and [rules with `paths:` frontmatter](https://code.claude.com/docs/en/memory#path-specific-rules) load later, on demand. Editing one yourself before it loads does take effect. After it loads, the content is part of the conversation history, so a mid-session edit doesn't retroactively change it.
223223 
224224### Changing permission mode
225225 
226Switching between [permission modes](/docs/en/permission-modes.md), such as from Manual to accept edits, does not change the system prompt or tool definitions, so mode changes are cache-safe. The exception is plan mode with the [`opusplan`](/docs/en/model-config#opusplan-model-setting) model setting, which switches the model between Opus and Sonnet as you enter or leave plan mode. That makes the mode toggle a [model switch](#switching-models).
226Switching between [permission modes](https://code.claude.com/docs/en/permission-modes.md), such as from Manual to accept edits, does not change the system prompt or tool definitions, so mode changes are cache-safe. The exception is plan mode with the [`opusplan`](https://code.claude.com/docs/en/model-config#opusplan-model-setting) model setting, which switches the model between Opus and Sonnet as you enter or leave plan mode. That makes the mode toggle a [model switch](#switching-models).
227227 
228228### Changing output style
229229 
230When you switch [output styles](/docs/en/output-styles.md) mid-session with [`/output-style`](/docs/en/output-styles#change-your-output-style), `/config`, or the `outputStyle` setting, Claude uses the new style starting with your next message. Claude Code delivers the new style's instructions as a message in the conversation, so that request still reads the system prompt and the earlier conversation from the cache.
230When you switch [output styles](https://code.claude.com/docs/en/output-styles.md) mid-session with [`/output-style`](https://code.claude.com/docs/en/output-styles#change-your-output-style), `/config`, or the `outputStyle` setting, Claude uses the new style starting with your next message. Claude Code delivers the new style's instructions as a message in the conversation, so that request still reads the system prompt and the earlier conversation from the cache.
231231 
232232Before v2.1.251, a mid-session style switch kept the cache but didn't apply until you ran `/clear` or started a new session.
233233 
234234### Invoking skills and commands
235235 
236[Skills](/docs/en/skills.md) and [commands](/docs/en/commands.md) inject their instructions as user messages at the point of invocation. Nothing earlier in the conversation changes. A skill or command whose frontmatter names a `model` can be a [model switch](#switching-models) for that turn.
236[Skills](https://code.claude.com/docs/en/skills.md) and [commands](https://code.claude.com/docs/en/commands.md) inject their instructions as user messages at the point of invocation. Nothing earlier in the conversation changes. A skill or command whose frontmatter names a `model` can be a [model switch](#switching-models) for that turn.
237237 
238238### Running `/recap`
239239 
240[`/recap`](/docs/en/interactive-mode#session-recap) generates a summary for display in your terminal. Unlike `/compact`, it appends the summary as command output rather than replacing your message history, so the cached prefix stays intact.
240[`/recap`](https://code.claude.com/docs/en/interactive-mode#session-recap) generates a summary for display in your terminal. Unlike `/compact`, it appends the summary as command output rather than replacing your message history, so the cached prefix stays intact.
241241 
242242### Rewinding the conversation
243243 
244[`/rewind`](/docs/en/checkpointing) truncates your conversation back to an earlier turn. The remaining history is the same content the cache was built from at that point, and the system prompt and project context layers are unchanged, so the next request hits the earlier cache entry. Every turn since then has read through that prefix, which kept the entry warm even if the original turn was longer ago than the TTL.
244[`/rewind`](https://code.claude.com/docs/en/checkpointing) truncates your conversation back to an earlier turn. The remaining history is the same content the cache was built from at that point, and the system prompt and project context layers are unchanged, so the next request hits the earlier cache entry. Every turn since then has read through that prefix, which kept the entry warm even if the original turn was longer ago than the TTL.
245245 
246246Restoring file checkpoints alongside the conversation has no separate effect on the cache. File contents enter context only when Claude reads them, the same as [editing files in your repository](#editing-files-in-your-repository).
247247 
248248## Resuming a session
249249 
250When you [resume a session](/docs/en/sessions.md#resume-a-session), Claude Code sends the whole conversation again, and the request reads from the cache whatever part of its prefix is unchanged and still within the [cache lifetime](#cache-lifetime). The layer table at the top of this page says what changes each layer.
250When you [resume a session](https://code.claude.com/docs/en/sessions.md#resume-a-session), Claude Code sends the whole conversation again, and the request reads from the cache whatever part of its prefix is unchanged and still within the [cache lifetime](#cache-lifetime). The layer table at the top of this page says what changes each layer.
251251 
252The system prompt would change after a [Claude Code upgrade](#upgrading-claude-code) or with different [`--append-system-prompt`](/docs/en/cli-reference#system-prompt-flags) text on the resume. By default, the resumed conversation keeps the system prompt it started with, so its history still sits behind the same prompt, and the change takes effect once the conversation is compacted or in a new conversation. [System prompt flags in resumed conversations](/docs/en/cli-reference.md#system-prompt-flags-in-resumed-conversations) covers the cases where Claude Code rebuilds the prompt on every request instead.
252The system prompt would change after a [Claude Code upgrade](#upgrading-claude-code) or with different [`--append-system-prompt`](https://code.claude.com/docs/en/cli-reference#system-prompt-flags) text on the resume. By default, the resumed conversation keeps the system prompt it started with, so its history still sits behind the same prompt, and the change takes effect once the conversation is compacted or in a new conversation. [System prompt flags in resumed conversations](https://code.claude.com/docs/en/cli-reference.md#system-prompt-flags-in-resumed-conversations) covers the cases where Claude Code rebuilds the prompt on every request instead.
253253 
254254## Cache lifetime
255255 
from line 262
262262Claude Code decides the TTL per request, and every request falls in one of two fixed buckets:
263263 
264264* **Main conversation**: your interactive turns, non-interactive `-p` runs, and Agent SDK turns, plus the helpers Claude Code runs inline with them
265* **Everything else**: the requests Claude Code makes outside that conversation, such as [subagents](/docs/en/sub-agents.md), [workflows](/docs/en/workflows.md), in-process [teammates](/docs/en/agent-teams.md), forks, compaction, and session titles
265* **Everything else**: the requests Claude Code makes outside that conversation, such as [subagents](https://code.claude.com/docs/en/sub-agents.md), [workflows](https://code.claude.com/docs/en/workflows.md), in-process [teammates](https://code.claude.com/docs/en/agent-teams.md), forks, compaction, and session titles
266266 
267267Unless you choose a TTL yourself, Claude Code requests the one-hour TTL only on a Claude subscription within your plan's included usage. There it requests the hour for the main conversation, plus a small set of helper requests that Anthropic controls server-side. This table gives each bucket's default TTL under both kinds of billing.
268268 
from line 277
277277 
278278You can set a TTL for either bucket. Each control takes `5m` or `1h`, and Claude Code ignores any other value.
279279 
280* **Main conversation**: the [`promptCacheTtl`](/docs/en/settings-reference#promptcachettl) setting, or the `CLAUDE_CODE_PROMPT_CACHE_TTL` [environment variable](/docs/en/env-vars.md)
281* **Everything else**: the [`subagentPromptCacheTtl`](/docs/en/settings-reference#subagentpromptcachettl) setting, or the `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL` environment variable
280* **Main conversation**: the [`promptCacheTtl`](https://code.claude.com/docs/en/settings-reference#promptcachettl) setting, or the `CLAUDE_CODE_PROMPT_CACHE_TTL` [environment variable](https://code.claude.com/docs/en/env-vars.md)
281* **Everything else**: the [`subagentPromptCacheTtl`](https://code.claude.com/docs/en/settings-reference#subagentpromptcachettl) setting, or the `CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL` environment variable
282282 
283283Both settings and both environment variables require Claude Code v2.1.242 or later. If you sign in with an API key or use a cloud provider, set `promptCacheTtl` to `1h` to give the main conversation a one-hour cache. Requests outside it keep the five-minute default until you choose a TTL for that bucket too.
284284 
from line 287
2872871. `FORCE_PROMPT_CACHING_5M=1`, which forces five minutes for both buckets
2882882. The bucket's environment variable
2892893. The bucket's setting
2904. For a subagent's requests, the `cacheTtl` value in the subagent's [`experimental` frontmatter field](/docs/en/sub-agents#supported-frontmatter-fields), which requires Claude Code v2.1.248 or later. Claude Code ignores a `1h` there while your Claude subscription is using usage credits
2904. For a subagent's requests, the `cacheTtl` value in the subagent's [`experimental` frontmatter field](https://code.claude.com/docs/en/sub-agents#supported-frontmatter-fields), which requires Claude Code v2.1.248 or later. Claude Code ignores a `1h` there while your Claude subscription is using usage credits
2912915. `ENABLE_PROMPT_CACHING_1H=1`, which requests one hour for both buckets
2922926. The [default for the request's bucket](#which-ttl-each-request-gets)
293293 
294Set `FORCE_PROMPT_CACHING_5M=1` when you're debugging cache behavior, comparing the two TTLs, or overriding a longer TTL set in [managed settings](/docs/en/managed-settings.md).
294Set `FORCE_PROMPT_CACHING_5M=1` when you're debugging cache behavior, comparing the two TTLs, or overriding a longer TTL set in [managed settings](https://code.claude.com/docs/en/managed-settings.md).
295295 
296296To confirm which TTL your main conversation's cache writes used, run `claude -p "hello" --output-format json` and read `usage.cache_creation` in the result. Claude Code reports one-hour cache writes under `ephemeral_1h_input_tokens` and five-minute cache writes under `ephemeral_5m_input_tokens`.
297297 
298298To get one hour through a gateway or on Amazon Bedrock:
299299 
300* **LLM gateway you set with `ANTHROPIC_BASE_URL`**: part of the one-hour request travels in the `anthropic-beta` header, so configure the gateway to [forward that header unchanged](/docs/en/llm-gateway-protocol.md#request-headers)
301* **[Claude apps gateway](/docs/en/claude-apps-gateway)**: ask your administrator to request the one-hour TTL in a [gateway policy](/docs/en/claude-apps-gateway-config.md#one-hour-prompt-cache)
300* **LLM gateway you set with `ANTHROPIC_BASE_URL`**: part of the one-hour request travels in the `anthropic-beta` header, so configure the gateway to [forward that header unchanged](https://code.claude.com/docs/en/llm-gateway-protocol.md#request-headers)
301* **[Claude apps gateway](https://code.claude.com/docs/en/claude-apps-gateway)**: ask your administrator to request the one-hour TTL in a [gateway policy](https://code.claude.com/docs/en/claude-apps-gateway-config.md#one-hour-prompt-cache)
302302* **Amazon Bedrock**: prompt caching support, minimum cacheable prefix length, and one-hour TTL availability all vary by model. If cache token counts stay at zero, check [supported models, regions, and limits](https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html#prompt-caching-models) in the Amazon Bedrock documentation.
303303 
304304## Cache scope
from line 307
307307 
308308Sessions you run in parallel in the same directory build matching prefixes and read each other's cache. Sequential sessions share the prefix only when the git status snapshot taken at startup matches, since each conversation also carries the branch and recent commits from that snapshot.
309309 
310The underlying API cache is broader. Caches are isolated between organizations, and on some providers, [between workspaces within an organization](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#cache-storage-and-sharing). Within those boundaries, any two requests with the same model and prefix read the same cache. For Agent SDK callers running fleets of automated processes, see [improve prompt caching across users and machines](/docs/en/agent-sdk/modifying-system-prompts.md#improve-prompt-caching-across-users-and-machines) to move the auto memory location out of the system prompt and share the system prompt's cache entry across users and machines.
310The underlying API cache is broader. Caches are isolated between organizations, and on some providers, [between workspaces within an organization](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#cache-storage-and-sharing). Within those boundaries, any two requests with the same model and prefix read the same cache. For Agent SDK callers running fleets of automated processes, see [improve prompt caching across users and machines](https://code.claude.com/docs/en/agent-sdk/modifying-system-prompts.md#improve-prompt-caching-across-users-and-machines) to move the auto memory location out of the system prompt and share the system prompt's cache entry across users and machines.
311311 
312312## Check cache performance
313313 
314Cache performance shows up as two token counts the API reports on every response. The most direct way to watch them live is a [statusline script](/docs/en/statusline.md) that reads the `current_usage` object:
314Cache performance shows up as two token counts the API reports on every response. The most direct way to watch them live is a [statusline script](https://code.claude.com/docs/en/statusline.md) that reads the `current_usage` object:
315315 
316316| Field | Meaning |
317317| - | - |
from line 320
320320 
321321A high read-to-creation ratio means caching is working well. If creation stays high turn after turn, something is changing in your prefix. The [actions that invalidate the cache](#actions-that-invalidate-the-cache) section lists the usual causes.
322322 
323For a per-session summary, run `/usage`. After the main conversation's first response, Claude Code adds a [`Prompt cache (main)` line](/docs/en/costs#prompt-cache-statistics) to the Session block, showing the session's hit ratio, miss count, and whether the cache is warm right now. A status line script can read the same numbers from the [`prompt_cache` object](/docs/en/statusline#prompt-cache-fields). Both require Claude Code v2.1.251 or later.
323For a per-session summary, run `/usage`. After the main conversation's first response, Claude Code adds a [`Prompt cache (main)` line](https://code.claude.com/docs/en/costs#prompt-cache-statistics) to the Session block, showing the session's hit ratio, miss count, and whether the cache is warm right now. A status line script can read the same numbers from the [`prompt_cache` object](https://code.claude.com/docs/en/statusline#prompt-cache-fields). Both require Claude Code v2.1.251 or later.
324324 
325325The `Prompt cache (main)` line also names the likely cause of the last miss when Claude Code can identify one, for example `likely cause: tool definitions changed`. The likely-cause text requires Claude Code v2.1.260 or later.
326326 
327For visibility across an organization, the OpenTelemetry exporter reports cache read and creation tokens per user and session. See [Monitor usage](/docs/en/monitoring-usage.md) for the metric and event attribute reference.
327For visibility across an organization, the OpenTelemetry exporter reports cache read and creation tokens per user and session. See [Monitor usage](https://code.claude.com/docs/en/monitoring-usage.md) for the metric and event attribute reference.
328328 
329329## Subagents and the cache
330330 
331A [subagent](/docs/en/sub-agents.md) starts its own conversation with its own system prompt and tool set, separate from the parent's. Its first request doesn't read the parent's cache, because the two prefixes differ, and it warms a cache of its own across its turns. Subagents fall outside the main-conversation [TTL bucket](#which-ttl-each-request-gets), so they get five minutes even on a subscription until you [choose a longer one](#choose-the-ttl-yourself).
331A [subagent](https://code.claude.com/docs/en/sub-agents.md) starts its own conversation with its own system prompt and tool set, separate from the parent's. Its first request doesn't read the parent's cache, because the two prefixes differ, and it warms a cache of its own across its turns. Subagents fall outside the main-conversation [TTL bucket](#which-ttl-each-request-gets), so they get five minutes even on a subscription until you [choose a longer one](#choose-the-ttl-yourself).
332332 
333333The parent's cache is unaffected. From the parent's side, the subagent's call and result append to the conversation, leaving the parent's prefix intact.
334334 
335A [fork](/docs/en/sub-agents.md#fork-the-current-conversation), by contrast, inherits the parent's system prompt, tools, and conversation history exactly, so its first request reads the parent's cache.
335A [fork](https://code.claude.com/docs/en/sub-agents.md#fork-the-current-conversation), by contrast, inherits the parent's system prompt, tools, and conversation history exactly, so its first request reads the parent's cache.
336336 
337337Other requests can also read a prefix that an earlier request cached:
338338 
339* **Session copies**: a session you [copy with `/fork`](/docs/en/agent-view#copy-the-session-with-%2Ffork) receives its isolation instruction as a message at the end of the copied conversation, so the cache that the original conversation built stays intact.
339* **Session copies**: a session you [copy with `/fork`](https://code.claude.com/docs/en/agent-view#copy-the-session-with-%2Ffork) receives its isolation instruction as a message at the end of the copied conversation, so the cache that the original conversation built stays intact.
340340* **Compaction**: the summarization call described in [Compacting the conversation](#compacting-the-conversation) uses the same prefix-sharing approach.
341* **Resumed subagents**: when Claude [resumes a subagent](/docs/en/sub-agents.md#resume-subagents), the resumed run's first request can read the cache the original run warmed.
342* **Workflow fan-outs**: in a [workflow fan-out](/docs/en/workflows.md#prompt-caching-in-a-fan-out) of same-prefix agents, Claude Code holds all but the first for up to 5 seconds by default, so their first requests can read the prefix that the first agent cached.
341* **Resumed subagents**: when Claude [resumes a subagent](https://code.claude.com/docs/en/sub-agents.md#resume-subagents), the resumed run's first request can read the cache the original run warmed.
342* **Workflow fan-outs**: in a [workflow fan-out](https://code.claude.com/docs/en/workflows.md#prompt-caching-in-a-fan-out) of same-prefix agents, Claude Code holds all but the first for up to 5 seconds by default, so their first requests can read the prefix that the first agent cached.
343343 
344344## Disable prompt caching
345345 
from line 361
361361 
362362`DISABLE_PROMPT_CACHING_SONNET` and `DISABLE_PROMPT_CACHING_OPUS` each apply to the model the `sonnet` or `opus` alias resolves to. If you set any other Sonnet or Opus model ID as your main model, that model keeps caching. For example, a session on `claude-sonnet-5` keeps caching while `sonnet` resolves to `claude-sonnet-5-5`. To disable caching for that model, set `DISABLE_PROMPT_CACHING`.
363363 
364To set caching policy across an organization, put any of these or the [TTL variables](#cache-lifetime) in the `env` block of [managed settings](/docs/en/managed-settings.md). For normal use, leave caching enabled.
364To set caching policy across an organization, put any of these or the [TTL variables](#cache-lifetime) in the `env` block of [managed settings](https://code.claude.com/docs/en/managed-settings.md). For normal use, leave caching enabled.
365365 
366366## Related resources
367367 
368368* [Lessons from building Claude Code: Prompt caching is everything](https://claude.com/blog/lessons-from-building-claude-code-prompt-caching-is-everything): the design rationale for plan mode, deferred tool loading, and compaction
369* [Explore the context window](/docs/en/context-window.md): what loads into context and when
370* [Reduce token usage](/docs/en/costs.md#reduce-token-usage): strategies beyond caching for managing context size
371* [Track and reduce costs](/docs/en/agent-sdk/cost-tracking.md): cache token tracking and TTL configuration for Agent SDK callers
369* [Explore the context window](https://code.claude.com/docs/en/context-window.md): what loads into context and when
370* [Reduce token usage](https://code.claude.com/docs/en/costs.md#reduce-token-usage): strategies beyond caching for managing context size
371* [Track and reduce costs](https://code.claude.com/docs/en/agent-sdk/cost-tracking.md): cache token tracking and TTL configuration for Agent SDK callers
372372* [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching): the underlying API mechanism, breakpoints, and pricing
373373 

quickstart Changed · +21 / -21 lines

from line 10
1010 
1111* A terminal or command prompt open
1212* A code project to work with
13* A [Claude subscription](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=quickstart_prereq) (Pro, Max, Team, or Enterprise), [Claude Console](https://platform.claude.com/) account, or access through a [supported cloud provider](/docs/en/third-party-integrations.md)
13* A [Claude subscription](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=quickstart_prereq) (Pro, Max, Team, or Enterprise), [Claude Console](https://platform.claude.com/) account, or access through a [supported cloud provider](https://code.claude.com/docs/en/third-party-integrations.md)
1414 
1515> [!NOTE]
1616>
1717> These cases are covered on other pages:
1818>
19> * **Never used a terminal before**: start with the [terminal guide](/docs/en/terminal-guide)
20> * **Want Claude Code somewhere other than the terminal**: Claude Code is also available on the [web](https://claude.ai/code), as a [desktop app](/docs/en/desktop.md), in [VS Code](/docs/en/vs-code.md) and [JetBrains IDEs](/docs/en/jetbrains.md), in [Slack](/docs/en/slack.md), and in CI/CD with [GitHub Actions](/docs/en/github-actions.md) and [GitLab](/docs/en/gitlab-ci-cd.md). See [all interfaces](/docs/en/overview.md#use-claude-code-everywhere).
19> * **Never used a terminal before**: start with the [terminal guide](https://code.claude.com/docs/en/terminal-guide)
20> * **Want Claude Code somewhere other than the terminal**: Claude Code is also available on the [web](https://claude.ai/code), as a [desktop app](https://code.claude.com/docs/en/desktop.md), in [VS Code](https://code.claude.com/docs/en/vs-code.md) and [JetBrains IDEs](https://code.claude.com/docs/en/jetbrains.md), in [Slack](https://code.claude.com/docs/en/slack.md), and in CI/CD with [GitHub Actions](https://code.claude.com/docs/en/github-actions.md) and [GitLab](https://code.claude.com/docs/en/gitlab-ci-cd.md). See [all interfaces](https://code.claude.com/docs/en/overview.md#use-claude-code-everywhere).
2121 
2222## Step 1: Install Claude Code
2323 
24To install Claude Code, open a terminal and run the command for your system. If you haven't used a terminal before, the [terminal guide](/docs/en/terminal-guide) shows how to open one and paste the command.
24To install Claude Code, open a terminal and run the command for your system. If you haven't used a terminal before, the [terminal guide](https://code.claude.com/docs/en/terminal-guide) shows how to open one and paste the command.
2525 
2626**Native Install (Recommended)**
2727 
from line 45
4545curl -fsSL https://claude.ai/install.cmd -o install.cmd && install.cmd && del install.cmd
4646```
4747 
48The install command shows no progress while it downloads Claude Code. When the installer finishes, open a new terminal window and run `claude --version`. A working installation prints a version number. If your shell says `claude` isn't found or isn't recognized, the install directory isn't on your PATH yet: see [Fix your PATH](/docs/en/troubleshoot-install.md#command-not-found-claude-after-installation).
48The install command shows no progress while it downloads Claude Code. When the installer finishes, open a new terminal window and run `claude --version`. A working installation prints a version number. If your shell says `claude` isn't found or isn't recognized, the install directory isn't on your PATH yet: see [Fix your PATH](https://code.claude.com/docs/en/troubleshoot-install.md#command-not-found-claude-after-installation).
4949 
5050If you see `The token '&&' is not a valid statement separator`, you're in PowerShell, not CMD. If you see `'irm' is not recognized as an internal or external command`, you're in CMD, not PowerShell.
5151 
52If the install command fails with `syntax error near unexpected token '<'`, a `403`, or any other error, see [Troubleshoot installation](/docs/en/troubleshoot-install.md#find-your-error) to match the error to a fix and for alternative install methods.
52If the install command fails with `syntax error near unexpected token '<'`, a `403`, or any other error, see [Troubleshoot installation](https://code.claude.com/docs/en/troubleshoot-install.md#find-your-error) to match the error to a fix and for alternative install methods.
5353 
5454[Git for Windows](https://git-scm.com/downloads/win) is recommended on native Windows so Claude Code can use the Bash tool. If Git for Windows is not installed, Claude Code uses PowerShell as the shell tool instead. WSL setups do not need Git for Windows.
5555 
from line 79
7979>
8080> WinGet installations do not auto-update. Run `winget upgrade Anthropic.ClaudeCode` periodically to get the latest features and security fixes.
8181 
82You can also install with [apt, dnf, or apk](/docs/en/setup.md#install-with-linux-package-managers) on Debian, Fedora, RHEL, and Alpine.
82You can also install with [apt, dnf, or apk](https://code.claude.com/docs/en/setup.md#install-with-linux-package-managers) on Debian, Fedora, RHEL, and Alpine.
8383 
8484To confirm the installation worked, run:
8585 
from line 106
106106 
107107* [Claude Pro, Max, Team, or Enterprise](https://claude.com/pricing?utm_source=claude_code\&utm_medium=docs\&utm_content=quickstart_login) (recommended)
108108* [Claude Console](https://platform.claude.com/) (API access with pre-paid credits). On first login, a "Claude Code" workspace is automatically created in the Console for centralized cost tracking.
109* [Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry](/docs/en/third-party-integrations.md) (enterprise cloud providers)
110* A self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway), if your organization runs one: your admin pre-configures the gateway URL, and `/login` opens directly on the **Cloud gateway** screen for you to sign in with corporate SSO
109* [Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry](https://code.claude.com/docs/en/third-party-integrations.md) (enterprise cloud providers)
110* A self-hosted [Claude apps gateway](https://code.claude.com/docs/en/claude-apps-gateway), if your organization runs one: your admin pre-configures the gateway URL, and `/login` opens directly on the **Cloud gateway** screen for you to sign in with corporate SSO
111111 
112Once logged in, your credentials are stored and you won't need to log in again. Learn more in [Credential Management](/docs/en/authentication.md#credential-management).
112Once logged in, your credentials are stored and you won't need to log in again. Learn more in [Credential Management](https://code.claude.com/docs/en/authentication.md#credential-management).
113113 
114114The Claude Code prompt appears with the version, current model, and working directory shown above it. Type `/help` for available commands or `/resume` to continue a previous conversation. To switch accounts later or re-authenticate, type `/login` inside the running session.
115115 
from line 163
163163 
164164Claude Code finds the appropriate file and shows you the change. If it asks before making the change, select **Yes** to approve.
165165 
166The session's [permission mode](/docs/en/permission-modes.md) sets which actions Claude can take without asking you first. Press `Shift+Tab` at any time to switch the permission mode of the session you're in.
166The session's [permission mode](https://code.claude.com/docs/en/permission-modes.md) sets which actions Claude can take without asking you first. Press `Shift+Tab` at any time to switch the permission mode of the session you're in.
167167 
168168## Step 5: Use Git with Claude Code
169169 
from line 245
245245| `claude -c` | Continue most recent conversation in current directory | `claude -c` |
246246| `claude -r` | Resume a previous conversation | `claude -r` |
247247 
248See the [CLI reference](/docs/en/cli-reference.md) for the complete list of shell commands.
248See the [CLI reference](https://code.claude.com/docs/en/cli-reference.md) for the complete list of shell commands.
249249 
250250### Session commands
251251 
from line 257
257257| `/help` | Show available commands | `/help` |
258258| `/exit` or Ctrl+D twice | Exit Claude Code | `/exit` |
259259 
260See the [commands reference](/docs/en/commands.md) for the complete list of session commands.
260See the [commands reference](https://code.claude.com/docs/en/commands.md) for the complete list of session commands.
261261 
262262## Pro tips for beginners
263263 
264For more, see [best practices](/docs/en/best-practices.md) and [common workflows](/docs/en/common-workflows.md).
264For more, see [best practices](https://code.claude.com/docs/en/best-practices.md) and [common workflows](https://code.claude.com/docs/en/common-workflows.md).
265265 
266266> **Be specific with your requests**
267267>
from line 302
302302 
303303Now that you've learned the basics, explore more advanced features:
304304 
305* [How Claude Code works](/docs/en/how-claude-code-works.md): understand the agentic loop, built-in tools, and how Claude Code interacts with your project
306* [Best practices](/docs/en/best-practices.md): get better results with effective prompting and project setup
307* [Common workflows](/docs/en/common-workflows.md): step-by-step guides for common tasks
308* [Extend Claude Code](/docs/en/features-overview.md): customize with CLAUDE.md, skills, hooks, MCP, and more
305* [How Claude Code works](https://code.claude.com/docs/en/how-claude-code-works.md): understand the agentic loop, built-in tools, and how Claude Code interacts with your project
306* [Best practices](https://code.claude.com/docs/en/best-practices.md): get better results with effective prompting and project setup
307* [Common workflows](https://code.claude.com/docs/en/common-workflows.md): step-by-step guides for common tasks
308* [Extend Claude Code](https://code.claude.com/docs/en/features-overview.md): customize with CLAUDE.md, skills, hooks, MCP, and more
309309 
310See [advanced setup](/docs/en/setup.md) for installation options, manual updates, or uninstallation instructions.
310See [advanced setup](https://code.claude.com/docs/en/setup.md) for installation options, manual updates, or uninstallation instructions.
311311 
312312## Getting help
313313 

remote-control Changed · +71 / -71 lines

from line 6
66 
77When you start a Remote Control session on your machine, Claude keeps running locally the entire time, so your code execution and filesystem access stay on your machine. With Remote Control you can:
88 
9* **Use your full local environment remotely**: your filesystem, [MCP servers](/docs/en/mcp.md), tools, and project configuration all stay available, and typing `@` autocompletes file paths from your local project.
10* **Work from both surfaces at once**: the conversation and the progress of [subagents](/docs/en/sub-agents.md) and [dynamic workflows](/docs/en/workflows.md) stay in sync across all connected devices, so you can send messages from your terminal, browser, and phone interchangeably.
9* **Use your full local environment remotely**: your filesystem, [MCP servers](https://code.claude.com/docs/en/mcp.md), tools, and project configuration all stay available, and typing `@` autocompletes file paths from your local project.
10* **Work from both surfaces at once**: the conversation and the progress of [subagents](https://code.claude.com/docs/en/sub-agents.md) and [dynamic workflows](https://code.claude.com/docs/en/workflows.md) stay in sync across all connected devices, so you can send messages from your terminal, browser, and phone interchangeably.
1111* **Send images and files from your phone or browser**: attach a photo or file in the Claude app or at claude.ai/code, with or without a caption. Claude sees attached photos directly as part of your message. Claude Code downloads other files to your machine and passes them to Claude as `@` file references.
1212* **Survive interruptions**: if your laptop sleeps or your network drops, Claude Code reconnects automatically when your machine comes back online.
1313 
14Unlike [cloud sessions](/docs/en/claude-code-on-the-web.md), which run on cloud infrastructure, Remote Control sessions run directly on your machine and interact with your local filesystem. The web and mobile interfaces are a window into that local session, so your computer has to stay on and the `claude` process has to keep running.
14Unlike [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md), which run on cloud infrastructure, Remote Control sessions run directly on your machine and interact with your local filesystem. The web and mobile interfaces are a window into that local session, so your computer has to stay on and the `claude` process has to keep running.
1515 
1616> [!NOTE]
1717>
from line 26
2626* **Authentication**: run `claude` and use `/login` to sign in through claude.ai if you haven't already. Without an eligible login, `claude remote-control` exits with an error, while `claude --remote-control` still starts an interactive session and shows a Remote Control failure notification shortly after launch.
2727* **API endpoint**: not available in any of these configurations:
2828 * You use Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry.
29 * You point [`ANTHROPIC_BASE_URL`](/docs/en/env-vars) at a host other than `api.anthropic.com`, such as an [LLM gateway](/docs/en/llm-gateway) or proxy. Unset the variable to use Remote Control.
30 * You sign in through an enterprise [Claude apps gateway](/docs/en/claude-apps-gateway).
31* **Feature-flag evaluation**: if you set an [environment variable that turns off feature-flag evaluation](/docs/en/env-vars.md#features-that-need-feature-flag-fetching), whether Remote Control is available depends on which one:
32 * If you set `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` or `DISABLE_GROWTHBOOK`, Remote Control is unavailable. Unset the variable wherever it's set, in your shell environment or in the `env` block of a [`settings.json` file](/docs/en/settings-reference#all-settings), to use Remote Control.
29 * You point [`ANTHROPIC_BASE_URL`](https://code.claude.com/docs/en/env-vars) at a host other than `api.anthropic.com`, such as an [LLM gateway](https://code.claude.com/docs/en/llm-gateway) or proxy. Unset the variable to use Remote Control.
30 * You sign in through an enterprise [Claude apps gateway](https://code.claude.com/docs/en/claude-apps-gateway).
31* **Feature-flag evaluation**: if you set an [environment variable that turns off feature-flag evaluation](https://code.claude.com/docs/en/env-vars.md#features-that-need-feature-flag-fetching), whether Remote Control is available depends on which one:
32 * If you set `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` or `DISABLE_GROWTHBOOK`, Remote Control is unavailable. Unset the variable wherever it's set, in your shell environment or in the `env` block of a [`settings.json` file](https://code.claude.com/docs/en/settings-reference#all-settings), to use Remote Control.
3333 * If you set only `DISABLE_TELEMETRY` or `DO_NOT_TRACK`, Remote Control stays available unless your organization requires [Trusted Devices](#trusted-devices). If it does, unset the variable to use Remote Control. Using Remote Control with either variable set requires Claude Code v2.1.283 or later, or v2.1.304 or later in the Claude Desktop app.
34* **Workspace trust**: in a directory you haven't trusted yet, `claude remote-control` prints what trusting it turns on and asks `Trust <directory>? [y/N]` before it starts. Answering `y` saves the choice, except in your home directory, where trust is never saved and the question returns on every run. When its standard input or output isn't a terminal, the command can't ask and exits with a [`Workspace not trusted`](/docs/en/errors#workspace-not-trusted-when-starting-remote-control) error.
34* **Workspace trust**: in a directory you haven't trusted yet, `claude remote-control` prints what trusting it turns on and asks `Trust <directory>? [y/N]` before it starts. Answering `y` saves the choice, except in your home directory, where trust is never saved and the question returns on every run. When its standard input or output isn't a terminal, the command can't ask and exits with a [`Workspace not trusted`](https://code.claude.com/docs/en/errors#workspace-not-trusted-when-starting-remote-control) error.
3535 
3636## Start a Remote Control session
3737 
38You can start a Remote Control session from the CLI, the [Claude Desktop app](/docs/en/desktop.md), or the VS Code extension. The CLI offers three invocation modes; the Desktop app and VS Code use the `/remote-control` command.
38You can start a Remote Control session from the CLI, the [Claude Desktop app](https://code.claude.com/docs/en/desktop.md), or the VS Code extension. The CLI offers three invocation modes; the Desktop app and VS Code use the `/remote-control` command.
3939 
4040**Server mode**
4141 
from line 57
5757| `--remote-control-session-name-prefix <prefix>` | Prefix for auto-generated session names when no explicit name is set. Defaults to your machine's hostname, producing names like `myhost-graceful-unicorn`. Set `CLAUDE_REMOTE_CONTROL_SESSION_NAME_PREFIX` for the same effect. |
5858| `-c`, `--continue` | Bring back the session that the last server in this directory started with, instead of creating a new one. See [Resume sessions after stopping the server](#resume-sessions-after-stopping-the-server). Can't be combined with `--session-id`, `--spawn`, `--capacity`, or `--create-session-in-dir`. Requires Claude Code v2.1.200 or later. |
5959| `--session-id <id>` | Bring back one session by its ID. See [Resume sessions after stopping the server](#resume-sessions-after-stopping-the-server). Can't be combined with `--continue`, `--spawn`, `--capacity`, or `--create-session-in-dir`. Requires Claude Code v2.1.200 or later. |
60| `--spawn <mode>` | How the server creates sessions. • `same-dir` (default when no mode is saved): all sessions share the current working directory, so they can conflict if editing the same files. • `worktree`: each on-demand session gets its own [git worktree](/docs/en/worktrees.md). Requires a git repository or a [`WorktreeCreate` hook](/docs/en/hooks#worktreecreate). • `session`: single-session mode. Serves exactly one session and rejects additional connections. Set at startup only. Press `w` at runtime to toggle between `same-dir` and `worktree`. The mode you switch to becomes the [default for this project](#set-the-default-spawn-mode-for-a-project). |
60| `--spawn <mode>` | How the server creates sessions. • `same-dir` (default when no mode is saved): all sessions share the current working directory, so they can conflict if editing the same files. • `worktree`: each on-demand session gets its own [git worktree](https://code.claude.com/docs/en/worktrees.md). Requires a git repository or a [`WorktreeCreate` hook](https://code.claude.com/docs/en/hooks#worktreecreate). • `session`: single-session mode. Serves exactly one session and rejects additional connections. Set at startup only. Press `w` at runtime to toggle between `same-dir` and `worktree`. The mode you switch to becomes the [default for this project](#set-the-default-spawn-mode-for-a-project). |
6161| `--capacity <N>` | Maximum number of concurrent sessions. Default is 32. Cannot be used with `--spawn=session`. |
6262| `--[no-]create-session-in-dir` | Pre-create one session in the current directory when the server starts, so you have somewhere to type immediately. In `worktree` mode this session stays in the current directory while on-demand sessions get isolated worktrees. On by default. If you pass `--no-create-session-in-dir` to start with none, Claude Code archives the server's sessions when you stop it, so there's nothing to [resume](#resume-sessions-after-stopping-the-server). |
63| `--permission-mode <mode>` | Set the starting [permission mode](/docs/en/permission-modes.md) for the server's sessions, such as `acceptEdits`. Accepts `manual` as an alias for `default`; an unrecognized mode stops the server at startup and lists the valid modes. |
64| `--chrome` / `--no-chrome` | Turn [Chrome integration](/docs/en/chrome.md) on or off in the sessions the server creates, so Claude can use Chrome on your machine while you work from another device. Without either flag, the session the server pre-creates and any session you start yourself from claude.ai/code or the Claude app begin with Chrome off, even if you [enabled Chrome by default](/docs/en/chrome.md#enable-chrome-by-default). A session the server starts for one of your [project](/docs/en/claude-projects.md) threads follows that setting instead, except in `bypassPermissions` mode. Requires Claude Code v2.1.273 or later. |
63| `--permission-mode <mode>` | Set the starting [permission mode](https://code.claude.com/docs/en/permission-modes.md) for the server's sessions, such as `acceptEdits`. Accepts `manual` as an alias for `default`; an unrecognized mode stops the server at startup and lists the valid modes. |
64| `--chrome` / `--no-chrome` | Turn [Chrome integration](https://code.claude.com/docs/en/chrome.md) on or off in the sessions the server creates, so Claude can use Chrome on your machine while you work from another device. Without either flag, the session the server pre-creates and any session you start yourself from claude.ai/code or the Claude app begin with Chrome off, even if you [enabled Chrome by default](https://code.claude.com/docs/en/chrome.md#enable-chrome-by-default). A session the server starts for one of your [project](https://code.claude.com/docs/en/claude-projects.md) threads follows that setting instead, except in `bypassPermissions` mode. Requires Claude Code v2.1.273 or later. |
6565| `-d`, `--debug[=<filter>]` | Turn on debug logging for the server, optionally filtered by category. Pass a filter only in the `=` form, such as `--debug=api,hooks`. Requires Claude Code v2.1.282 or later. |
6666| `--debug-file <path>` | Write debug logs to the given file. |
6767| `--verbose` | Show detailed connection and session logs. |
68| `--sandbox`, `--no-sandbox` | Removed in v2.1.300. If you pass either flag, the server [exits with an error](/docs/en/errors.md#sandbox-is-not-supported-by-claude-remote-control) instead of starting. |
68| `--sandbox`, `--no-sandbox` | Removed in v2.1.300. If you pass either flag, the server [exits with an error](https://code.claude.com/docs/en/errors.md#sandbox-is-not-supported-by-claude-remote-control) instead of starting. |
6969 
70If you pass a global `claude` flag before `remote-control`, or a wrapper script adds one, Claude Code doesn't carry the flag over to the sessions the server creates. Claude Code lets the flag through only when dropping it is known not to change what those sessions can do, such as `--verbose` or `--model`. For any other flag, such as `--settings`, Claude Code [refuses to start](/docs/en/errors.md#not-carried-over-to-the-sessions-remote-control-starts) and names the flag to remove.
70If you pass a global `claude` flag before `remote-control`, or a wrapper script adds one, Claude Code doesn't carry the flag over to the sessions the server creates. Claude Code lets the flag through only when dropping it is known not to change what those sessions can do, such as `--verbose` or `--model`. For any other flag, such as `--settings`, Claude Code [refuses to start](https://code.claude.com/docs/en/errors.md#not-carried-over-to-the-sessions-remote-control-starts) and names the flag to remove.
7171 
72To sandbox the sessions the server starts, turn on [sandboxing](/docs/en/sandboxing.md) in a settings file.
72To sandbox the sessions the server starts, turn on [sandboxing](https://code.claude.com/docs/en/sandboxing.md) in a settings file.
7373 
7474To print the `remote-control` flags in your terminal, run `claude remote-control --help`. Before v2.1.302, without an eligible sign-in the command returned one of the errors under [Troubleshooting](#troubleshooting) instead.
7575 
from line 111
111111 
112112**VS Code**
113113 
114In the [Claude Code VS Code extension](/docs/en/vs-code.md), type `/remote-control` or `/rc` in the prompt box.
114In the [Claude Code VS Code extension](https://code.claude.com/docs/en/vs-code.md), type `/remote-control` or `/rc` in the prompt box.
115115 
116116```text theme={null}
117117/remote-control
from line 123
123123 
124124**Desktop app**
125125 
126In a local session in the [Claude Desktop app's](/docs/en/desktop.md) Code tab, type `/remote-control` or `/rc` in the prompt box.
126In a local session in the [Claude Desktop app's](https://code.claude.com/docs/en/desktop.md) Code tab, type `/remote-control` or `/rc` in the prompt box.
127127 
128128```text theme={null}
129129/remote-control
from line 168
168168 
169169A connected device shows the conversation in your terminal as it happens. These cases go beyond ordinary messages:
170170 
171* **Compaction and `/clear`**: while Claude Code [compacts the conversation](/docs/en/context-window.md#what-survives-compaction), connected devices show the progress and then where the conversation was compacted. When you run `/clear`, the conversation resets on connected devices too.
171* **Compaction and `/clear`**: while Claude Code [compacts the conversation](https://code.claude.com/docs/en/context-window.md#what-survives-compaction), connected devices show the progress and then where the conversation was compacted. When you run `/clear`, the conversation resets on connected devices too.
172172* **Switching conversations with `/resume`**: the connected device doesn't receive the switched-to conversation's title or earlier history, but new messages in both directions go to and from whichever conversation is open in your terminal. To work on the original conversation from the device again, run `/resume` in your terminal and switch back to it.
173* **Pulling a session with `/teleport`**: when you pull a [cloud session](/docs/en/claude-code-on-the-web.md#from-cloud-to-terminal) into your terminal with `/teleport`, the connected device doesn't receive the pulled conversation's earlier history. New messages in both directions go to and from the pulled conversation, which is now the one open in your terminal.
174* **Messages from your other sessions**: with [cross-session messaging](/docs/en/cross-session-messaging.md), the same connection carries messages between your own sessions on different machines.
173* **Pulling a session with `/teleport`**: when you pull a [cloud session](https://code.claude.com/docs/en/claude-code-on-the-web.md#from-cloud-to-terminal) into your terminal with `/teleport`, the connected device doesn't receive the pulled conversation's earlier history. New messages in both directions go to and from the pulled conversation, which is now the one open in your terminal.
174* **Messages from your other sessions**: with [cross-session messaging](https://code.claude.com/docs/en/cross-session-messaging.md), the same connection carries messages between your own sessions on different machines.
175175* **Diff of your changes**: when the session's directory is in a git repository, a connected device's diff pane shows your changes. On a branch that has commits ahead of the repository's default branch, the pane shows the changes since the branch split from it, including your uncommitted edits. On the default branch itself, or on a branch that isn't ahead of it, the pane shows only your uncommitted changes.
176* **Model**: when you pick a [model](/docs/en/model-config.md) from a connected device, Claude Code runs the session on that model. Requires Claude Code v2.1.238 or later. A model you pick from the device's model control applies to the current session only. When you send `/model <name>` from the device to an interactive session, Claude Code also sets your default for new sessions.
177* **Effort level**: when you set the [effort level](/docs/en/model-config.md#adjust-effort-level) from a connected device, with `/effort` or the device's effort control, Claude Code applies it to the session on your machine. If you pinned a level with `CLAUDE_CODE_EFFORT_LEVEL`, the session keeps that level, and Claude Code refuses a different pick from the effort control. Picking a level from the effort control requires Claude Code v2.1.234 or later on your machine.
178* **Reconnecting after a connection failure**: run `/remote-control` to reconnect. If compaction rewrote the conversation or you switched conversations with `/resume` in the meantime, Claude Code archives the server session it was using instead of leaving it in the session list. You can still find it by [filtering for archived sessions](/docs/en/claude-code-on-the-web.md#archive-sessions). Switching conversations while a device is still connected doesn't archive the session.
176* **Model**: when you pick a [model](https://code.claude.com/docs/en/model-config.md) from a connected device, Claude Code runs the session on that model. Requires Claude Code v2.1.238 or later. A model you pick from the device's model control applies to the current session only. When you send `/model <name>` from the device to an interactive session, Claude Code also sets your default for new sessions.
177* **Effort level**: when you set the [effort level](https://code.claude.com/docs/en/model-config.md#adjust-effort-level) from a connected device, with `/effort` or the device's effort control, Claude Code applies it to the session on your machine. If you pinned a level with `CLAUDE_CODE_EFFORT_LEVEL`, the session keeps that level, and Claude Code refuses a different pick from the effort control. Picking a level from the effort control requires Claude Code v2.1.234 or later on your machine.
178* **Reconnecting after a connection failure**: run `/remote-control` to reconnect. If compaction rewrote the conversation or you switched conversations with `/resume` in the meantime, Claude Code archives the server session it was using instead of leaving it in the session list. You can still find it by [filtering for archived sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md#archive-sessions). Switching conversations while a device is still connected doesn't archive the session.
179179 
180180### Enable Remote Control for all sessions
181181 
from line 182
182182Remote Control only activates when you explicitly run `claude remote-control`, `claude --remote-control`, or `/remote-control`, unless auto-connect is turned on. To turn auto-connect on for every interactive session, run `/config` inside Claude Code and set **Enable Remote Control for all sessions**. The toggle takes three values:
183183 
184184* **`true`**: connect automatically when an interactive session starts.
185* **`false`**: turn auto-connect off, though a `true` from [managed settings](/docs/en/managed-settings.md) outranks it, because Claude Code saves the choice to your user settings. A `false` in project or local settings (`.claude/settings.json`, `.claude/settings.local.json`) turns auto-connect off even over a managed `true`.
185* **`false`**: turn auto-connect off, though a `true` from [managed settings](https://code.claude.com/docs/en/managed-settings.md) outranks it, because Claude Code saves the choice to your user settings. A `false` in project or local settings (`.claude/settings.json`, `.claude/settings.local.json`) turns auto-connect off even over a managed `true`.
186186* **`default`**: clear your choice and follow your organization's admin default if one is set, otherwise Claude Code's current default. If you set `DISABLE_TELEMETRY` or `DO_NOT_TRACK`, see [Auto-connect with `DISABLE_TELEMETRY` or `DO_NOT_TRACK` set](#auto-connect-with-disable-telemetry-or-do-not-track).
187187 
188188The VS Code extension and the Desktop app also have an auto-connect toggle:
189189 
190* **VS Code extension**: **Enable Remote Control for all sessions** in the [command menu's](/docs/en/vs-code.md#use-the-prompt-box) Settings section.
191* **Desktop app**: **Settings > Claude Code > Connect new sessions to Remote Control**. See [Control which sessions appear on your other devices](/docs/en/desktop.md#control-which-sessions-appear-on-your-other-devices).
190* **VS Code extension**: **Enable Remote Control for all sessions** in the [command menu's](https://code.claude.com/docs/en/vs-code.md#use-the-prompt-box) Settings section.
191* **Desktop app**: **Settings > Claude Code > Connect new sessions to Remote Control**. See [Control which sessions appear on your other devices](https://code.claude.com/docs/en/desktop.md#control-which-sessions-appear-on-your-other-devices).
192192 
193To turn auto-connect on from a settings file instead, set [`remoteControlAtStartup`](/docs/en/settings-reference#remotecontrolatstartup) to `true` in your user `~/.claude/settings.json` or in [managed settings](/docs/en/managed-settings.md). In project or local settings (`.claude/settings.json`, `.claude/settings.local.json`), Claude Code honors a `false` and turns auto-connect off for that repository, but ignores a `true`, so a checked-in file can't turn on Remote Control for everyone who opens the repository.
193To turn auto-connect on from a settings file instead, set [`remoteControlAtStartup`](https://code.claude.com/docs/en/settings-reference#remotecontrolatstartup) to `true` in your user `~/.claude/settings.json` or in [managed settings](https://code.claude.com/docs/en/managed-settings.md). In project or local settings (`.claude/settings.json`, `.claude/settings.local.json`), Claude Code honors a `false` and turns auto-connect off for that repository, but ignores a `true`, so a checked-in file can't turn on Remote Control for everyone who opens the repository.
194194 
195195Auto-connect signs in with your own claude.ai account, so a session it starts appears only in your own account's Claude apps and grants no one else access.
196196 
from line 215
215215 
216216These commands work for about four hours after the server stopped. After that, run `claude remote-control` to start a new session. If you archived a session in the meantime, `--continue` and `--session-id` unarchive it on Claude Code v2.1.228 or later.
217217 
218To bring back a session you started with `claude --remote-control` or `/remote-control`, resume the conversation with `claude --continue` or `claude --resume`. For the permission mode that the resumed conversation starts in, see [permission mode on resume](/docs/en/sessions.md#permission-mode-on-resume). If Remote Control doesn't reconnect, see [Couldn't reconnect to your Remote Control session](#couldnt-reconnect-to-your-remote-control-session).
218To bring back a session you started with `claude --remote-control` or `/remote-control`, resume the conversation with `claude --continue` or `claude --resume`. For the permission mode that the resumed conversation starts in, see [permission mode on resume](https://code.claude.com/docs/en/sessions.md#permission-mode-on-resume). If Remote Control doesn't reconnect, see [Couldn't reconnect to your Remote Control session](#couldnt-reconnect-to-your-remote-control-session).
219219 
220220If you resume the conversation in a second terminal while the first one still has Remote Control on, Claude Code prints a `Remote Control not started here` notice in the second terminal and leaves Remote Control off there instead of taking the session away from the first. Run `/remote-control` in the second terminal to move Remote Control to it.
221221 
from line 236
236236 
237237All traffic travels through the Anthropic API over TLS, the same transport security as any Claude Code session. The connection uses multiple short-lived credentials, each scoped to a single purpose and expiring independently.
238238 
239While Remote Control is connected, the session transcript, including your messages, Claude's responses, and tool activity, is stored on Anthropic servers. The stored transcript keeps the conversation in sync across your devices and lets the session reconnect after a network drop. Execution and filesystem access stay on your machine, and stored transcripts are retained under the [Data usage](/docs/en/data-usage.md) policy.
239While Remote Control is connected, the session transcript, including your messages, Claude's responses, and tool activity, is stored on Anthropic servers. The stored transcript keeps the conversation in sync across your devices and lets the session reconnect after a network drop. Execution and filesystem access stay on your machine, and stored transcripts are retained under the [Data usage](https://code.claude.com/docs/en/data-usage.md) policy.
240240 
241To turn Remote Control off entirely, use the [`disableRemoteControl`](/docs/en/settings-reference#disableremotecontrol) setting. Organizations with [Zero Data Retention](/docs/en/zero-data-retention.md) enabled, or with the [HIPAA configuration](/docs/en/hipaa-setup.md) applied, can't enable Remote Control.
241To turn Remote Control off entirely, use the [`disableRemoteControl`](https://code.claude.com/docs/en/settings-reference#disableremotecontrol) setting. Organizations with [Zero Data Retention](https://code.claude.com/docs/en/zero-data-retention.md) enabled, or with the [HIPAA configuration](https://code.claude.com/docs/en/hipaa-setup.md) applied, can't enable Remote Control.
242242 
243243## Trusted Devices
244244 
from line 295
295295 
296296## Remote Control vs cloud sessions
297297 
298Remote Control and [cloud sessions](/docs/en/claude-code-on-the-web.md) both use the claude.ai/code interface. The key difference is where the session runs: Remote Control executes on your machine, so your local MCP servers, tools, and project configuration stay available. A cloud session executes on cloud infrastructure, Anthropic-managed by default.
298Remote Control and [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md) both use the claude.ai/code interface. The key difference is where the session runs: Remote Control executes on your machine, so your local MCP servers, tools, and project configuration stay available. A cloud session executes on cloud infrastructure, Anthropic-managed by default.
299299 
300Use Remote Control when you're in the middle of local work and want to keep going from another device. Use a cloud session when you want to start a task without any local setup, work on a repo you don't have cloned, or run multiple tasks in parallel. A [project](/docs/en/claude-projects.md) combines the two: its threads run in the cloud, and it uses Remote Control to [run a thread on your computer](/docs/en/claude-projects.md#run-a-thread-on-your-own-computer) when you ask for one there.
300Use Remote Control when you're in the middle of local work and want to keep going from another device. Use a cloud session when you want to start a task without any local setup, work on a repo you don't have cloned, or run multiple tasks in parallel. A [project](https://code.claude.com/docs/en/claude-projects.md) combines the two: its threads run in the cloud, and it uses Remote Control to [run a thread on your computer](https://code.claude.com/docs/en/claude-projects.md#run-a-thread-on-your-own-computer) when you ask for one there.
301301 
302302Claude Code offers several ways to work when you're not at your terminal. They differ in what triggers the work, where Claude runs, and how much you need to set up.
303303 
304304| | Trigger | Claude runs on | Setup | Best for |
305305| :- | :- | :- | :- | :- |
306| [Dispatch](/docs/en/desktop.md#sessions-from-dispatch) | Message a task from the Claude mobile app | Your machine (Desktop) | [Pair the mobile app with Desktop](https://support.claude.com/en/articles/13947068) | Delegating work while you're away, minimal setup |
307| [Remote Control](/docs/en/remote-control.md) | Drive a running session from [claude.ai/code](https://claude.ai/code) or the Claude mobile app | Your machine (CLI, Desktop, or VS Code) | Run [`claude remote-control` or `/remote-control`](/docs/en/remote-control#start-a-remote-control-session) | Steering in-progress work from another device |
308| [Channels](/docs/en/channels.md) | Push events from a chat app like Telegram or Discord, or your own server | Your machine (CLI) | [Install a channel plugin](/docs/en/channels.md#quickstart) or [build your own](/docs/en/channels-reference.md) | Reacting to external events like CI failures or chat messages |
309| [Slack](/docs/en/slack.md) | Mention `@Claude` in a team channel | Anthropic cloud | [Install the Slack app](/docs/en/slack.md#setting-up-claude-code-in-slack) with [Claude Code on the web](/docs/en/claude-code-on-the-web.md) enabled, on Pro and Max plans | PRs and reviews from team chat |
310| [Self-hosted environments](/docs/en/self-hosted-environments) | Start a [cloud session](/docs/en/claude-code-on-the-web.md) and pick your organization's environment | Your organization's infrastructure | [Deploy runners](/docs/en/self-hosted-environments-quickstart.md), on Team and Enterprise plans | Cloud sessions that must run inside your network |
311| [Scheduled tasks](/docs/en/scheduled-tasks.md) | Set a schedule | [CLI](/docs/en/scheduled-tasks.md), [Desktop](/docs/en/desktop-scheduled-tasks.md), or [cloud](/docs/en/routines.md) | Pick a frequency | Recurring automation like daily reviews |
306| [Dispatch](https://code.claude.com/docs/en/desktop.md#sessions-from-dispatch) | Message a task from the Claude mobile app | Your machine (Desktop) | [Pair the mobile app with Desktop](https://support.claude.com/en/articles/13947068) | Delegating work while you're away, minimal setup |
307| [Remote Control](https://code.claude.com/docs/en/remote-control.md) | Drive a running session from [claude.ai/code](https://claude.ai/code) or the Claude mobile app | Your machine (CLI, Desktop, or VS Code) | Run [`claude remote-control` or `/remote-control`](https://code.claude.com/docs/en/remote-control#start-a-remote-control-session) | Steering in-progress work from another device |
308| [Channels](https://code.claude.com/docs/en/channels.md) | Push events from a chat app like Telegram or Discord, or your own server | Your machine (CLI) | [Install a channel plugin](https://code.claude.com/docs/en/channels.md#quickstart) or [build your own](https://code.claude.com/docs/en/channels-reference.md) | Reacting to external events like CI failures or chat messages |
309| [Slack](https://code.claude.com/docs/en/slack.md) | Mention `@Claude` in a team channel | Anthropic cloud | [Install the Slack app](https://code.claude.com/docs/en/slack.md#setting-up-claude-code-in-slack) with [Claude Code on the web](https://code.claude.com/docs/en/claude-code-on-the-web.md) enabled, on Pro and Max plans | PRs and reviews from team chat |
310| [Self-hosted environments](https://code.claude.com/docs/en/self-hosted-environments) | Start a [cloud session](https://code.claude.com/docs/en/claude-code-on-the-web.md) and pick your organization's environment | Your organization's infrastructure | [Deploy runners](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md), on Team and Enterprise plans | Cloud sessions that must run inside your network |
311| [Scheduled tasks](https://code.claude.com/docs/en/scheduled-tasks.md) | Set a schedule | [CLI](https://code.claude.com/docs/en/scheduled-tasks.md), [Desktop](https://code.claude.com/docs/en/desktop-scheduled-tasks.md), or [cloud](https://code.claude.com/docs/en/routines.md) | Pick a frequency | Recurring automation like daily reviews |
312312 
313313## Mobile push notifications
314314 
from line 340
340340* On iOS, Focus modes and notification summaries can suppress or delay pushes. Check Settings → Notifications → Claude.
341341* On Android, aggressive battery optimization can delay delivery. Exempt the Claude app from battery optimization in system settings.
342342 
343Claude Code skips mobile push notifications while you are typing in or focused on the connected terminal. To extend this to any time you are at the machine, even in another window, set [`CLAUDE_CLIENT_PRESENCE_FILE`](/docs/en/env-vars) to a marker file path: notifications are skipped while the file exists. Configure a screen-lock listener or similar tool to create the file when your screen unlocks and delete it when your screen locks.
343Claude Code skips mobile push notifications while you are typing in or focused on the connected terminal. To extend this to any time you are at the machine, even in another window, set [`CLAUDE_CLIENT_PRESENCE_FILE`](https://code.claude.com/docs/en/env-vars) to a marker file path: notifications are skipped while the file exists. Configure a screen-lock listener or similar tool to create the file when your screen unlocks and delete it when your screen locks.
344344 
345345## Limitations
346346 
from line 353
353353 * **Interactive session**: keep working locally. Claude Code retries for as long as the outage lasts and reconnects on its own when the network returns.
354354* **Attachments that don't download**: if a file you attach from your phone or browser can't be downloaded to your machine, Claude still receives your message and the files that were downloaded. In place of the missing files, Claude Code adds a note such as `[1 of 3 attachments did not arrive]` to the message.
355355* **Presence heartbeats failing**: if an interactive session disconnects with `could not reach the Remote Control server for about 30 minutes`, run `/remote-control` to reconnect.
356* **Forwarded dialogs expire**: Claude Code keeps permission prompts and `AskUserQuestion` questions open until you answer them. When Claude Code forwards another kind of dialog to the remote session, such as the model-choice prompt shown after a safety refusal, it waits five minutes by default, then closes the dialog and continues with the dialog's no-action default. Set [`dialogExpiry`](/docs/en/settings-reference#dialogexpiry) to adjust or disable the deadline. Requires Claude Code v2.1.224 or later.
357* **The Fable usage-credits consent prompt isn't forwarded**: Claude Code shows the mid-session [Fable usage-credits consent prompt](/docs/en/model-config.md#fable-and-usage-credits) only where the session runs, not on your device. When the session runs in a terminal and nobody there answers before Claude Code closes the prompt, the turn ends without sending the request; see [The prompt to confirm went unanswered](/docs/en/errors.md#the-prompt-to-confirm-went-unanswered).
358* **Some commands are local-only**: commands that only run in the terminal interface, such as `/plugin` or `/resume`, work only from the local CLI, whether or not you pass an argument. `/claude-api` is also unavailable when you type it from mobile or web. Claude can still [load that skill on its own](/docs/en/skills.md#work-on-claude-api-projects) there. The following work from mobile and web:
356* **Forwarded dialogs expire**: Claude Code keeps permission prompts and `AskUserQuestion` questions open until you answer them. When Claude Code forwards another kind of dialog to the remote session, such as the model-choice prompt shown after a safety refusal, it waits five minutes by default, then closes the dialog and continues with the dialog's no-action default. Set [`dialogExpiry`](https://code.claude.com/docs/en/settings-reference#dialogexpiry) to adjust or disable the deadline. Requires Claude Code v2.1.224 or later.
357* **The Fable usage-credits consent prompt isn't forwarded**: Claude Code shows the mid-session [Fable usage-credits consent prompt](https://code.claude.com/docs/en/model-config.md#fable-and-usage-credits) only where the session runs, not on your device. When the session runs in a terminal and nobody there answers before Claude Code closes the prompt, the turn ends without sending the request; see [The prompt to confirm went unanswered](https://code.claude.com/docs/en/errors.md#the-prompt-to-confirm-went-unanswered).
358* **Some commands are local-only**: commands that only run in the terminal interface, such as `/plugin` or `/resume`, work only from the local CLI, whether or not you pass an argument. `/claude-api` is also unavailable when you type it from mobile or web. Claude can still [load that skill on its own](https://code.claude.com/docs/en/skills.md#work-on-claude-api-projects) there. The following work from mobile and web:
359359 * Text-output commands: `/compact`, `/clear`, `/context`, `/usage`, `/exit`, `/usage-credits`, `/recap`, and `/reload-plugins`. `/usage-credits` prints the billing URL instead of opening a browser. `/reload-plugins` works only when the session runs in an interactive terminal; a session without one declines it.
360360 * `/model`, `/effort`, `/fast`, `/color`, and `/rename`: pass the value as an argument, for example `/model sonnet` or `/effort high`. From mobile and web, `/model` and `/effort` take the argument in place of the terminal picker or slider.
361 * `/mcp`: from the mobile app, returns a text summary of server status instead of opening the picker. On the web, `/mcp` on its own opens a directory of [claude.ai connectors](/docs/en/mcp.md#use-mcp-servers-from-claude-ai) instead of returning the summary. The `reconnect`, `enable`, and `disable` [subcommands](/docs/en/commands.md#all-commands) work from both when the session runs in an interactive terminal. `/mcp reconnect` with no server name retries every server that has failed or needs authentication. To authorize a claude.ai connector without the picker, see [Authorize a connector again from your shell](#authorize-a-connector-again-from-your-shell).
361 * `/mcp`: from the mobile app, returns a text summary of server status instead of opening the picker. On the web, `/mcp` on its own opens a directory of [claude.ai connectors](https://code.claude.com/docs/en/mcp.md#use-mcp-servers-from-claude-ai) instead of returning the summary. The `reconnect`, `enable`, and `disable` [subcommands](https://code.claude.com/docs/en/commands.md#all-commands) work from both when the session runs in an interactive terminal. `/mcp reconnect` with no server name retries every server that has failed or needs authentication. To authorize a claude.ai connector without the picker, see [Authorize a connector again from your shell](#authorize-a-connector-again-from-your-shell).
362362 * `/config`: from the mobile app, pass `key=value` to set a setting, or run it with no argument to list the keys you can set. On the web, `/config` opens the Claude Code section of your settings instead.
363 * On Team and Enterprise, `/usage-credits` from mobile or web doesn't send a [usage-credits request to your admin](/docs/en/costs.md#add-usage-credits-to-your-subscription). Sending requires a confirmation that appears only in the interactive CLI, so the command tells you to run it there instead.
363 * On Team and Enterprise, `/usage-credits` from mobile or web doesn't send a [usage-credits request to your admin](https://code.claude.com/docs/en/costs.md#add-usage-credits-to-your-subscription). Sending requires a confirmation that appears only in the interactive CLI, so the command tells you to run it there instead.
364364 * `/autocompact`, from v2.1.221: pass the window size as an argument, for example `/autocompact 500k`. With no argument, it prints the current window size as text instead of opening the dialog the command shows in a terminal session.
365365 * `/advisor`, from v2.1.260: pass the model as an argument, for example `/advisor opus`, or pass `off` to turn the advisor off. Both forms apply to the current session only and leave your saved default unchanged. With no argument, it prints the current advisor as text instead of opening the picker.
366 * `/output-style`, from v2.1.269: pass the style name as an argument, for example `/output-style concise`, or run it with no argument to list the styles. From mobile and web, you can list and select only [built-in styles](/docs/en/output-styles.md#built-in-output-styles). To use a [custom style](/docs/en/output-styles.md#create-a-custom-output-style), select it in the session itself.
367 * `/focus`, from v2.1.281: pass `on` or `off` as an argument, for example `/focus on`, or run it with no argument to toggle the [focus view](/docs/en/commands.md#all-commands). Both forms apply to the current session only and leave your saved selection unchanged.
366 * `/output-style`, from v2.1.269: pass the style name as an argument, for example `/output-style concise`, or run it with no argument to list the styles. From mobile and web, you can list and select only [built-in styles](https://code.claude.com/docs/en/output-styles.md#built-in-output-styles). To use a [custom style](https://code.claude.com/docs/en/output-styles.md#create-a-custom-output-style), select it in the session itself.
367 * `/focus`, from v2.1.281: pass `on` or `off` as an argument, for example `/focus on`, or run it with no argument to toggle the [focus view](https://code.claude.com/docs/en/commands.md#all-commands). Both forms apply to the current session only and leave your saved selection unchanged.
368368 
369369## Authorize a connector again from your shell
370370 
371When a claude.ai connector needs authentication in a session you drive over Remote Control, the `/mcp` panel isn't available from the mobile app or the web. Get the authorization link from a terminal on the machine where the session runs, then open the link on the device you're using. You can't run the command from the mobile app or the web. A line you send from there that starts with `!` goes to Claude as a message and doesn't run in [shell mode](/docs/en/interactive-mode.md#shell-mode-with-prefix).
371When a claude.ai connector needs authentication in a session you drive over Remote Control, the `/mcp` panel isn't available from the mobile app or the web. Get the authorization link from a terminal on the machine where the session runs, then open the link on the device you're using. You can't run the command from the mobile app or the web. A line you send from there that starts with `!` goes to Claude as a message and doesn't run in [shell mode](https://code.claude.com/docs/en/interactive-mode.md#shell-mode-with-prefix).
372372 
373373**1. Get the authorization link**
374374 
from line 405
405405* Signed out, from `claude remote-control`: `You must be logged in to use Remote Control. Remote Control is only available with claude.ai subscriptions.`
406406* Signed in, but an API key or token is in use: `Remote Control requires claude.ai subscription auth.` followed by the credential in use, such as `ANTHROPIC_API_KEY is set, so this session is using API-key auth`. An `apiKeyHelper` setting and `ANTHROPIC_AUTH_TOKEN` are named the same way.
407407 
408Run `claude auth login` and choose the claude.ai option. If the message names `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN`, remove it wherever it's set: your shell environment or the `env` block of a [settings file](/docs/en/settings-reference.md#env). If it names `apiKeyHelper`, remove that setting.
408Run `claude auth login` and choose the claude.ai option. If the message names `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN`, remove it wherever it's set: your shell environment or the `env` block of a [settings file](https://code.claude.com/docs/en/settings-reference.md#env). If it names `apiKeyHelper`, remove that setting.
409409 
410410### "Remote Control requires a full-scope login token"
411411 
from line 429
429429 
430430### "Remote Control requires feature-flag evaluation"
431431 
432An [environment variable](/docs/en/env-vars.md#features-that-need-feature-flag-fetching) that turns off feature-flag evaluation is set, and the full message names the variable Claude Code found. On versions before 2.1.154, the same configuration produces "Remote Control is not yet enabled for your account" instead. What to do depends on the variable the message names:
432An [environment variable](https://code.claude.com/docs/en/env-vars.md#features-that-need-feature-flag-fetching) that turns off feature-flag evaluation is set, and the full message names the variable Claude Code found. On versions before 2.1.154, the same configuration produces "Remote Control is not yet enabled for your account" instead. What to do depends on the variable the message names:
433433 
434* **`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` or `DISABLE_GROWTHBOOK`**: unset the variable wherever it's set, in your shell environment or in the `env` block of a [`settings.json` file](/docs/en/settings-reference#all-settings).
434* **`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` or `DISABLE_GROWTHBOOK`**: unset the variable wherever it's set, in your shell environment or in the `env` block of a [`settings.json` file](https://code.claude.com/docs/en/settings-reference#all-settings).
435435* **`DISABLE_TELEMETRY` or `DO_NOT_TRACK`**: on a Pro, Max, Team, or Enterprise plan with `DISABLE_GROWTHBOOK` unset, these variables leave Remote Control available unless your organization requires [Trusted Devices](#trusted-devices). If it does, unset the variable wherever it's set to use Remote Control. On v2.1.154 through v2.1.282, you see this message with either variable set, so update Claude Code to a version that meets the [requirement for these variables](#requirements).
436436 
437437### "Remote Control is only available when using Claude via api.anthropic.com"
438438 
439The session isn't talking to the Anthropic API directly, which Remote Control requires. This happens on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry. It also happens when [`ANTHROPIC_BASE_URL`](/docs/en/env-vars) points at a host other than `api.anthropic.com`, such as an [LLM gateway](/docs/en/llm-gateway) or proxy, even if you sign in with claude.ai. See the [error reference](/docs/en/errors.md#remote-control-requires-the-anthropic-api) for the full cause list.
439The session isn't talking to the Anthropic API directly, which Remote Control requires. This happens on Amazon Bedrock, Google Cloud's Agent Platform, and Microsoft Foundry. It also happens when [`ANTHROPIC_BASE_URL`](https://code.claude.com/docs/en/env-vars) points at a host other than `api.anthropic.com`, such as an [LLM gateway](https://code.claude.com/docs/en/llm-gateway) or proxy, even if you sign in with claude.ai. See the [error reference](https://code.claude.com/docs/en/errors.md#remote-control-requires-the-anthropic-api) for the full cause list.
440440 
441The message names what routed the session away from the Anthropic API, such as `CLAUDE_CODE_USE_BEDROCK` or a custom `ANTHROPIC_BASE_URL`. If you have an eligible claude.ai login, unset the named variable, remove it from the `env` key in [settings](/docs/en/settings.md) if you set it there, and restart the session.
441The message names what routed the session away from the Anthropic API, such as `CLAUDE_CODE_USE_BEDROCK` or a custom `ANTHROPIC_BASE_URL`. If you have an eligible claude.ai login, unset the named variable, remove it from the `env` key in [settings](https://code.claude.com/docs/en/settings.md) if you set it there, and restart the session.
442442 
443443### "Remote Control is disabled by your organization's policy"
444444 
445445A policy blocks Remote Control. Check these causes in order:
446446 
447* **The error mentions `disableRemoteControl`**: your IT administrator has disabled Remote Control on this device through [managed settings](/docs/en/managed-settings.md), independent of the organization-wide toggle and of how you're signed in.
447* **The error mentions `disableRemoteControl`**: your IT administrator has disabled Remote Control on this device through [managed settings](https://code.claude.com/docs/en/managed-settings.md), independent of the organization-wide toggle and of how you're signed in.
448448* **Your claude.ai plan is Pro or Max**: Claude Code is still signed in under a Team or Enterprise organization from an earlier login, so it checks that organization's Remote Control policy. Run `/status` to see which plan and organization your sign-in uses. Run `claude auth logout` then `claude auth login` to sign in again under your current plan.
449* **The message doesn't say to contact your organization admin**: your organization has the [HIPAA configuration](/docs/en/hipaa-setup.md) applied, which turns Remote Control off. To confirm, run `/status` and look for `HIPAA` on the `Organization configuration` line. An Owner sees the **Remote Control** toggle grayed out in [admin settings](https://claude.ai/admin-settings/claude-code) and can't turn it on. If you have questions about the configuration, ask an Owner, who can contact your organization's Anthropic account team. Before v2.1.267, this case showed "Remote Control isn't available for your organization due to its compliance policy" instead.
449* **The message doesn't say to contact your organization admin**: your organization has the [HIPAA configuration](https://code.claude.com/docs/en/hipaa-setup.md) applied, which turns Remote Control off. To confirm, run `/status` and look for `HIPAA` on the `Organization configuration` line. An Owner sees the **Remote Control** toggle grayed out in [admin settings](https://claude.ai/admin-settings/claude-code) and can't turn it on. If you have questions about the configuration, ask an Owner, who can contact your organization's Anthropic account team. Before v2.1.267, this case showed "Remote Control isn't available for your organization due to its compliance policy" instead.
450450* **Remote Control works for others in your organization but not for you**: if your Enterprise organization manages your access with [custom roles](https://support.claude.com/en/articles/13930452), your roles can be the cause. Remote Control needs both the cloud sessions permission and the **Claude Code remote control** permission. Ask an Owner to check whether your roles include both and add what's missing at [**Organization settings > Roles**](https://claude.ai/admin-settings/roles).
451451* **Otherwise, an Owner hasn't enabled it for your organization**: Remote Control is off by default on Team and Enterprise plans. An Owner can enable it at [claude.ai/admin-settings/claude-code](https://claude.ai/admin-settings/claude-code) by turning on the **Remote Control** toggle. This toggle is a server-side organization setting.
452452 
from line 459
459459* **With `/remote-control`, `claude --remote-control`, or [auto-connect](#enable-remote-control-for-all-sessions)**: the session keeps running without Remote Control, and Claude Code archives it at claude.ai
460460* **With `claude remote-control`**: the server stops and archives the sessions it was serving, then exits
461461 
462You can still find an archived session by [filtering for archived sessions](/docs/en/claude-code-on-the-web.md#archive-sessions).
462You can still find an archived session by [filtering for archived sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md#archive-sessions).
463463 
464464Remote Control doesn't reconnect on its own. To turn it back on after your organization allows it again, run `/remote-control` in the session or `claude remote-control` in your shell. Either command fails with [`Remote Control is disabled by your organization's policy`](#remote-control-is-disabled-by-your-organizations-policy) until Claude Code on this machine has fetched the changed policy. An open session fetches it about once an hour. To find out what is blocking Remote Control, match the full text the command prints against that entry.
465465 
from line 498
498498 
499499### "Previous session is unavailable — run /remote-control to start a new one"
500500 
501Claude Code couldn't bring back the previous Remote Control session and stopped instead of starting a new one on its own. You can see this message after you resume a conversation with `claude --resume` or `claude --continue`, or after Claude Code [reconnects on its own following a disconnect](/docs/en/errors.md#remote-control-couldnt-refresh-your-login).
501Claude Code couldn't bring back the previous Remote Control session and stopped instead of starting a new one on its own. You can see this message after you resume a conversation with `claude --resume` or `claude --continue`, or after Claude Code [reconnects on its own following a disconnect](https://code.claude.com/docs/en/errors.md#remote-control-couldnt-refresh-your-login).
502502 
503503Run `/remote-control` to start a new Remote Control session under the current login; your local session keeps running without Remote Control in the meantime. The related message `Remote Control could not verify the signed-in account — run /remote-control to reconnect` has the same fix. If you run `/remote-control` after `Previous session is unavailable` without restarting Claude Code first, Claude Code leaves the conversation's earlier messages out of the new session.
504504 
from line 516
516516 
517517## Related resources
518518 
519* [Use Claude Code in the cloud](/docs/en/claude-code-on-the-web.md): run sessions in the cloud instead of your machine, configured through [cloud environments](/docs/en/cloud-environments.md)
520* [Cross-session messaging](/docs/en/cross-session-messaging.md): let Claude message your sessions on other machines or your [cloud sessions](/docs/en/claude-code-on-the-web.md)
521* [Channels](/docs/en/channels.md): forward Telegram, Discord, or iMessage into a session so Claude reacts to messages while you're away
522* [Dispatch](/docs/en/desktop.md#sessions-from-dispatch): message a task from your phone and it can spawn a Desktop session to handle it
523* [Authentication](/docs/en/authentication.md): set up `/login` and manage credentials for claude.ai
524* [CLI reference](/docs/en/cli-reference.md): full list of flags and commands including `claude remote-control`
525* [Security](/docs/en/security.md): how Remote Control sessions fit into the Claude Code security model
526* [Data usage](/docs/en/data-usage.md): what data flows through the Anthropic API during local, Remote Control, and cloud sessions
519* [Use Claude Code in the cloud](https://code.claude.com/docs/en/claude-code-on-the-web.md): run sessions in the cloud instead of your machine, configured through [cloud environments](https://code.claude.com/docs/en/cloud-environments.md)
520* [Cross-session messaging](https://code.claude.com/docs/en/cross-session-messaging.md): let Claude message your sessions on other machines or your [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md)
521* [Channels](https://code.claude.com/docs/en/channels.md): forward Telegram, Discord, or iMessage into a session so Claude reacts to messages while you're away
522* [Dispatch](https://code.claude.com/docs/en/desktop.md#sessions-from-dispatch): message a task from your phone and it can spawn a Desktop session to handle it
523* [Authentication](https://code.claude.com/docs/en/authentication.md): set up `/login` and manage credentials for claude.ai
524* [CLI reference](https://code.claude.com/docs/en/cli-reference.md): full list of flags and commands including `claude remote-control`
525* [Security](https://code.claude.com/docs/en/security.md): how Remote Control sessions fit into the Claude Code security model
526* [Data usage](https://code.claude.com/docs/en/data-usage.md): what data flows through the Anthropic API during local, Remote Control, and cloud sessions
527527 

routines Changed · +40 / -40 lines

from line 6
66>
77> Scheduled tasks are in research preview. Behavior, limits, and the API surface may change.
88 
9A cloud scheduled task, formerly a routine, is a saved Claude Code configuration: a prompt, one or more repositories, and a set of [connectors](/docs/en/mcp.md), packaged once and run automatically. Scheduled tasks execute on Anthropic-managed cloud infrastructure, or on your organization's [self-hosted environment](/docs/en/self-hosted-environments) when routed there, so they keep working when your laptop is closed.
9A cloud scheduled task, formerly a routine, is a saved Claude Code configuration: a prompt, one or more repositories, and a set of [connectors](https://code.claude.com/docs/en/mcp.md), packaged once and run automatically. Scheduled tasks execute on Anthropic-managed cloud infrastructure, or on your organization's [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) when routed there, so they keep working when your laptop is closed.
1010 
1111Each scheduled task can have one or more triggers attached to it:
1212 
from line 25
2525 
2626> [!NOTE]
2727>
28> Two other features schedule work on your machine instead of in the cloud: [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks.md) and [`/loop`](/docs/en/scheduled-tasks). To choose among all three, see [Compare scheduling options](/docs/en/scheduled-tasks.md#compare-scheduling-options).
28> Two other features schedule work on your machine instead of in the cloud: [Desktop scheduled tasks](https://code.claude.com/docs/en/desktop-scheduled-tasks.md) and [`/loop`](https://code.claude.com/docs/en/scheduled-tasks). To choose among all three, see [Compare scheduling options](https://code.claude.com/docs/en/scheduled-tasks.md#compare-scheduling-options).
2929 
3030Team and Enterprise Owners can turn off scheduled tasks for all members with the **Scheduled tasks (Was Routines)** toggle at [**Organization settings > Capabilities > Remote sessions**](https://claude.ai/admin-settings/capabilities). When the toggle is off, existing scheduled tasks stop running and members can't create new ones.
3131 
from line 46
4646 
4747Create a scheduled task from the web at [claude.ai/code/routines](https://claude.ai/code/routines), from the Desktop app, or from the CLI. All three surfaces write to the same cloud account, so a scheduled task you create in one shows up in the others immediately. In the Desktop app's **Code** tab, click **Scheduled** in the sidebar or in the sidebar's **More** menu, then **New task**, and continue with [Create from the web](#create-from-the-web).
4848 
49Scheduled tasks run autonomously as full Claude Code cloud sessions. The session runs shell commands, uses [skills](/docs/en/skills.md), and calls any connectors you include. Outside a [project](/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories)'s threads, those skills include the ones committed to the cloned repository. You don't choose a permission mode when you create the task: see [Permission prompts during a run](#permission-prompts-during-a-run) for when a run stops to wait for you.
49Scheduled tasks run autonomously as full Claude Code cloud sessions. The session runs shell commands, uses [skills](https://code.claude.com/docs/en/skills.md), and calls any connectors you include. Outside a [project](https://code.claude.com/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories)'s threads, those skills include the ones committed to the cloned repository. You don't choose a permission mode when you create the task: see [Permission prompts during a run](#permission-prompts-during-a-run) for when a run stops to wait for you.
5050 
5151What a scheduled task can reach is determined by:
5252 
5353* The repositories you select
54* The [environment's](/docs/en/cloud-environments.md) network access and variables
54* The [environment's](https://code.claude.com/docs/en/cloud-environments.md) network access and variables
5555* The connectors you include
5656 
5757Scope each of those to what the task needs.
from line 96
9696 
9797**5. Select an environment**
9898 
99After you select a repository, **Cloud environment** appears below it. Pick a [cloud environment](/docs/en/cloud-environments.md) for the task. Environments control what the cloud session has access to:
99After you select a repository, **Cloud environment** appears below it. Pick a [cloud environment](https://code.claude.com/docs/en/cloud-environments.md) for the task. Environments control what the cloud session has access to:
100100 
101101* **Network access**: set the level of internet access available during each run
102* **Environment variables**: provide values Claude can use during each run. They're [visible to anyone who uses the environment](/docs/en/cloud-environments.md#what-carries-over-from-your-setup), so in an Anthropic-hosted environment, store keys for the APIs Claude calls during a run as [network secrets](/docs/en/cloud-environments.md#add-network-secrets) instead. That section also lists the requests that never get a secret
103* **Setup script**: install dependencies and tools the task needs. The result is [cached](/docs/en/cloud-environments.md#environment-caching), so the script doesn't re-run on every session
102* **Environment variables**: provide values Claude can use during each run. They're [visible to anyone who uses the environment](https://code.claude.com/docs/en/cloud-environments.md#what-carries-over-from-your-setup), so in an Anthropic-hosted environment, store keys for the APIs Claude calls during a run as [network secrets](https://code.claude.com/docs/en/cloud-environments.md#add-network-secrets) instead. That section also lists the requests that never get a secret
103* **Setup script**: install dependencies and tools the task needs. The result is [cached](https://code.claude.com/docs/en/cloud-environments.md#environment-caching), so the script doesn't re-run on every session
104104 
105A **Default** environment is provided with **Trusted** network access, which allows only the [default allowlist](/docs/en/cloud-environments.md#default-allowed-domains) of package registries, cloud provider APIs, container registries, and common development domains through the session's network. Connectors you add to the task reach their services through Anthropic's servers, so they don't need allowlist changes.
105A **Default** environment is provided with **Trusted** network access, which allows only the [default allowlist](https://code.claude.com/docs/en/cloud-environments.md#default-allowed-domains) of package registries, cloud provider APIs, container registries, and common development domains through the session's network. Connectors you add to the task reach their services through Anthropic's servers, so they don't need allowlist changes.
106106 
107If your task needs to reach your own services directly, or a domain outside that list, edit the environment's [network access](/docs/en/cloud-environments.md#network-access) before running. To use a separate environment, [create one](/docs/en/cloud-environments.md#configure-your-environment) first.
107If your task needs to reach your own services directly, or a domain outside that list, edit the environment's [network access](https://code.claude.com/docs/en/cloud-environments.md#network-access) before running. To use a separate environment, [create one](https://code.claude.com/docs/en/cloud-environments.md#configure-your-environment) first.
108108 
109109**6. Review connectors**
110110 
111Switch to the **Connectors** tab of **Advanced settings**. All of your connected [MCP connectors](/docs/en/mcp.md) are included by default. Remove any the task doesn't need: Claude can use an included connector during a run, including writes. For when a run stops to ask you first, see [Permission prompts during a run](#permission-prompts-during-a-run).
111Switch to the **Connectors** tab of **Advanced settings**. All of your connected [MCP connectors](https://code.claude.com/docs/en/mcp.md) are included by default. Remove any the task doesn't need: Claude can use an included connector during a run, including writes. For when a run stops to ask you first, see [Permission prompts during a run](#permission-prompts-during-a-run).
112112 
113113**7. Create the task**
114114 
from line 337
337337 
338338> [!NOTE]
339339>
340> See [GitHub authentication options](/docs/en/claude-code-on-the-web.md#github-authentication-options) for the two ways to grant access. On Team and Enterprise plans, an [Owner](/docs/en/server-managed-settings.md#access-control) of your Claude organization has to turn on each method before you can use it; see [Connect GitHub](/docs/en/web-quickstart.md#connect-github).
340> See [GitHub authentication options](https://code.claude.com/docs/en/claude-code-on-the-web.md#github-authentication-options) for the two ways to grant access. On Team and Enterprise plans, an [Owner](https://code.claude.com/docs/en/server-managed-settings.md#access-control) of your Claude organization has to turn on each method before you can use it; see [Connect GitHub](https://code.claude.com/docs/en/web-quickstart.md#connect-github).
341341 
342342If your GitHub connection is missing or expired when a run is due, the scheduled task skips runs until you reconnect, for up to 72 hours. Reconnect GitHub within that window and the task resumes on its own. After 72 hours without a connection, the task turns off, and you turn it back on after reconnecting GitHub.
343343 
344344Each repository you add is cloned on every run. Claude starts from the repository's default branch unless your prompt specifies otherwise. If a [GitHub pull request event](#add-a-github-trigger) triggers the run and the pull request's repository is the task's first repository, that repository starts from the pull request's head commit instead.
345345 
346Claude pushes its work to a branch prefixed with `claude/` unless your prompt directs it to push to another branch. To control which branches a run can push to, use branch protection rules or rulesets on GitHub. For runs on Anthropic-managed infrastructure, and for self-hosted runs that push through [Anthropic's git proxy](/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy), GitHub applies them to the GitHub access you connected, so a rule that access can bypass doesn't block a run's push. A self-hosted run that pushes with the git credentials your deployment provides is checked against those instead. See [Configure git](/docs/en/self-hosted-environments-deploy.md#configure-git).
346Claude pushes its work to a branch prefixed with `claude/` unless your prompt directs it to push to another branch. To control which branches a run can push to, use branch protection rules or rulesets on GitHub. For runs on Anthropic-managed infrastructure, and for self-hosted runs that push through [Anthropic's git proxy](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy), GitHub applies them to the GitHub access you connected, so a rule that access can bypass doesn't block a run's push. A self-hosted run that pushes with the git credentials your deployment provides is checked against those instead. See [Configure git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#configure-git).
347347 
348348### Connectors
349349 
350350Scheduled tasks can use your connected MCP connectors to read from and write to external services during each run. For example, a task that triages support requests might read from a Slack channel and create issues in Linear.
351351 
352Connectors are the [claude.ai integrations](/docs/en/mcp.md#use-mcp-servers-from-claude-ai) on your account. MCP servers you added locally in the CLI with `claude mcp add` are stored on your machine rather than your claude.ai account, so they do not appear in the connectors list. To use one of those servers in a scheduled task:
352Connectors are the [claude.ai integrations](https://code.claude.com/docs/en/mcp.md#use-mcp-servers-from-claude-ai) on your account. MCP servers you added locally in the CLI with `claude mcp add` are stored on your machine rather than your claude.ai account, so they do not appear in the connectors list. To use one of those servers in a scheduled task:
353353 
354354* **As a connector**: add it as a connector at [claude.ai/customize/connectors](https://claude.ai/customize/connectors)
355* **In the repository**: for a scheduled task with one repository, you can instead declare the server in a committed [`.mcp.json`](/docs/en/mcp#project-scope), unless the task runs as threads in a [project](/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories). If it does, add the server as a connector.
355* **In the repository**: for a scheduled task with one repository, you can instead declare the server in a committed [`.mcp.json`](https://code.claude.com/docs/en/mcp#project-scope), unless the task runs as threads in a [project](https://code.claude.com/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories). If it does, add the server as a connector.
356356 
357357When you create a scheduled task, all of your currently connected connectors are included by default. Remove any that aren't needed to limit which tools Claude has access to during the run. You can also add connectors directly from the task's creation or edit dialog.
358358 
from line 362
362362 
363363> [!NOTE]
364364>
365> For a task that runs in your organization's self-hosted environment, see [Permissions and tool approval](/docs/en/self-hosted-environments-configuration.md#permissions-and-tool-approval).
365> For a task that runs in your organization's self-hosted environment, see [Permissions and tool approval](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#permissions-and-tool-approval).
366366 
367When a run starts a new session on Anthropic's cloud, that session begins in [auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) unless auto mode is unavailable. In auto mode, a classifier model allows or blocks actions that would otherwise wait for your approval.
367When a run starts a new session on Anthropic's cloud, that session begins in [auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) unless auto mode is unavailable. In auto mode, a classifier model allows or blocks actions that would otherwise wait for your approval.
368368 
369369A run still stops and waits for an answer in cases such as these:
370370 
371* **Actions that prompt even in auto mode**: for example, an action that matches an [ask rule](/docs/en/permissions.md#manage-permissions). See [How auto mode evaluates actions](/docs/en/permission-modes.md#how-auto-mode-evaluates-actions)
372* **Repeated blocks**: one block doesn't end the run. Once blocks reach auto mode's [repeated-block thresholds](/docs/en/permission-modes.md#repeated-block-thresholds), a permission prompt appears in the run's session and waits until someone answers it
371* **Actions that prompt even in auto mode**: for example, an action that matches an [ask rule](https://code.claude.com/docs/en/permissions.md#manage-permissions). See [How auto mode evaluates actions](https://code.claude.com/docs/en/permission-modes.md#how-auto-mode-evaluates-actions)
372* **Repeated blocks**: one block doesn't end the run. Once blocks reach auto mode's [repeated-block thresholds](https://code.claude.com/docs/en/permission-modes.md#repeated-block-thresholds), a permission prompt appears in the run's session and waits until someone answers it
373373 
374374If auto mode is unavailable, such as when your organization turned it off or the task's model doesn't support it, the run keeps the permission mode it launched in. To answer a waiting prompt, open the run from the task's [detail page](#view-and-interact-with-runs).
375375 
376In a [project](/docs/en/claude-projects.md), a run can continue a thread rather than start a new session. That run keeps whatever permission mode the thread is in at the time. When that thread was made, it started in auto mode unless auto mode was unavailable. To answer a prompt waiting in that thread, see [Unblock a thread waiting on approval](/docs/en/claude-projects.md#unblock-a-thread-waiting-on-approval).
376In a [project](https://code.claude.com/docs/en/claude-projects.md), a run can continue a thread rather than start a new session. That run keeps whatever permission mode the thread is in at the time. When that thread was made, it started in auto mode unless auto mode was unavailable. To answer a prompt waiting in that thread, see [Unblock a thread waiting on approval](https://code.claude.com/docs/en/claude-projects.md#unblock-a-thread-waiting-on-approval).
377377 
378378### When a run publishes an artifact without asking
379379 
380During a run in [auto mode](#permission-prompts-during-a-run), Claude publishes an [artifact](/docs/en/artifacts.md) without asking you once the classifier allows the publish. You're still asked first when, for example, the publish matches an [ask rule](/docs/en/permissions.md#manage-permissions). A new artifact is visible only to you until you [share it](/docs/en/artifacts.md#share-an-artifact). The [Accept edits rules](#artifacts-in-accept-edits-mode) that follow don't apply in auto mode.
380During a run in [auto mode](#permission-prompts-during-a-run), Claude publishes an [artifact](https://code.claude.com/docs/en/artifacts.md) without asking you once the classifier allows the publish. You're still asked first when, for example, the publish matches an [ask rule](https://code.claude.com/docs/en/permissions.md#manage-permissions). A new artifact is visible only to you until you [share it](https://code.claude.com/docs/en/artifacts.md#share-an-artifact). The [Accept edits rules](#artifacts-in-accept-edits-mode) that follow don't apply in auto mode.
381381 
382382When a scheduled task's job is to keep a page current, give it an artifact you already published.
383383 
384384#### Artifacts in Accept edits mode
385385 
386When a scheduled task's schedule or **Run now** starts a run, Claude can publish a new [artifact](/docs/en/artifacts.md) without asking. The new artifact is visible only to you until you [share it](/docs/en/artifacts.md#share-an-artifact). If the publish declares runtime capabilities for the page, such as [connector calls](/docs/en/artifacts.md#pull-live-data-with-mcp-connectors) or [file downloads](/docs/en/artifacts.md#offer-a-file-download), Claude asks first.
386When a scheduled task's schedule or **Run now** starts a run, Claude can publish a new [artifact](https://code.claude.com/docs/en/artifacts.md) without asking. The new artifact is visible only to you until you [share it](https://code.claude.com/docs/en/artifacts.md#share-an-artifact). If the publish declares runtime capabilities for the page, such as [connector calls](https://code.claude.com/docs/en/artifacts.md#pull-live-data-with-mcp-connectors) or [file downloads](https://code.claude.com/docs/en/artifacts.md#offer-a-file-download), Claude asks first.
387387 
388388In those runs, Claude republishes an existing artifact without asking only when all of these hold:
389389 
from line 393
393393* If the artifact is shared with specific people or your organization, its viewers don't automatically see each new version
394394* The publish doesn't force over a newer version
395395* The publish doesn't declare runtime capabilities for the page
396* The existing page holds no grant that reaches beyond the page, such as [connector calls](/docs/en/artifacts.md#pull-live-data-with-mcp-connectors)
396* The existing page holds no grant that reaches beyond the page, such as [connector calls](https://code.claude.com/docs/en/artifacts.md#pull-live-data-with-mcp-connectors)
397397 
398398In every other case, including a run that an API or GitHub trigger starts, Claude asks first.
399399 
400400### Environments and network access
401401 
402Each scheduled task uses a [cloud environment](/docs/en/cloud-environments.md) that controls network access, environment variables, and setup scripts. The task inherits the environment's network policy on every run.
402Each scheduled task uses a [cloud environment](https://code.claude.com/docs/en/cloud-environments.md) that controls network access, environment variables, and setup scripts. The task inherits the environment's network policy on every run.
403403 
404The **Default** environment uses **Trusted** network access, which allows only the [default allowlist](/docs/en/cloud-environments.md#default-allowed-domains) through the session's network. Requests on that path to hosts outside the allowlist fail with `403` and `x-deny-reason: host_not_allowed`. MCP connector traffic is routed through Anthropic's servers rather than that path, so the connectors you add to the task work without adding their hosts to **Allowed domains**. Remove any connectors you don't need under [Connectors](#connectors).
404The **Default** environment uses **Trusted** network access, which allows only the [default allowlist](https://code.claude.com/docs/en/cloud-environments.md#default-allowed-domains) through the session's network. Requests on that path to hosts outside the allowlist fail with `403` and `x-deny-reason: host_not_allowed`. MCP connector traffic is routed through Anthropic's servers rather than that path, so the connectors you add to the task work without adding their hosts to **Allowed domains**. Remove any connectors you don't need under [Connectors](#connectors).
405405 
406To allow additional domains on one of your own environments, follow these steps. An [organization-shared environment](/docs/en/cloud-environments.md#organization-shared-environments) opens read-only here, so an Owner changes its network access from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings) instead.
406To allow additional domains on one of your own environments, follow these steps. An [organization-shared environment](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) opens read-only here, so an Owner changes its network access from the **Cloud environments** page in [admin settings](https://claude.ai/admin-settings) instead.
407407 
408408**1. Open the task for editing**
409409 
from line 419
419419 
420420**4. Change the network access level**
421421 
422In the **Edit cloud environment** dialog, change **Network access** to **Custom** and enter your domains in **Allowed domains**. Check **Also include default list of common package managers** to keep the [default allowlist](/docs/en/cloud-environments.md#default-allowed-domains) alongside your custom domains. Select **Full** instead for unrestricted access.
422In the **Edit cloud environment** dialog, change **Network access** to **Custom** and enter your domains in **Allowed domains**. Check **Also include default list of common package managers** to keep the [default allowlist](https://code.claude.com/docs/en/cloud-environments.md#default-allowed-domains) alongside your custom domains. Select **Full** instead for unrestricted access.
423423 
424424**5. Save**
425425 
426426Click **Save changes** in the **Edit cloud environment** dialog, then close the **Edit scheduled task** dialog behind it. The new policy applies from the next run.
427427 
428See [Network access](/docs/en/cloud-environments.md#network-access) for details on access levels and the default allowlist.
428See [Network access](https://code.claude.com/docs/en/cloud-environments.md#network-access) for details on access levels and the default allowlist.
429429 
430430## Usage and limits
431431 
from line 464
464464 
465465The cause is usually one of the following:
466466 
467* You are authenticated with a Console API key, an [Anthropic profile or federation credential](/docs/en/authentication.md#anthropic-profiles-and-federation-credentials), or a cloud provider such as Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. `/schedule` requires a claude.ai subscription login. With a Console API key or a profile, and feature-flag fetching enabled, submitting `/schedule` instead shows `/schedule is available with Claude for Enterprise — ask your admin about migrating from API-key access`. With a cloud-provider login, you still see `Unknown command: /schedule`. If `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` is set in your shell, or `apiKeyHelper` is set in `settings.json`, remove it first, since these take precedence over a claude.ai login. A profile or federation credential takes precedence too, so switch that off as well
467* You are authenticated with a Console API key, an [Anthropic profile or federation credential](https://code.claude.com/docs/en/authentication.md#anthropic-profiles-and-federation-credentials), or a cloud provider such as Amazon Bedrock, Google Cloud's Agent Platform, or Microsoft Foundry. `/schedule` requires a claude.ai subscription login. With a Console API key or a profile, and feature-flag fetching enabled, submitting `/schedule` instead shows `/schedule is available with Claude for Enterprise — ask your admin about migrating from API-key access`. With a cloud-provider login, you still see `Unknown command: /schedule`. If `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` is set in your shell, or `apiKeyHelper` is set in `settings.json`, remove it first, since these take precedence over a claude.ai login. A profile or federation credential takes precedence too, so switch that off as well
468468* You are fully signed out, with no API key or other credential. With feature-flag fetching enabled, submitting `/schedule` shows `/schedule requires a claude.ai subscription. Run /login to sign in with your claude.ai account.` Before v2.1.268, a signed-out session showed the same Claude for Enterprise message as a Console API key
469469* You are inside a cloud session, where submitting `/schedule` answers that the command isn't available in that environment. Manage scheduled tasks from the [web UI](https://claude.ai/code/routines) instead
470* Your organization's policy disables [cloud sessions](/docs/en/claude-code-on-the-web.md), which scheduled tasks require. In this case, submitting `/schedule` answers [`Cloud sessions are disabled by your organization's policy`](/docs/en/errors#cloud-sessions-are-disabled-by-your-organizations-policy) instead. Before v2.1.268, it returned `Unknown command: /schedule`
470* Your organization's policy disables [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md), which scheduled tasks require. In this case, submitting `/schedule` answers [`Cloud sessions are disabled by your organization's policy`](https://code.claude.com/docs/en/errors#cloud-sessions-are-disabled-by-your-organizations-policy) instead. Before v2.1.268, it returned `Unknown command: /schedule`
471471* An Owner [turned off scheduled tasks](#routines-are-disabled-by-your-organizations-policy) for your Team or Enterprise organization. Before v2.1.227, the command still appeared in this case, and claude.ai rejected the task when Claude tried to create or run it
472472* Your Enterprise organization manages your access with [custom roles](https://support.claude.com/en/articles/13930452), and none of your roles includes the scheduled tasks permission under **Remote sessions**. Ask an Owner to add it to one of your roles
473473 
from line 479
479479 
480480## Related resources
481481 
482* [`/loop` and in-session scheduling](/docs/en/scheduled-tasks): schedule local tasks within an open CLI session
483* [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks.md): local scheduled tasks that run on your machine with access to local files
484* [Cloud environments](/docs/en/cloud-environments.md): configure network access, environment variables, and setup scripts for cloud sessions
485* [Projects](/docs/en/claude-projects.md): ongoing work Claude coordinates across parallel cloud sessions; scheduled tasks created from a project appear on its **Scheduled tasks** tab
486* [MCP connectors](/docs/en/mcp.md): connect external services like Slack, Linear, and Google Drive
487* [GitHub Actions](/docs/en/github-actions.md): run Claude in your CI pipeline on repository events
482* [`/loop` and in-session scheduling](https://code.claude.com/docs/en/scheduled-tasks): schedule local tasks within an open CLI session
483* [Desktop scheduled tasks](https://code.claude.com/docs/en/desktop-scheduled-tasks.md): local scheduled tasks that run on your machine with access to local files
484* [Cloud environments](https://code.claude.com/docs/en/cloud-environments.md): configure network access, environment variables, and setup scripts for cloud sessions
485* [Projects](https://code.claude.com/docs/en/claude-projects.md): ongoing work Claude coordinates across parallel cloud sessions; scheduled tasks created from a project appear on its **Scheduled tasks** tab
486* [MCP connectors](https://code.claude.com/docs/en/mcp.md): connect external services like Slack, Linear, and Google Drive
487* [GitHub Actions](https://code.claude.com/docs/en/github-actions.md): run Claude in your CI pipeline on repository events
488488 

sandbox-environments Changed · +31 / -31 lines

from line 8
88 
99> [!NOTE]
1010>
11> For the broader security model, see [Security](/docs/en/security.md). For Agent SDK deployments, see [Secure deployment](/docs/en/agent-sdk/secure-deployment.md).
11> For the broader security model, see [Security](https://code.claude.com/docs/en/security.md). For Agent SDK deployments, see [Secure deployment](https://code.claude.com/docs/en/agent-sdk/secure-deployment.md).
1212 
1313## Compare sandboxing approaches
1414 
from line 23
2323| [Virtual machine](#virtual-machine) | Full operating system | No | High |
2424| [Cloud sessions](#cloud-sessions) | Full operating system, hosted by Anthropic | No | None; requires a Claude subscription, and a connected GitHub account unless you launch with `claude --cloud` |
2525 
26The [sandboxed Bash tool](/docs/en/sandboxing.md) is built into Claude Code and restricts Bash commands. Built-in file tools, MCP servers, and hooks still run directly on your host. Every other approach in the table puts the whole Claude Code process inside the isolation boundary, so file tools, MCP servers, and hooks are restricted too.
26The [sandboxed Bash tool](https://code.claude.com/docs/en/sandboxing.md) is built into Claude Code and restricts Bash commands. Built-in file tools, MCP servers, and hooks still run directly on your host. Every other approach in the table puts the whole Claude Code process inside the isolation boundary, so file tools, MCP servers, and hooks are restricted too.
2727 
2828> [!WARNING]
2929>
30> Sandbox isolation reduces the impact of a breach, but it does not eliminate risk. Any approach that allows network egress can still leak data the agent can read, and any approach that mounts your project directory writable can still modify that code. Review the [security limitations](/docs/en/sandboxing.md#security-limitations) before relying on a sandbox as a hard control.
30> Sandbox isolation reduces the impact of a breach, but it does not eliminate risk. Any approach that allows network egress can still leak data the agent can read, and any approach that mounts your project directory writable can still modify that code. Review the [security limitations](https://code.claude.com/docs/en/sandboxing.md#security-limitations) before relying on a sandbox as a hard control.
3131>
32> Isolation also does not change what is sent to the model. Your prompts and the files Claude reads are transmitted to the Anthropic API or your configured provider with or without a sandbox. See [Data usage](/docs/en/data-usage.md) for what Claude Code sends and how to reduce it.
32> Isolation also does not change what is sent to the model. Your prompts and the files Claude reads are transmitted to the Anthropic API or your configured provider with or without a sandbox. See [Data usage](https://code.claude.com/docs/en/data-usage.md) for what Claude Code sends and how to reduce it.
3333 
3434## Choose an approach
3535 
from line 37
3737 
3838| You want to | Start with |
3939| :- | :- |
40| Reduce permission prompts during everyday work on your own machine | The [sandboxed Bash tool](/docs/en/sandboxing.md), configured with `/sandbox` |
41| Let Claude work unattended with `--dangerously-skip-permissions` or auto mode | The preconfigured [dev container](/docs/en/devcontainer.md), any container or VM, or the [sandbox runtime](#sandbox-runtime) |
40| Reduce permission prompts during everyday work on your own machine | The [sandboxed Bash tool](https://code.claude.com/docs/en/sandboxing.md), configured with `/sandbox` |
41| Let Claude work unattended with `--dangerously-skip-permissions` or auto mode | The preconfigured [dev container](https://code.claude.com/docs/en/devcontainer.md), any container or VM, or the [sandbox runtime](#sandbox-runtime) |
4242| Isolate MCP servers and hooks as well as Bash, without Docker | The sandbox runtime |
43| Work on an untrusted repository | A dedicated virtual machine, or a [cloud session](/docs/en/claude-code-on-the-web.md) if you have a Claude subscription; GitHub is not required when you launch with `claude --cloud` |
44| Standardize a sandboxed environment across a team | The preconfigured [dev container](/docs/en/devcontainer.md), copied into your repository |
45| Use Claude Code from a device with no local setup | A [cloud session](/docs/en/claude-code-on-the-web.md), which requires a Claude subscription and a connected GitHub account |
43| Work on an untrusted repository | A dedicated virtual machine, or a [cloud session](https://code.claude.com/docs/en/claude-code-on-the-web.md) if you have a Claude subscription; GitHub is not required when you launch with `claude --cloud` |
44| Standardize a sandboxed environment across a team | The preconfigured [dev container](https://code.claude.com/docs/en/devcontainer.md), copied into your repository |
45| Use Claude Code from a device with no local setup | A [cloud session](https://code.claude.com/docs/en/claude-code-on-the-web.md), which requires a Claude subscription and a connected GitHub account |
4646| Require isolation for every developer in your organization | [Enforce isolation across an organization](#enforce-isolation-across-an-organization) |
4747| Work on a native Windows host | A container or VM, or run the Bash sandbox inside WSL2 |
4848 
4949### How isolation relates to permission modes
5050 
51[Permission modes](/docs/en/permission-modes.md) decide whether a tool call runs and whether you are prompted first. Isolation restricts what a command can access once it runs. The two work together: when a permission mode lets actions run without asking you, an isolation boundary limits what those actions can reach.
51[Permission modes](https://code.claude.com/docs/en/permission-modes.md) decide whether a tool call runs and whether you are prompted first. Isolation restricts what a command can access once it runs. The two work together: when a permission mode lets actions run without asking you, an isolation boundary limits what those actions can reach.
5252 
53When you pass `--dangerously-skip-permissions`, Claude acts without asking you first. The [actions no mode auto-approves](/docs/en/permission-modes.md#actions-no-mode-auto-approves) still apply.
53When you pass `--dangerously-skip-permissions`, Claude acts without asking you first. The [actions no mode auto-approves](https://code.claude.com/docs/en/permission-modes.md#actions-no-mode-auto-approves) still apply.
5454 
5555With no prompts to catch mistakes, the isolation boundary you choose is what protects your system. Always run `--dangerously-skip-permissions` sessions inside a container, a VM, or the [sandbox runtime](#sandbox-runtime), so that file tools, MCP servers, and hooks are also inside the boundary. On Linux and macOS, Claude Code refuses to start with this flag when running as root, so run the container, VM, or sandbox runtime as a non-root user.
5656 
57[Auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) replaces the prompt with a classifier that reviews actions. The classifier is a per-action control, not an isolation boundary, so an isolation boundary still adds defense in depth for unattended runs, and is not required the way it is for `--dangerously-skip-permissions`.
57[Auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) replaces the prompt with a classifier that reviews actions. The classifier is a per-action control, not an isolation boundary, so an isolation boundary still adds defense in depth for unattended runs, and is not required the way it is for `--dangerously-skip-permissions`.
5858 
59The [sandboxed Bash tool](#sandboxed-bash-tool) on its own constrains only shell commands, so it is not sufficient for fully unattended runs in either mode. You can layer approaches: running the sandboxed Bash tool inside a container or VM gives you OS-level command restrictions on top of the outer environment boundary. For how the Bash sandbox itself interacts with permission rules and modes, see [How sandboxing relates to permissions and permission modes](/docs/en/sandboxing.md#how-sandboxing-relates-to-permissions-and-permission-modes).
59The [sandboxed Bash tool](#sandboxed-bash-tool) on its own constrains only shell commands, so it is not sufficient for fully unattended runs in either mode. You can layer approaches: running the sandboxed Bash tool inside a container or VM gives you OS-level command restrictions on top of the outer environment boundary. For how the Bash sandbox itself interacts with permission rules and modes, see [How sandboxing relates to permissions and permission modes](https://code.claude.com/docs/en/sandboxing.md#how-sandboxing-relates-to-permissions-and-permission-modes).
6060 
6161## Sandboxed Bash tool
6262 
from line 66
6666 
6767The sandboxed Bash tool is built into Claude Code. It uses operating system primitives to restrict the filesystem and network access of Bash, PowerShell, and Monitor tool commands Claude runs.
6868 
69Run the `/sandbox` command to open the sandbox panel and choose a mode. The [Sandboxing](/docs/en/sandboxing.md) guide covers the approval modes, the default boundary, and how to widen or narrow it.
69Run the `/sandbox` command to open the sandbox panel and choose a mode. The [Sandboxing](https://code.claude.com/docs/en/sandboxing.md) guide covers the approval modes, the default boundary, and how to widen or narrow it.
7070 
7171The per-command sandbox does not cover everything that runs in a session:
7272 
73* Other [built-in tools](/docs/en/tools-reference.md) such as Read, Edit, and WebFetch run inside the Claude Code process and do not spawn arbitrary code. [Permission rules](/docs/en/permissions.md) for path or domain gate them instead.
74* [MCP](/docs/en/mcp.md) servers, [command hooks](/docs/en/hooks.md#command-hook-fields), and [plugin monitors](/docs/en/plugins/components.md#monitors) are separate processes that run unconstrained on the host. For other processes that run this way, see [What runs outside the sandbox](/docs/en/sandboxing.md#what-runs-outside-the-sandbox).
73* Other [built-in tools](https://code.claude.com/docs/en/tools-reference.md) such as Read, Edit, and WebFetch run inside the Claude Code process and do not spawn arbitrary code. [Permission rules](https://code.claude.com/docs/en/permissions.md) for path or domain gate them instead.
74* [MCP](https://code.claude.com/docs/en/mcp.md) servers, [command hooks](https://code.claude.com/docs/en/hooks.md#command-hook-fields), and [plugin monitors](https://code.claude.com/docs/en/plugins/components.md#monitors) are separate processes that run unconstrained on the host. For other processes that run this way, see [What runs outside the sandbox](https://code.claude.com/docs/en/sandboxing.md#what-runs-outside-the-sandbox).
7575 
7676To put built-in tools, MCP servers, and hooks all behind one OS boundary, run the whole Claude Code process inside the [sandbox runtime](#sandbox-runtime), the [dev container](#dev-containers), or a [custom container](#custom-container).
7777 
from line 79
7979 
8080The [`@anthropic-ai/sandbox-runtime`](https://github.com/anthropics/sandbox-runtime) package wraps an entire process in the same Seatbelt or bubblewrap isolation that the built-in Bash sandbox uses. Running Claude Code through the runtime constrains the session's tools, hooks, and MCP servers as well as shell commands. The runtime is a beta research preview, and its configuration format may change as the package evolves.
8181 
82This section covers what you configure and what the runtime enforces on its own. For deploying the runtime in Agent SDK applications, see the [secure deployment guide](/docs/en/agent-sdk/secure-deployment.md#sandbox-runtime).
82This section covers what you configure and what the runtime enforces on its own. For deploying the runtime in Agent SDK applications, see the [secure deployment guide](https://code.claude.com/docs/en/agent-sdk/secure-deployment.md#sandbox-runtime).
8383 
8484### Set up and launch the runtime
8585 
86On Linux and WSL2, the runtime relies on the same `bubblewrap` and `socat` packages as the built-in sandbox, plus `ripgrep`, which Claude Code bundles but the standalone runtime resolves from your PATH. Install `bubblewrap` and `socat` as described in [Set up Linux and WSL2](/docs/en/sandboxing.md#set-up-linux-and-wsl2), and `ripgrep` from your distribution's package manager. On macOS you need no additional packages. The runtime uses the built-in Seatbelt sandbox there.
86On Linux and WSL2, the runtime relies on the same `bubblewrap` and `socat` packages as the built-in sandbox, plus `ripgrep`, which Claude Code bundles but the standalone runtime resolves from your PATH. Install `bubblewrap` and `socat` as described in [Set up Linux and WSL2](https://code.claude.com/docs/en/sandboxing.md#set-up-linux-and-wsl2), and `ripgrep` from your distribution's package manager. On macOS you need no additional packages. The runtime uses the built-in Seatbelt sandbox there.
8787 
8888By default the runtime denies network access and confines writes to a small set of built-in runtime paths, so configure it before launching Claude Code through it. Put your configuration in `~/.srt-settings.json`, or in a file you pass with `--settings`. The package [README](https://github.com/anthropics/sandbox-runtime) documents the configuration schema.
8989 
from line 91
9191 
9292* Your project directory.
9393* Claude Code's configuration paths `~/.claude` and `~/.claude.json`.
94* The directory where Claude Code writes runtime files. Unless you set [`CLAUDE_CODE_TMPDIR`](/docs/en/env-vars), that directory is:
94* The directory where Claude Code writes runtime files. Unless you set [`CLAUDE_CODE_TMPDIR`](https://code.claude.com/docs/en/env-vars), that directory is:
9595 * **Linux and WSL2**: `/tmp`
9696 * **macOS**: `/private/tmp`. `/tmp` is a symlink to that directory, and Seatbelt checks the resolved path.
9797 
from line 98
9898Allow the network domains your session needs:
9999 
100100* `api.anthropic.com`, or your configured provider's endpoint. On a third-party provider, keep `api.anthropic.com` as well: the WebFetch domain safety check still calls it by default unless you set `skipWebFetchPreflight: true`.
101* `claude.ai` and `platform.claude.com`, which [OAuth sign-in and token refresh](/docs/en/network-config.md#network-access-requirements) require. Runs authenticated with an API key can drop these two.
101* `claude.ai` and `platform.claude.com`, which [OAuth sign-in and token refresh](https://code.claude.com/docs/en/network-config.md#network-access-requirements) require. Runs authenticated with an API key can drop these two.
102102 
103103On Linux and WSL2, the runtime applies write grants only to paths that already exist. In a fresh environment, create Claude Code's configuration paths before the first launch:
104104 
from line 141
141141 
142142A dev container runs Claude Code inside a Docker container that VS Code or a compatible editor manages, with your project mounted in. You can define your own with a `.devcontainer/` directory in your repository.
143143 
144The claude-code repository publishes an [example dev container](/docs/en/devcontainer.md) with a default-deny iptables firewall as a starting point. Copy it into your repository and adjust the firewall allowlist, base image, and pinned Claude Code version to fit your environment. Because the firewall blocks unapproved egress, a configuration like this supports running Claude Code with `--dangerously-skip-permissions` for unattended work.
144The claude-code repository publishes an [example dev container](https://code.claude.com/docs/en/devcontainer.md) with a default-deny iptables firewall as a starting point. Copy it into your repository and adjust the firewall allowlist, base image, and pinned Claude Code version to fit your environment. Because the firewall blocks unapproved egress, a configuration like this supports running Claude Code with `--dangerously-skip-permissions` for unattended work.
145145 
146146## Custom container
147147 
from line 149
149149 
150150Several managed sandbox and remote execution services can host the container for you. The same checklist applies as for any container you operate: review what is mounted writable, what credentials and tokens are reachable inside it, and what the network egress policy allows.
151151 
152You can layer the built-in Bash sandbox inside the container for per-command restrictions. Unprivileged containers need `enableWeakerNestedSandbox`, described in [Bubblewrap fails to start inside a container](/docs/en/sandboxing.md#bubblewrap-fails-to-start-inside-a-container).
152You can layer the built-in Bash sandbox inside the container for per-command restrictions. Unprivileged containers need `enableWeakerNestedSandbox`, described in [Bubblewrap fails to start inside a container](https://code.claude.com/docs/en/sandboxing.md#bubblewrap-fails-to-start-inside-a-container).
153153 
154154## Virtual machine
155155 
from line 159
159159 
160160## Cloud sessions
161161 
162A [cloud session](/docs/en/claude-code-on-the-web.md) runs in an isolated, Anthropic-managed virtual machine. A network proxy enforces a default allowlist, and a separate proxy holds your GitHub token outside the sandbox while issuing scoped credentials for repository access inside it. Sessions your organization routes to a [self-hosted environment](/docs/en/self-hosted-environments) run on infrastructure you provision instead, where isolation, egress control, and git credentials are your deployment's responsibility.
162A [cloud session](https://code.claude.com/docs/en/claude-code-on-the-web.md) runs in an isolated, Anthropic-managed virtual machine. A network proxy enforces a default allowlist, and a separate proxy holds your GitHub token outside the sandbox while issuing scoped credentials for repository access inside it. Sessions your organization routes to a [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) run on infrastructure you provision instead, where isolation, egress control, and git credentials are your deployment's responsibility.
163163 
164Use this approach when you want full VM isolation without provisioning infrastructure yourself, or when you are delegating tasks from a device that does not have a local development environment. It requires a Claude subscription. Unless you launch from the CLI, you also need a connected GitHub account so the sandbox can clone your repository. When you launch from the CLI with `--cloud`, Claude Code can [bundle and upload your local repository](/docs/en/claude-code-on-the-web.md#send-local-repositories-without-github) instead. See [Use Claude Code in the cloud](/docs/en/claude-code-on-the-web.md) for plan availability and GitHub authentication options.
164Use this approach when you want full VM isolation without provisioning infrastructure yourself, or when you are delegating tasks from a device that does not have a local development environment. It requires a Claude subscription. Unless you launch from the CLI, you also need a connected GitHub account so the sandbox can clone your repository. When you launch from the CLI with `--cloud`, Claude Code can [bundle and upload your local repository](https://code.claude.com/docs/en/claude-code-on-the-web.md#send-local-repositories-without-github) instead. See [Use Claude Code in the cloud](https://code.claude.com/docs/en/claude-code-on-the-web.md) for plan availability and GitHub authentication options.
165165 
166166## Enforce isolation across an organization
167167 
168168Individual developers can opt into any of the sandboxing approaches on this page. What an organization can enforce, and with which tools, depends on the approach:
169169 
170* **Built-in Bash sandbox**: the only approach Claude Code enforces itself. Deliver the `sandbox` settings keys through [managed settings](/docs/en/managed-settings.md#delivery-mechanisms), either as a file managed by your MDM or through [server-managed settings](/docs/en/server-managed-settings.md) on Claude.ai. See [Enforce sandboxing with managed settings](/docs/en/sandboxing.md#enforce-sandboxing-with-managed-settings) for the keys to deploy and how to keep developers from widening the policy.
171* **Dev containers**: commit the [example dev container](/docs/en/devcontainer.md) to your repositories to standardize the environment across a team. This is a convention rather than an enforcement boundary, because Claude Code does not require a container. If developers should not be able to run Claude Code outside it, enforce that with your organization's device management or software allowlisting tools.
170* **Built-in Bash sandbox**: the only approach Claude Code enforces itself. Deliver the `sandbox` settings keys through [managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms), either as a file managed by your MDM or through [server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md) on Claude.ai. See [Enforce sandboxing with managed settings](https://code.claude.com/docs/en/sandboxing.md#enforce-sandboxing-with-managed-settings) for the keys to deploy and how to keep developers from widening the policy.
171* **Dev containers**: commit the [example dev container](https://code.claude.com/docs/en/devcontainer.md) to your repositories to standardize the environment across a team. This is a convention rather than an enforcement boundary, because Claude Code does not require a container. If developers should not be able to run Claude Code outside it, enforce that with your organization's device management or software allowlisting tools.
172172* **Custom containers and VMs**: distribute Claude Code through the approved image and use your organization's device management or software allowlisting tools to prevent installation outside it.
173173 
174174## See also
from line 175
175175 
176176These pages cover configuration and policy details for the sandboxing approaches on this page.
177177 
178* [Sandboxing](/docs/en/sandboxing.md): configure the built-in sandboxed Bash tool
179* [Dev container](/docs/en/devcontainer.md): the preconfigured Docker development container
180* [Security](/docs/en/security.md): the full Claude Code security model
181* [Secure deployment](/docs/en/agent-sdk/secure-deployment.md): isolation guidance for Agent SDK applications
182* [Settings](/docs/en/settings-reference.md#sandbox-settings): all sandbox configuration keys, including managed settings delivery
178* [Sandboxing](https://code.claude.com/docs/en/sandboxing.md): configure the built-in sandboxed Bash tool
179* [Dev container](https://code.claude.com/docs/en/devcontainer.md): the preconfigured Docker development container
180* [Security](https://code.claude.com/docs/en/security.md): the full Claude Code security model
181* [Secure deployment](https://code.claude.com/docs/en/agent-sdk/secure-deployment.md): isolation guidance for Agent SDK applications
182* [Settings](https://code.claude.com/docs/en/settings-reference.md#sandbox-settings): all sandbox configuration keys, including managed settings delivery
183183 

sandboxing Changed · +148 / -148 lines

from line 12
1212>
1313> This page covers the sandbox around shell commands on your own machine. Other pages cover related questions:
1414>
15> * For how a cloud session is isolated, see [Security and isolation](/docs/en/claude-code-on-the-web.md#security-and-isolation)
16> * To compare other isolation approaches such as dev containers, custom containers, and virtual machines, see [Sandbox environments](/docs/en/sandbox-environments.md)
17> * To reduce permission prompts for tools other than Bash, see [permission modes](/docs/en/permission-modes.md)
15> * For how a cloud session is isolated, see [Security and isolation](https://code.claude.com/docs/en/claude-code-on-the-web.md#security-and-isolation)
16> * To compare other isolation approaches such as dev containers, custom containers, and virtual machines, see [Sandbox environments](https://code.claude.com/docs/en/sandbox-environments.md)
17> * To reduce permission prompts for tools other than Bash, see [permission modes](https://code.claude.com/docs/en/permission-modes.md)
1818 
1919## What the sandbox restricts
2020 
21While the sandbox is on, the shell commands Claude runs start inside its boundary, and so do the processes they start. The sandbox is off by default. To turn it on, run `/sandbox` in a session, as [Get started](#get-started) shows, or set [`sandbox.enabled`](/docs/en/settings-reference#sandbox-enabled) to `true` in a [settings file](/docs/en/settings.md) such as `~/.claude/settings.json`.
21While the sandbox is on, the shell commands Claude runs start inside its boundary, and so do the processes they start. The sandbox is off by default. To turn it on, run `/sandbox` in a session, as [Get started](#get-started) shows, or set [`sandbox.enabled`](https://code.claude.com/docs/en/settings-reference#sandbox-enabled) to `true` in a [settings file](https://code.claude.com/docs/en/settings.md) such as `~/.claude/settings.json`.
2222 
2323The table shows what a sandboxed command can reach by default and the settings that change each default.
2424 
2525| Access | Default | Change it with |
2626| :- | :- | :- |
27| Writes | The working directory, a per-user temp directory, and [directories you've added](/docs/en/permissions.md#additional-directories-grant-file-access-not-configuration). [Protected paths](#protected-paths) stay write-denied | [`filesystem.allowWrite`](/docs/en/settings-reference#sandbox-filesystem-allowwrite), [`filesystem.denyWrite`](/docs/en/settings-reference#sandbox-filesystem-denywrite) |
28| Reads | Most of the machine, including credential files such as `~/.ssh` and `~/.aws/credentials` | [`filesystem.denyRead`](/docs/en/settings-reference#sandbox-filesystem-denyread), [`credentials`](#protect-credentials) |
29| Network | No direct route out. Connections go through a proxy on your machine that checks each host against your allowed domains, which start empty. Your permission mode decides [what happens to other hosts](#hosts-outside-your-allowed-domains) | [`network.allowedDomains`](/docs/en/settings-reference#sandbox-network-alloweddomains), [`network.deniedDomains`](/docs/en/settings-reference#sandbox-network-denieddomains) |
30| Environment variables | Inherited from Claude Code, including any secrets in its environment | [`credentials`](#protect-credentials), [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](/docs/en/env-vars) |
27| Writes | The working directory, a per-user temp directory, and [directories you've added](https://code.claude.com/docs/en/permissions.md#additional-directories-grant-file-access-not-configuration). [Protected paths](#protected-paths) stay write-denied | [`filesystem.allowWrite`](https://code.claude.com/docs/en/settings-reference#sandbox-filesystem-allowwrite), [`filesystem.denyWrite`](https://code.claude.com/docs/en/settings-reference#sandbox-filesystem-denywrite) |
28| Reads | Most of the machine, including credential files such as `~/.ssh` and `~/.aws/credentials` | [`filesystem.denyRead`](https://code.claude.com/docs/en/settings-reference#sandbox-filesystem-denyread), [`credentials`](#protect-credentials) |
29| Network | No direct route out. Connections go through a proxy on your machine that checks each host against your allowed domains, which start empty. Your permission mode decides [what happens to other hosts](#hosts-outside-your-allowed-domains) | [`network.allowedDomains`](https://code.claude.com/docs/en/settings-reference#sandbox-network-alloweddomains), [`network.deniedDomains`](https://code.claude.com/docs/en/settings-reference#sandbox-network-denieddomains) |
30| Environment variables | Inherited from Claude Code, including any secrets in its environment | [`credentials`](#protect-credentials), [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](https://code.claude.com/docs/en/env-vars) |
3131 
3232Claude Code builds the sandbox on the open source [`@anthropic-ai/sandbox-runtime`](https://github.com/anthropics/sandbox-runtime) package.
3333 
from line 35
3535 
3636The sandbox wraps shell commands. These tools and processes run outside it:
3737 
38* **Built-in file and web tools**: tools such as Read, Edit, Write, WebFetch, and WebSearch follow [permission rules](/docs/en/permissions.md) instead. A `denyRead` entry doesn't stop the Read tool, and `allowedDomains` doesn't limit WebFetch
39* **Other processes Claude Code starts**: command [hooks](/docs/en/hooks.md), local [MCP servers](/docs/en/mcp.md), [plugin monitors](/docs/en/plugins/components.md#monitors), [LSP servers](/docs/en/tools-reference.md#lsp-tool-behavior), and helper commands such as your [status line](/docs/en/statusline.md) command and `apiKeyHelper` run with your full access
38* **Built-in file and web tools**: tools such as Read, Edit, Write, WebFetch, and WebSearch follow [permission rules](https://code.claude.com/docs/en/permissions.md) instead. A `denyRead` entry doesn't stop the Read tool, and `allowedDomains` doesn't limit WebFetch
39* **Other processes Claude Code starts**: command [hooks](https://code.claude.com/docs/en/hooks.md), local [MCP servers](https://code.claude.com/docs/en/mcp.md), [plugin monitors](https://code.claude.com/docs/en/plugins/components.md#monitors), [LSP servers](https://code.claude.com/docs/en/tools-reference.md#lsp-tool-behavior), and helper commands such as your [status line](https://code.claude.com/docs/en/statusline.md) command and `apiKeyHelper` run with your full access
4040 
4141Some shell commands also run outside the sandbox, depending on your settings:
4242 
43* **Commands you type yourself**: a command you enter at the [`!` shell-mode prompt](/docs/en/interactive-mode#shell-mode-with-prefix) runs unsandboxed in most sessions. [Strict sandbox mode](#turn-off-the-retry-with-strict-sandbox-mode) lists the sessions where a command you type runs sandboxed
43* **Commands you type yourself**: a command you enter at the [`!` shell-mode prompt](https://code.claude.com/docs/en/interactive-mode#shell-mode-with-prefix) runs unsandboxed in most sessions. [Strict sandbox mode](#turn-off-the-retry-with-strict-sandbox-mode) lists the sessions where a command you type runs sandboxed
4444* **Excluded commands**: commands that match [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands) run unsandboxed
4545* **Unsandboxed retries**: Claude can [ask to run a command unsandboxed](#the-unsandboxed-retry-escape-hatch), usually after it fails in the sandbox
4646 
47To put the tools, processes, and commands in this section behind one boundary, run the Claude Code process itself in a [container, virtual machine, or the sandbox runtime](/docs/en/sandbox-environments.md).
47To put the tools, processes, and commands in this section behind one boundary, run the Claude Code process itself in a [container, virtual machine, or the sandbox runtime](https://code.claude.com/docs/en/sandbox-environments.md).
4848 
4949## Get started
5050 
from line 64
6464This opens the sandbox panel with three tabs, plus a Dependencies tab on Linux when the optional seccomp filter is missing:
6565 
6666* **Mode**: choose how sandboxed commands are approved, covered in the next step
67* **Overrides**: choose whether commands that fail under the sandbox can fall back to running unsandboxed. This is the [`allowUnsandboxedCommands`](/docs/en/settings-reference#sandbox-allowunsandboxedcommands) setting
67* **Overrides**: choose whether commands that fail under the sandbox can fall back to running unsandboxed. This is the [`allowUnsandboxedCommands`](https://code.claude.com/docs/en/settings-reference#sandbox-allowunsandboxedcommands) setting
6868* **Config**: view the resolved sandbox settings
6969 
7070If the panel shows only a Dependencies tab, a required package is missing. Install it as described in [Set up Linux and WSL2](#set-up-linux-and-wsl2), restart Claude Code, and run `/sandbox` again.
from line 75
7575 
7676**3. Run a Bash command**
7777 
78Ask Claude to run a command, such as a build or a test suite. By default, commands inside the sandbox can write to the working directory, a [per-user temp directory](/docs/en/env-vars.md), and any [directories you've added](/docs/en/permissions.md#additional-directories-grant-file-access-not-configuration) with `--add-dir`, `/add-dir`, or `permissions.additionalDirectories`.
78Ask Claude to run a command, such as a build or a test suite. By default, commands inside the sandbox can write to the working directory, a [per-user temp directory](https://code.claude.com/docs/en/env-vars.md), and any [directories you've added](https://code.claude.com/docs/en/permissions.md#additional-directories-grant-file-access-not-configuration) with `--add-dir`, `/add-dir`, or `permissions.additionalDirectories`.
7979 
80The first time a command needs a new network domain, Claude Code prompts for approval; in [auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode), Claude instead names the hosts a command needs [on the command itself](#per-command-allowed-domains-in-auto-mode) for the classifier to review with it.
80The first time a command needs a new network domain, Claude Code prompts for approval; in [auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode), Claude instead names the hosts a command needs [on the command itself](#per-command-allowed-domains-in-auto-mode) for the classifier to review with it.
8181 
8282To widen or narrow what the sandbox allows, see [Configure sandboxing](#configure-sandboxing).
8383 
8484If sandboxed commands fail with `Operation not permitted` inside a container, see [Bubblewrap fails to start inside a container](#bubblewrap-fails-to-start-inside-a-container).
8585 
86When you select a mode in the panel, Claude Code saves it to your project's local settings at `.claude/settings.local.json`, which apply to the current project. Claude Code adds that file to your global gitignore when it saves a setting there. To enable the sandbox across all of your projects, set [`sandbox.enabled`](/docs/en/settings-reference#sandbox-enabled) to `true` in your user settings at `~/.claude/settings.json`. To enforce sandboxing for every developer in an organization, use [managed settings](#enforce-sandboxing-with-managed-settings).
86When you select a mode in the panel, Claude Code saves it to your project's local settings at `.claude/settings.local.json`, which apply to the current project. Claude Code adds that file to your global gitignore when it saves a setting there. To enable the sandbox across all of your projects, set [`sandbox.enabled`](https://code.claude.com/docs/en/settings-reference#sandbox-enabled) to `true` in your user settings at `~/.claude/settings.json`. To enforce sandboxing for every developer in an organization, use [managed settings](#enforce-sandboxing-with-managed-settings).
8787 
88To change the sandbox for one session without writing to a settings file, start Claude Code with [`--settings`](/docs/en/settings#change-a-setting-for-one-session). For example, this command starts a sandboxed session in which Claude can't retry a blocked command outside the sandbox:
88To change the sandbox for one session without writing to a settings file, start Claude Code with [`--settings`](https://code.claude.com/docs/en/settings#change-a-setting-for-one-session). For example, this command starts a sandboxed session in which Claude can't retry a blocked command outside the sandbox:
8989 
9090```bash theme={null}
9191claude --settings '{"sandbox": {"enabled": true, "allowUnsandboxedCommands": false}}'
from line 93
9393 
9494> [!WARNING]
9595>
96> By default, if the sandbox can't start because a dependency is missing or the platform is unsupported, Claude Code runs commands without sandboxing. To make Claude Code exit at startup instead, set [`sandbox.failIfUnavailable`](/docs/en/settings-reference#sandbox-failifunavailable) to `true`. Managed deployments that require sandboxing as a security gate can use this setting.
96> By default, if the sandbox can't start because a dependency is missing or the platform is unsupported, Claude Code runs commands without sandboxing. To make Claude Code exit at startup instead, set [`sandbox.failIfUnavailable`](https://code.claude.com/docs/en/settings-reference#sandbox-failifunavailable) to `true`. Managed deployments that require sandboxing as a security gate can use this setting.
9797 
9898### Confirm commands run inside the sandbox
9999 
from line 161
161161>
162162> Check your WSL version with `wsl -l -v` from PowerShell. If you see `Sandboxing requires WSL2`, your distribution is running WSL1. Upgrade it to WSL2 or run Claude Code without sandboxing.
163163>
164> On WSL2, WSL hands a launch of a Windows binary such as `cmd.exe`, `powershell.exe`, or anything under `/mnt/c/` to the Windows host over a Unix socket, so whether a sandboxed command can launch one follows the sandbox's [Unix-socket settings](/docs/en/settings-reference.md#sandbox-network-allowunixsockets): the optional seccomp filter has to be installed to block the socket in the first place. To allow these launches, set `allowAllUnixSockets`, which opens every Unix socket to sandboxed commands.
164> On WSL2, WSL hands a launch of a Windows binary such as `cmd.exe`, `powershell.exe`, or anything under `/mnt/c/` to the Windows host over a Unix socket, so whether a sandboxed command can launch one follows the sandbox's [Unix-socket settings](https://code.claude.com/docs/en/settings-reference.md#sandbox-network-allowunixsockets): the optional seccomp filter has to be installed to block the socket in the first place. To allow these launches, set `allowAllUnixSockets`, which opens every Unix socket to sandboxed commands.
165165 
166166### Sandbox modes
167167 
from line 169
169169 
170170#### Auto-allow mode
171171 
172Claude Code approves a command automatically, with no prompt, when the command runs inside the sandbox. A command goes through the regular [permission flow](/docs/en/permissions.md) when it runs outside the sandbox because it matches [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands) or because Claude [retries it unsandboxed](#the-unsandboxed-retry-escape-hatch).
172Claude Code approves a command automatically, with no prompt, when the command runs inside the sandbox. A command goes through the regular [permission flow](https://code.claude.com/docs/en/permissions.md) when it runs outside the sandbox because it matches [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands) or because Claude [retries it unsandboxed](#the-unsandboxed-retry-escape-hatch).
173173 
174174A sandboxed command that connects to a host you haven't allowed stays in the sandbox. [Hosts outside your allowed domains](#hosts-outside-your-allowed-domains) covers who decides whether the connection goes through.
175175 
176176Even in auto-allow mode, the following still apply:
177177 
178* A command in a [form that a `Bash` deny rule matches](/docs/en/permissions#bash-rule-limits) is still blocked
179* `rm` or `rmdir` commands recognized as targeting a [critical path](/docs/en/permission-modes.md#which-paths-are-critical) still go through the regular permission flow
180* Content-scoped [ask rules](/docs/en/permissions.md) like `Bash(git push *)` still force a prompt even for sandboxed commands
181* A bare `Bash` ask rule, or the equivalent `Bash(*)` form, is skipped for commands that run sandboxed; it still applies to commands that run outside the sandbox. In [plan mode](/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode), the rule isn't skipped: it prompts for sandboxed commands too, including read-only ones
182* [Monitor tool](/docs/en/tools-reference.md#monitor-tool) commands aren't approved automatically, though they still run in the sandbox. To skip the prompt, add an [allow rule](/docs/en/permissions.md#bash) that matches the command, such as `Bash(npm run *)`
178* A command in a [form that a `Bash` deny rule matches](https://code.claude.com/docs/en/permissions#bash-rule-limits) is still blocked
179* `rm` or `rmdir` commands recognized as targeting a [critical path](https://code.claude.com/docs/en/permission-modes.md#which-paths-are-critical) still go through the regular permission flow
180* Content-scoped [ask rules](https://code.claude.com/docs/en/permissions.md) like `Bash(git push *)` still force a prompt even for sandboxed commands
181* A bare `Bash` ask rule, or the equivalent `Bash(*)` form, is skipped for commands that run sandboxed; it still applies to commands that run outside the sandbox. In [plan mode](https://code.claude.com/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode), the rule isn't skipped: it prompts for sandboxed commands too, including read-only ones
182* [Monitor tool](https://code.claude.com/docs/en/tools-reference.md#monitor-tool) commands aren't approved automatically, though they still run in the sandbox. To skip the prompt, add an [allow rule](https://code.claude.com/docs/en/permissions.md#bash) that matches the command, such as `Bash(npm run *)`
183183 
184Some sandboxed commands also go through the regular [permission flow](/docs/en/permissions.md) instead of being approved automatically. This happens with commands such as `FOO=bar npm test`, where a variable set in front of a command can change which program runs, and with `make CC=clang` or `eval "ls"`. Your allow rules and permission mode then decide whether you see a prompt, and the command still runs inside the sandbox.
184Some sandboxed commands also go through the regular [permission flow](https://code.claude.com/docs/en/permissions.md) instead of being approved automatically. This happens with commands such as `FOO=bar npm test`, where a variable set in front of a command can change which program runs, and with `make CC=clang` or `eval "ls"`. Your allow rules and permission mode then decide whether you see a prompt, and the command still runs inside the sandbox.
185185 
186For these three commands, an [allow rule](/docs/en/permissions.md#bash) that matches the command stops a recurring prompt. These rules show the pattern:
186For these three commands, an [allow rule](https://code.claude.com/docs/en/permissions.md#bash) that matches the command stops a recurring prompt. These rules show the pattern:
187187 
188188* `Bash(make *)` approves `make CC=clang`
189189* `Bash(FOO=bar npm *)` approves `FOO=bar npm test`. When the command starts with a variable assignment, include the assignment in the rule
from line 193
193193 
194194> [!NOTE]
195195>
196> Auto-allow mode works independently of your permission mode setting, with three exceptions: [plan mode](/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode), an auto mode command that carries [per-command allowed domains](#per-command-allowed-domains-in-auto-mode), and [server-side classifier review](/docs/en/permission-modes.md#how-the-classifier-evaluates-actions) of sandboxed commands in auto mode. Even if you're not in "accept edits" mode, sandboxed Bash commands run automatically when auto-allow is enabled. This means Bash commands that modify files within the sandbox boundaries execute without prompting, even in Manual mode, where the file edit tools would prompt.
196> Auto-allow mode works independently of your permission mode setting, with three exceptions: [plan mode](https://code.claude.com/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode), an auto mode command that carries [per-command allowed domains](#per-command-allowed-domains-in-auto-mode), and [server-side classifier review](https://code.claude.com/docs/en/permission-modes.md#how-the-classifier-evaluates-actions) of sandboxed commands in auto mode. Even if you're not in "accept edits" mode, sandboxed Bash commands run automatically when auto-allow is enabled. This means Bash commands that modify files within the sandbox boundaries execute without prompting, even in Manual mode, where the file edit tools would prompt.
197197>
198> In plan mode, auto-allow doesn't widen approvals; see [plan mode](/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode) for how Claude Code gates commands while you plan.
198> In plan mode, auto-allow doesn't widen approvals; see [plan mode](https://code.claude.com/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode) for how Claude Code gates commands while you plan.
199199 
200200#### Regular permissions mode
201201 
from line 209
209209 
210210* **`bypassPermissions` mode**: the retry runs without a prompt
211211* **Manual mode and `acceptEdits` mode**: you get a prompt titled "Bash command (unsandboxed)"
212* **[Auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode)**: a separate classifier model evaluates the underlying command
212* **[Auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode)**: a separate classifier model evaluates the underlying command
213213* **`dontAsk` mode**: Claude Code denies the retry
214* **Plan mode**: see [how Claude Code gates commands while you plan](/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode)
214* **Plan mode**: see [how Claude Code gates commands while you plan](https://code.claude.com/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode)
215215 
216216These rules and settings change who approves the retry:
217217 
218218* **A matching allow rule**: if an allow rule such as `Bash(curl *)` matches the command, it also approves the retry, so the command runs outside the sandbox with no prompt
219* **An ask rule for the parameter**: add an [ask rule](/docs/en/permissions.md#match-by-input-parameter) for `Bash(dangerouslyDisableSandbox:true)` to be prompted on Bash retries. You get the prompt in auto mode and `bypassPermissions` mode too, and the rule takes precedence over a matching allow rule
220* **[`permissions.blockReadsOutsideWorkingDirectories`](/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories)**: [Actions no mode auto-approves](/docs/en/permission-modes.md#actions-no-mode-auto-approves) covers the retries that prompt while it's on
219* **An ask rule for the parameter**: add an [ask rule](https://code.claude.com/docs/en/permissions.md#match-by-input-parameter) for `Bash(dangerouslyDisableSandbox:true)` to be prompted on Bash retries. You get the prompt in auto mode and `bypassPermissions` mode too, and the rule takes precedence over a matching allow rule
220* **[`permissions.blockReadsOutsideWorkingDirectories`](https://code.claude.com/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories)**: [Actions no mode auto-approves](https://code.claude.com/docs/en/permission-modes.md#actions-no-mode-auto-approves) covers the retries that prompt while it's on
221221 
222222#### Turn off the retry with strict sandbox mode
223223 
224You can disable the unsandboxed retry by setting `"allowUnsandboxedCommands": false` in your [sandbox settings](/docs/en/settings-reference.md#sandbox-settings). With the retry disabled, Claude Code ignores the `dangerouslyDisableSandbox` parameter. While the sandbox is running, commands Claude runs are then sandboxed unless they match an `excludedCommands` entry. To keep Claude Code from running commands unsandboxed when the sandbox can't start, also set [`failIfUnavailable`](/docs/en/settings-reference#sandbox-failifunavailable). The `/sandbox` **Overrides** tab shows this setting as **Strict sandbox mode**.
224You can disable the unsandboxed retry by setting `"allowUnsandboxedCommands": false` in your [sandbox settings](https://code.claude.com/docs/en/settings-reference.md#sandbox-settings). With the retry disabled, Claude Code ignores the `dangerouslyDisableSandbox` parameter. While the sandbox is running, commands Claude runs are then sandboxed unless they match an `excludedCommands` entry. To keep Claude Code from running commands unsandboxed when the sandbox can't start, also set [`failIfUnavailable`](https://code.claude.com/docs/en/settings-reference#sandbox-failifunavailable). The `/sandbox` **Overrides** tab shows this setting as **Strict sandbox mode**.
225225 
226226A `false` in your user settings, `--settings`, or managed settings holds even when a project's settings set `true`. A `false` in your user settings doesn't make the sandbox admin-required, so a project's other sandbox settings still apply. Before v2.1.285, a project's `true` overrode a `false` in your user settings.
227227 
228228If you or your administrator disable the retry in managed settings or with the `--settings` flag, the sandbox becomes admin-required. Claude Code then ignores the settings in a repository's files that loosen the sandbox, `excludedCommands` entries included. [Repository settings under an admin-required sandbox](#repository-settings-under-an-admin-required-sandbox) lists them.
229229 
230Strict sandbox mode applies to the commands Claude runs. Commands you type yourself at the [`!` shell-mode prompt](/docs/en/interactive-mode#shell-mode-with-prefix) run outside the sandbox unless the session is one of these:
230Strict sandbox mode applies to the commands Claude runs. Commands you type yourself at the [`!` shell-mode prompt](https://code.claude.com/docs/en/interactive-mode#shell-mode-with-prefix) run outside the sandbox unless the session is one of these:
231231 
232* **A [background session](/docs/en/agent-view.md)**: strict sandbox mode covers shell-mode commands too
233* **A Linux session with [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](/docs/en/env-vars#variables) set**: every command runs sandboxed, shell-mode commands included
232* **A [background session](https://code.claude.com/docs/en/agent-view.md)**: strict sandbox mode covers shell-mode commands too
233* **A Linux session with [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](https://code.claude.com/docs/en/env-vars#variables) set**: every command runs sandboxed, shell-mode commands included
234234 
235235Before v2.1.260, strict sandbox mode sandboxed shell-mode commands in every session.
236236 
from line 238
238238 
239239A per-user temp directory is writable inside the sandbox by default, alongside the working directory. Unless you [disable filesystem isolation](#disable-filesystem-isolation), Claude Code sets `$TMPDIR` to this directory for sandboxed commands, so tools that write temporary files work without extra configuration.
240240 
241Unsandboxed commands inherit your shell's `$TMPDIR` when it is set, so while filesystem isolation is on, sandboxed and unsandboxed commands resolve `$TMPDIR` to different directories. If your shell leaves `$TMPDIR` unset or empty, an unsandboxed command that references `$TMPDIR` receives your [`CLAUDE_CODE_TMPDIR`](/docs/en/env-vars) override, or the operating system's temp directory when you haven't set one or the override is a long path, so the variable doesn't expand to an empty string. To pass temporary files between the two, write them under the working directory instead.
241Unsandboxed commands inherit your shell's `$TMPDIR` when it is set, so while filesystem isolation is on, sandboxed and unsandboxed commands resolve `$TMPDIR` to different directories. If your shell leaves `$TMPDIR` unset or empty, an unsandboxed command that references `$TMPDIR` receives your [`CLAUDE_CODE_TMPDIR`](https://code.claude.com/docs/en/env-vars) override, or the operating system's temp directory when you haven't set one or the override is a long path, so the variable doesn't expand to an empty string. To pass temporary files between the two, write them under the working directory instead.
242242 
243243## Configure sandboxing
244244 
245Customize sandbox behavior through your `settings.json` file. See [Settings](/docs/en/settings-reference.md#sandbox-settings) for the complete configuration reference.
245Customize sandbox behavior through your `settings.json` file. See [Settings](https://code.claude.com/docs/en/settings-reference.md#sandbox-settings) for the complete configuration reference.
246246 
247By default, sandboxed commands can write to the current working directory, the per-user temp directory, and any [directories you've added](/docs/en/permissions.md#additional-directories-grant-file-access-not-configuration) with `--add-dir`, `/add-dir`, or `permissions.additionalDirectories`. If subprocess commands like `kubectl`, `terraform`, or `npm` need to write outside those directories, use `sandbox.filesystem.allowWrite` to grant access to specific paths:
247By default, sandboxed commands can write to the current working directory, the per-user temp directory, and any [directories you've added](https://code.claude.com/docs/en/permissions.md#additional-directories-grant-file-access-not-configuration) with `--add-dir`, `/add-dir`, or `permissions.additionalDirectories`. If subprocess commands like `kubectl`, `terraform`, or `npm` need to write outside those directories, use `sandbox.filesystem.allowWrite` to grant access to specific paths:
248248 
249249```json theme={null}
250250{
from line 259
259259 
260260These paths are enforced at the OS level, so all commands running inside the sandbox, including their child processes, respect them. This is the recommended approach when a tool needs write access to a specific location, rather than excluding the tool from the sandbox entirely with `excludedCommands`.
261261 
262When you define the same filesystem array in multiple [settings scopes](/docs/en/settings.md#settings-precedence), Claude Code merges them, combining the paths rather than replacing one scope's array with another's. Claude Code leaves an entry out of the merge when a lock under [Keep developers from widening the policy](#keep-developers-from-widening-the-policy) covers it.
262When you define the same filesystem array in multiple [settings scopes](https://code.claude.com/docs/en/settings.md#settings-precedence), Claude Code merges them, combining the paths rather than replacing one scope's array with another's. Claude Code leaves an entry out of the merge when a lock under [Keep developers from widening the policy](#keep-developers-from-widening-the-policy) covers it.
263263 
264If you exclude a source with [`--setting-sources`](/docs/en/cli-reference) on the CLI or [`settingSources`](/docs/en/agent-sdk/claude-code-features#control-filesystem-settings-with-settingsources) in the Agent SDK, Claude Code ignores its `sandbox.filesystem` entries, its `Edit` permission rules, and its `Read` deny rules when building the sandbox configuration. Requires Claude Code v2.1.246 or later.
264If you exclude a source with [`--setting-sources`](https://code.claude.com/docs/en/cli-reference) on the CLI or [`settingSources`](https://code.claude.com/docs/en/agent-sdk/claude-code-features#control-filesystem-settings-with-settingsources) in the Agent SDK, Claude Code ignores its `sandbox.filesystem` entries, its `Edit` permission rules, and its `Read` deny rules when building the sandbox configuration. Requires Claude Code v2.1.246 or later.
265265 
266When you edit these filesystem lists during a session, Claude Code [applies the change to the running session](/docs/en/settings.md#when-edits-take-effect), so the next sandboxed command runs under the new paths.
266When you edit these filesystem lists during a session, Claude Code [applies the change to the running session](https://code.claude.com/docs/en/settings.md#when-edits-take-effect), so the next sandboxed command runs under the new paths.
267267 
268Sandbox filesystem paths use standard conventions: `/tmp/build` is absolute and `~/.kube` is relative to your home directory. This differs from [Read and Edit permission rules](/docs/en/permissions.md#read-and-edit), which use `//path` for absolute and `/path` for project-relative. For relative paths, trailing slashes, and wildcards, see [Sandbox path prefixes](/docs/en/settings-reference.md#sandbox-path-prefixes).
268Sandbox filesystem paths use standard conventions: `/tmp/build` is absolute and `~/.kube` is relative to your home directory. This differs from [Read and Edit permission rules](https://code.claude.com/docs/en/permissions.md#read-and-edit), which use `//path` for absolute and `/path` for project-relative. For relative paths, trailing slashes, and wildcards, see [Sandbox path prefixes](https://code.claude.com/docs/en/settings-reference.md#sandbox-path-prefixes).
269269 
270270You can also deny write or read access using `sandbox.filesystem.denyWrite` and `sandbox.filesystem.denyRead`, and re-allow specific paths within a denied region using `sandbox.filesystem.allowRead`. When read rules overlap, the rule with the narrower path applies:
271271 
from line 273
273273| :- | :- |
274274| `"denyRead": ["~/"]` with `"allowRead": ["~/projects"]` | `~/projects` is readable and the rest of the home directory stays blocked. The narrower allow re-opens that part of the denied region |
275275| `"allowRead": ["~/"]` with `"denyRead": ["~/.env"]` | `~/.env` stays blocked and the rest of the home directory is readable. The deny holds inside a wider allow, so a broad allow can't silently re-expose a secret |
276| `"allowRead": ["~/"]` with `"denyRead": ["~/**/.env"]` | Every `.env` under the home directory stays blocked and the rest is readable. A [wildcard deny](/docs/en/settings-reference.md#sandbox-path-prefixes) holds inside a wider allow the same way an exact path does |
276| `"allowRead": ["~/"]` with `"denyRead": ["~/**/.env"]` | Every `.env` under the home directory stays blocked and the rest is readable. A [wildcard deny](https://code.claude.com/docs/en/settings-reference.md#sandbox-path-prefixes) holds inside a wider allow the same way an exact path does |
277277 
278278The example below blocks reading from the entire home directory while still allowing reads from the current project. Place it in your project's `.claude/settings.json`, because the relative path `.` resolves to the project root when the configuration lives in project settings:
279279 
from line 291
291291 
292292If you placed the same configuration in `~/.claude/settings.json`, `.` would resolve to `~/.claude` instead, and project files would remain blocked by the `denyRead` rule.
293293 
294To deny sandboxed commands read access to home directories and mounted volumes while keeping the working directories readable, set [`permissions.blockReadsOutsideWorkingDirectories`](/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories) instead of writing path rules.
294To deny sandboxed commands read access to home directories and mounted volumes while keeping the working directories readable, set [`permissions.blockReadsOutsideWorkingDirectories`](https://code.claude.com/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories) instead of writing path rules.
295295 
296296### Run commands outside the sandbox with `excludedCommands`
297297 
298List a command pattern in [`sandbox.excludedCommands`](/docs/en/settings-reference#sandbox-excludedcommands) to run matching commands outside the sandbox, which means no filesystem restrictions and no network proxy. Use it for a tool that can't work inside the sandbox and that you trust with your full access. A tool that needs one more directory or one more host may work with `allowWrite` or `allowedDomains`, which keep the command sandboxed.
298List a command pattern in [`sandbox.excludedCommands`](https://code.claude.com/docs/en/settings-reference#sandbox-excludedcommands) to run matching commands outside the sandbox, which means no filesystem restrictions and no network proxy. Use it for a tool that can't work inside the sandbox and that you trust with your full access. A tool that needs one more directory or one more host may work with `allowWrite` or `allowedDomains`, which keep the command sandboxed.
299299 
300300This example takes `docker compose` commands out of the sandbox. Save it in `~/.claude/settings.json` to apply it to all of your projects:
301301 
from line 310
310310 
311311Claude Code checks your entries against each Bash and Monitor call. A call is the whole command line Claude sends, which can chain several commands. The following rules decide whether a call leaves the sandbox:
312312 
313* **End the pattern with ` *`**: entries use the same syntax as a `Bash(...)` [permission rule](/docs/en/permissions.md#permission-rule-syntax), where a pattern with no wildcard is an exact match. `docker` matches only `docker` with no arguments. `docker *` matches `docker` with or without arguments
313* **End the pattern with ` *`**: entries use the same syntax as a `Bash(...)` [permission rule](https://code.claude.com/docs/en/permissions.md#permission-rule-syntax), where a pattern with no wildcard is an exact match. `docker` matches only `docker` with no arguments. `docker *` matches `docker` with or without arguments
314314* **Every command in the call has to match**: `npm ci && docker compose build` stays sandboxed unless another entry covers `npm ci`
315315* **Claude Code matches the text of the call**: a script or `make` target that calls `docker` internally doesn't match, and neither does `/usr/local/bin/docker`
316* **Some calls stay sandboxed**: a redirect to a file, a `cd`, or a command substitution such as `$(...)` keeps the whole call sandboxed. The [reference entry](/docs/en/settings-reference.md#sandbox-excludedcommands) lists more calls that stay sandboxed
316* **Some calls stay sandboxed**: a redirect to a file, a `cd`, or a command substitution such as `$(...)` keeps the whole call sandboxed. The [reference entry](https://code.claude.com/docs/en/settings-reference.md#sandbox-excludedcommands) lists more calls that stay sandboxed
317317* **Where you save the entry can matter**: while the sandbox is [admin-required](#repository-settings-under-an-admin-required-sandbox), Claude Code ignores entries in `.claude/settings.json` and `.claude/settings.local.json`
318318 
319319An excluded command goes through the regular permission flow:
320320 
321* [Read-only commands](/docs/en/permissions.md#read-only-commands) and commands your allow rules cover run without a prompt
321* [Read-only commands](https://code.claude.com/docs/en/permissions.md#read-only-commands) and commands your allow rules cover run without a prompt
322322* In auto mode, the classifier reviews other excluded commands
323323* In `bypassPermissions` mode, an excluded command runs without a prompt unless an ask rule matches it
324324 
from line 360
360360 
361361* User settings, managed settings, and the `--settings` CLI flag can set it. Project settings in `.claude/settings.json` and `.claude/settings.local.json` can't, so a checked-out project can't switch filesystem isolation off.
362362* When managed settings configure `sandbox.filesystem` at all, or list any `sandbox.credentials.files` entry with `"mode": "deny"`, only managed settings can set the key. This keeps developers from turning filesystem isolation off in such a deployment; to turn it off for everyone, set `"disabled": true` in managed settings.
363* When [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](/docs/en/env-vars) is set, Claude Code ignores `filesystem.disabled` from every source, including managed settings, and keeps filesystem isolation on.
363* When [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](https://code.claude.com/docs/en/env-vars) is set, Claude Code ignores `filesystem.disabled` from every source, including managed settings, and keeps filesystem isolation on.
364364 
365A [valid](/docs/en/settings-reference.md#invalid-credential-entries-in-managed-settings) `mask` entry doesn't lock the key, even when Claude Code [falls back to `deny`](#mask-credential-files) for it at startup. List a path that can't be masked, such as a credential directory, as an explicit `deny` entry in managed settings, which locks the key.
365A [valid](https://code.claude.com/docs/en/settings-reference.md#invalid-credential-entries-in-managed-settings) `mask` entry doesn't lock the key, even when Claude Code [falls back to `deny`](#mask-credential-files) for it at startup. List a path that can't be masked, such as a credential directory, as an explicit `deny` entry in managed settings, which locks the key.
366366 
367367#### What changes when filesystem isolation is off
368368 
from line 379
379379* Sandboxed commands inherit your shell's `$TMPDIR` instead of the per-user temp directory, because every temp directory is writable and Claude Code no longer redirects commands to the per-user one.
380380 
381381 On Linux the variable is often unset in the parent shell. The Bash tool guidance tells Claude to create scratch directories with `mktemp -d` instead of relying on `$TMPDIR`.
382* [`autoAllowBashIfSandboxed`](/docs/en/settings-reference#sandbox-autoallowbashifsandboxed) still defaults to `true`, so sandboxed commands keep running without prompts. Set it to `false` to prompt for sandboxed commands.
382* [`autoAllowBashIfSandboxed`](https://code.claude.com/docs/en/settings-reference#sandbox-autoallowbashifsandboxed) still defaults to `true`, so sandboxed commands keep running without prompts. Set it to `false` to prompt for sandboxed commands.
383383 
384384### Protect credentials
385385 
from line 409
409409 
410410Environment variable entries and file entries also accept `"mode": "mask"`, described under [Mask credentials](#mask-credentials).
411411 
412File paths follow the same [prefix rules](/docs/en/settings-reference.md#sandbox-path-prefixes) as `sandbox.filesystem.*` settings.
412File paths follow the same [prefix rules](https://code.claude.com/docs/en/settings-reference.md#sandbox-path-prefixes) as `sandbox.filesystem.*` settings.
413413 
414Claude Code merges the `deny` entries from every [settings scope](/docs/en/settings.md#settings-precedence) the session loads. A `deny` entry only ever narrows access, so any scope can add one, but no scope can remove one that another scope added.
414Claude Code merges the `deny` entries from every [settings scope](https://code.claude.com/docs/en/settings.md#settings-precedence) the session loads. A `deny` entry only ever narrows access, so any scope can add one, but no scope can remove one that another scope added.
415415 
416416When you [exclude a settings source](#configure-sandboxing):
417417 
from line 420
420420 
421421There is no built-in credential deny list, so only the files and variables you list are restricted.
422422 
423`sandbox.credentials` affects sandboxed Bash commands only. To strip credentials from all subprocesses regardless of sandboxing, set [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](/docs/en/env-vars).
423`sandbox.credentials` affects sandboxed Bash commands only. To strip credentials from all subprocesses regardless of sandboxing, set [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](https://code.claude.com/docs/en/env-vars).
424424 
425425### Mask credentials
426426 
427When you mask a credential, Claude Code shows sandboxed commands a per-session placeholder called the sentinel, and the [sandbox proxy](#network-isolation) substitutes the real value on outbound requests to hosts you allow. A `deny` entry under [Protect credentials](#protect-credentials) blocks the credential instead. For files on macOS, Claude Code [blocks the file instead](#mask-credential-files) of masking it. The [`sandbox.credentials`](/docs/en/settings-reference#sandbox-credentials) reference lists every field.
427When you mask a credential, Claude Code shows sandboxed commands a per-session placeholder called the sentinel, and the [sandbox proxy](#network-isolation) substitutes the real value on outbound requests to hosts you allow. A `deny` entry under [Protect credentials](#protect-credentials) blocks the credential instead. For files on macOS, Claude Code [blocks the file instead](#mask-credential-files) of masking it. The [`sandbox.credentials`](https://code.claude.com/docs/en/settings-reference#sandbox-credentials) reference lists every field.
428428 
429429Masking requires the following:
430430 
431* **TLS termination**: the proxy substitutes the real value inside request contents, so it has to see them. Set [`network.tlsTerminate`](/docs/en/settings-reference#sandbox-network-tlsterminate) so the proxy terminates TLS itself. Without it, masking fails without exposing anything: the command still sees only the sentinel, but the sentinel reaches the server unchanged and authentication fails. To check for this misconfiguration, run `claude doctor` in your terminal and look for the `TLS termination is unavailable` warning.
431* **TLS termination**: the proxy substitutes the real value inside request contents, so it has to see them. Set [`network.tlsTerminate`](https://code.claude.com/docs/en/settings-reference#sandbox-network-tlsterminate) so the proxy terminates TLS itself. Without it, masking fails without exposing anything: the command still sees only the sentinel, but the sentinel reaches the server unchanged and authentication fails. To check for this misconfiguration, run `claude doctor` in your terminal and look for the `TLS termination is unavailable` warning.
432432* **An allowed destination**: each `mask` entry can list `injectHosts`, the hosts the real value is allowed to reach. The proxy injects only on connections the [domain allowlist](#network-isolation) admits, so each `injectHosts` host must also be reachable through `network.allowedDomains`. For a `mask` entry with no `injectHosts`, the proxy substitutes the real value on requests to every host in `network.allowedDomains`.
433* **A trusted settings scope**: masking authorizes the proxy to send your real credential somewhere, so Claude Code honors `mask` entries, `network.tlsTerminate`, [`credentials.allowPlaintextInject`](/docs/en/settings-reference#sandbox-credentials-allowplaintextinject), `awsPairs`, and `sigv4` only from user settings, managed settings, and the `--settings` flag. It ignores them in a repository's `.claude/settings.json` or `.claude/settings.local.json`. When your administrator delivers `mask` entries, `network.tlsTerminate`, or `credentials.allowPlaintextInject` through server-managed settings, they count as [settings that need approval](/docs/en/server-managed-settings.md#security-approval-dialogs).
433* **A trusted settings scope**: masking authorizes the proxy to send your real credential somewhere, so Claude Code honors `mask` entries, `network.tlsTerminate`, [`credentials.allowPlaintextInject`](https://code.claude.com/docs/en/settings-reference#sandbox-credentials-allowplaintextinject), `awsPairs`, and `sigv4` only from user settings, managed settings, and the `--settings` flag. It ignores them in a repository's `.claude/settings.json` or `.claude/settings.local.json`. When your administrator delivers `mask` entries, `network.tlsTerminate`, or `credentials.allowPlaintextInject` through server-managed settings, they count as [settings that need approval](https://code.claude.com/docs/en/server-managed-settings.md#security-approval-dialogs).
434434 
435435#### Mask environment variables
436436 
from line 456
456456}
457457```
458458 
459Masking replaces the whole value by default. For a value with structure, such as a `DATABASE_URL` connection string or a JWT, use the [`extract`, `decode`, `maskClaims`, and `onExtractNoMatch` fields](/docs/en/settings-reference#sandbox-credentials-envvars) so tools that parse the value keep working.
459Masking replaces the whole value by default. For a value with structure, such as a `DATABASE_URL` connection string or a JWT, use the [`extract`, `decode`, `maskClaims`, and `onExtractNoMatch` fields](https://code.claude.com/docs/en/settings-reference#sandbox-credentials-envvars) so tools that parse the value keep working.
460460 
461461For an IPv6 destination, spell the address differently in the two lists:
462462 
from line 469
469469 
470470AWS requests carry SigV4 signatures over the request contents, so mask `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` together. The proxy detects a SigV4 request by the access key's [sentinel](#mask-credentials) and re-signs the request with the real values, which requires Claude Code v2.1.221 or later. If you mask only the secret, requests are signed with a placeholder the proxy can't detect, so they fail at AWS.
471471 
472Claude Code links the conventional `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` variables into one credential automatically when you mask their whole values. If your AWS credential is in variables with other names, group them with [`credentials.awsPairs`](/docs/en/settings-reference#sandbox-credentials-awspairs), which requires Claude Code v2.1.224 or later.
472Claude Code links the conventional `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` variables into one credential automatically when you mask their whole values. If your AWS credential is in variables with other names, group them with [`credentials.awsPairs`](https://code.claude.com/docs/en/settings-reference#sandbox-credentials-awspairs), which requires Claude Code v2.1.224 or later.
473473 
474Streaming uploads, presigned URLs, and SigV4A requests carry signatures the proxy can't recompute. When one of these requests is signed with a masked pair's placeholder, the proxy fails it rather than forward a broken signature. Requests signed with unmasked credentials aren't affected. Use [`credentials.sigv4`](/docs/en/settings-reference#sandbox-credentials-sigv4), which requires Claude Code v2.1.224 or later, to forward one of these request forms instead. AWS still rejects the request, so the calling tool receives AWS's own rejection response instead of a proxy error.
474Streaming uploads, presigned URLs, and SigV4A requests carry signatures the proxy can't recompute. When one of these requests is signed with a masked pair's placeholder, the proxy fails it rather than forward a broken signature. Requests signed with unmasked credentials aren't affected. Use [`credentials.sigv4`](https://code.claude.com/docs/en/settings-reference#sandbox-credentials-sigv4), which requires Claude Code v2.1.224 or later, to forward one of these request forms instead. AWS still rejects the request, so the calling tool receives AWS's own rejection response instead of a proxy error.
475475 
476476#### Mask credential files
477477 
from line 506
506506 
507507To confirm the mask is active, ask Claude to run `cat ~/.config/gh/hosts.yml` in a sandboxed command. On Linux and WSL2 the output shows a sentinel in place of the token, and on macOS the read fails.
508508 
509Without `extract` or `decode`, Claude Code replaces the entire file with one sentinel, which suits a file holding a single bare secret. Use the [`extract`, `decode`, `maskClaims`, `onExtractNoMatch`, and `maskDuplicates` fields](/docs/en/settings-reference#sandbox-credentials-files) to control partial masking and what happens when the pattern matches nothing.
509Without `extract` or `decode`, Claude Code replaces the entire file with one sentinel, which suits a file holding a single bare secret. Use the [`extract`, `decode`, `maskClaims`, `onExtractNoMatch`, and `maskDuplicates` fields](https://code.claude.com/docs/en/settings-reference#sandbox-credentials-files) to control partial masking and what happens when the pattern matches nothing.
510510 
511511> [!WARNING]
512512>
513> When matching finds nothing to mask, the default `onExtractNoMatch` value, `warn`, skips the entry, so sandboxed commands can read the real file unmasked. On macOS, Claude Code applies `mask` entries as `deny` before the pattern runs whenever filesystem isolation is on, so the no-match outcomes take effect there only when [filesystem isolation is off](#disable-filesystem-isolation). The default suits credentials that may be legitimately absent. If the secret might be present but the pattern might miss it, use [`deny`](/docs/en/settings-reference#mask-fields-for-files).
513> When matching finds nothing to mask, the default `onExtractNoMatch` value, `warn`, skips the entry, so sandboxed commands can read the real file unmasked. On macOS, Claude Code applies `mask` entries as `deny` before the pattern runs whenever filesystem isolation is on, so the no-match outcomes take effect there only when [filesystem isolation is off](#disable-filesystem-isolation). The default suits credentials that may be legitimately absent. If the secret might be present but the pattern might miss it, use [`deny`](https://code.claude.com/docs/en/settings-reference#mask-fields-for-files).
514514 
515515`mask` applies to a single file, so list each credential file individually. Claude Code falls back to `deny` for a `mask` entry it can't mask safely: a directory path, a glob pattern, a file larger than 8 MiB, or a file that isn't UTF-8 text.
516516 
from line 520
520520 
521521The sandboxed Bash tool restricts file system access to specific directories:
522522 
523* **Default write behavior**: read and write access to the current working directory and its subdirectories, any directories you've added with `--add-dir`, `/add-dir`, or [`permissions.additionalDirectories`](/docs/en/settings-reference#permissions-additionaldirectories), plus the per-user temp directory that `$TMPDIR` points to
523* **Default write behavior**: read and write access to the current working directory and its subdirectories, any directories you've added with `--add-dir`, `/add-dir`, or [`permissions.additionalDirectories`](https://code.claude.com/docs/en/settings-reference#permissions-additionaldirectories), plus the per-user temp directory that `$TMPDIR` points to
524524* **Default read behavior**: read access to the entire computer, except certain denied directories. This default still allows reading credential files, so [protect credentials](#protect-credentials) you don't want commands to read.
525* **Read block**: with [`permissions.blockReadsOutsideWorkingDirectories`](/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories) on, sandboxed commands also lose read access to your home directory and the other directories that hold user files, apart from the paths that [Sandboxed commands under the block](/docs/en/settings-reference.md#sandboxed-commands-under-the-block) lists. That section also says when this part of the block doesn't apply.
526* **Git worktrees**: when the working directory is a [linked git worktree](/docs/en/worktrees.md), the sandbox also allows writes to the main repository's shared `.git` directory so commands such as `git commit` can update refs and the index. Writes to `hooks/` and `config` inside that directory remain denied.
525* **Read block**: with [`permissions.blockReadsOutsideWorkingDirectories`](https://code.claude.com/docs/en/settings-reference#permissions-blockreadsoutsideworkingdirectories) on, sandboxed commands also lose read access to your home directory and the other directories that hold user files, apart from the paths that [Sandboxed commands under the block](https://code.claude.com/docs/en/settings-reference.md#sandboxed-commands-under-the-block) lists. That section also says when this part of the block doesn't apply.
526* **Git worktrees**: when the working directory is a [linked git worktree](https://code.claude.com/docs/en/worktrees.md), the sandbox also allows writes to the main repository's shared `.git` directory so commands such as `git commit` can update refs and the index. Writes to `hooks/` and `config` inside that directory remain denied.
527527 
528528To skip filesystem isolation entirely while keeping network isolation, set [`sandbox.filesystem.disabled`](#disable-filesystem-isolation).
529529 
530530### Protected paths
531531 
532Inside the directories that sandboxed commands can write to, the sandbox still denies writes to the files Claude Code loads configuration and code from. A command that could edit those files could grant itself permissions, or add a hook or MCP server that Claude Code runs outside the sandbox. The permission system has its own [protected paths](/docs/en/permission-modes.md#protected-paths), which control what Claude Code approves before a tool runs; the sandbox's list applies to a command that is already running. It covers four groups of paths:
532Inside the directories that sandboxed commands can write to, the sandbox still denies writes to the files Claude Code loads configuration and code from. A command that could edit those files could grant itself permissions, or add a hook or MCP server that Claude Code runs outside the sandbox. The permission system has its own [protected paths](https://code.claude.com/docs/en/permission-modes.md#protected-paths), which control what Claude Code approves before a tool runs; the sandbox's list applies to a command that is already running. It covers four groups of paths:
533533 
534534* **In your working directory and the directories above it**: the `.claude` settings files, the `.claude/skills`, `.claude/agents`, `.claude/commands`, and `.claude/hooks` directories, `.mcp.json`, and the files Claude Code runs on its own, such as `.claude/workflows` and `.claude/scheduled_tasks.json`
535535* **In your working directory only**: shell startup files such as `.bashrc` and `.zshrc`, `.gitconfig`, the `.vscode` and `.idea` directories, and `hooks` and `config` inside `.git`
from line 542
542542 
543543> [!WARNING]
544544>
545> If you set [`allowNestedUserNamespaces`](/docs/en/settings-reference#sandbox-allownestedusernamespaces) to `true` on Linux or WSL2, don't rely on protected paths: code from a sandboxed command can end up running outside the sandbox.
545> If you set [`allowNestedUserNamespaces`](https://code.claude.com/docs/en/settings-reference#sandbox-allownestedusernamespaces) to `true` on Linux or WSL2, don't rely on protected paths: code from a sandboxed command can end up running outside the sandbox.
546546 
547547To see most of these paths resolved for your machine, run `/sandbox` and open the **Config** tab, which lists them under **Denied within allowed**, mixed in with your own `denyWrite` entries.
548548 
from line 570
570570The following settings and behaviors control which hosts the proxy allows:
571571 
572572* **Domain restrictions**: your allowed domains start empty. [Hosts outside your allowed domains](#hosts-outside-your-allowed-domains) covers what happens the first time a command needs a new domain.
573* **Approval choices**: if you choose Yes when prompted, Claude Code allows the host for the rest of the current session. If you choose "Yes, and don't ask again", Claude Code saves a `WebFetch(domain:...)` allow rule to your [local settings](/docs/en/permissions.md#permission-system), so the host stays allowed in future sessions. While the sandbox is [admin-required](#repository-settings-under-an-admin-required-sandbox), Claude Code saves the rule to your user settings, where it applies in every project.
574* **Pre-allowed domains**: pre-allow domains with [`allowedDomains`](/docs/en/settings-reference#sandbox-network-alloweddomains) to avoid the prompt entirely. Claude Code also pre-allows domains from `WebFetch(domain:...)` allow rules, as described in [Permission rules](#permission-rules).
575* **Strict allowlist**: if you set [`strictAllowlist`](/docs/en/settings-reference#sandbox-network-strictallowlist) to `true` in user, managed, or CLI `--settings` settings, Claude Code denies sandboxed commands access to any host outside the allowlist instead of prompting. The allowlist is `allowedDomains` plus domains from `WebFetch(domain:...)` allow rules, or only the managed settings entries when `allowManagedDomainsOnly` is set. [Locks that apply without an admin-required sandbox](#locks-that-apply-without-an-admin-required-sandbox) covers a repository's entries. Claude Code enforces this for sandboxed commands only; in-process tools such as `WebFetch` still follow their [permission rules](#permission-rules). Setting it in a repository's `.claude/settings.json` or `.claude/settings.local.json` has no effect. Requires Claude Code v2.1.219 or later.
576* **Managed lockdown**: if [`allowManagedDomainsOnly`](/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly) is set in managed settings, non-allowed domains are blocked automatically instead of prompting, and only `allowedDomains` and `WebFetch(domain:...)` allow rules from managed settings are honored.
577* **Corporate proxy**: when your network requires outbound traffic to go through a corporate proxy, set `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY` as [proxy configuration](/docs/en/network-config.md#proxy-configuration) describes, in the `env` block of your settings so that [background agents](/docs/en/network-config.md#set-network-variables-in-settings-not-the-shell) get them too, or in the environment you launch Claude Code from. Claude Code enforces the domain allowlist and then tunnels allowed connections through that upstream proxy. `http://` and `https://` proxy URLs work, with basic authentication in the URL if you need it.
573* **Approval choices**: if you choose Yes when prompted, Claude Code allows the host for the rest of the current session. If you choose "Yes, and don't ask again", Claude Code saves a `WebFetch(domain:...)` allow rule to your [local settings](https://code.claude.com/docs/en/permissions.md#permission-system), so the host stays allowed in future sessions. While the sandbox is [admin-required](#repository-settings-under-an-admin-required-sandbox), Claude Code saves the rule to your user settings, where it applies in every project.
574* **Pre-allowed domains**: pre-allow domains with [`allowedDomains`](https://code.claude.com/docs/en/settings-reference#sandbox-network-alloweddomains) to avoid the prompt entirely. Claude Code also pre-allows domains from `WebFetch(domain:...)` allow rules, as described in [Permission rules](#permission-rules).
575* **Strict allowlist**: if you set [`strictAllowlist`](https://code.claude.com/docs/en/settings-reference#sandbox-network-strictallowlist) to `true` in user, managed, or CLI `--settings` settings, Claude Code denies sandboxed commands access to any host outside the allowlist instead of prompting. The allowlist is `allowedDomains` plus domains from `WebFetch(domain:...)` allow rules, or only the managed settings entries when `allowManagedDomainsOnly` is set. [Locks that apply without an admin-required sandbox](#locks-that-apply-without-an-admin-required-sandbox) covers a repository's entries. Claude Code enforces this for sandboxed commands only; in-process tools such as `WebFetch` still follow their [permission rules](#permission-rules). Setting it in a repository's `.claude/settings.json` or `.claude/settings.local.json` has no effect. Requires Claude Code v2.1.219 or later.
576* **Managed lockdown**: if [`allowManagedDomainsOnly`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly) is set in managed settings, non-allowed domains are blocked automatically instead of prompting, and only `allowedDomains` and `WebFetch(domain:...)` allow rules from managed settings are honored.
577* **Corporate proxy**: when your network requires outbound traffic to go through a corporate proxy, set `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY` as [proxy configuration](https://code.claude.com/docs/en/network-config.md#proxy-configuration) describes, in the `env` block of your settings so that [background agents](https://code.claude.com/docs/en/network-config.md#set-network-variables-in-settings-not-the-shell) get them too, or in the environment you launch Claude Code from. Claude Code enforces the domain allowlist and then tunnels allowed connections through that upstream proxy. `http://` and `https://` proxy URLs work, with basic authentication in the URL if you need it.
578578 
579579In a `WebFetch(domain:...)` rule, the sandbox honors two wildcard forms: a leading `*.`, such as `*.example.com`, and a bare `*`. The bare `*` form requires Claude Code v2.1.186 or later. A wildcard in any other position, such as `WebFetch(domain:example.*)`, still matches fetches but has no effect on sandboxed commands.
580580 
581581> [!NOTE]
582582>
583> The built-in proxy enforces the allowlist based on the requested hostname and, by default, does not terminate or inspect TLS traffic. The experimental [`network.tlsTerminate`](/docs/en/settings-reference#sandbox-network-tlsterminate) setting makes the built-in proxy terminate TLS itself, which [`mask` credential entries](#mask-credentials) require. See [Security limitations](#security-limitations) for the implications of the default, and [Custom proxy configuration](#custom-proxy-configuration) if your threat model requires TLS inspection.
583> The built-in proxy enforces the allowlist based on the requested hostname and, by default, does not terminate or inspect TLS traffic. The experimental [`network.tlsTerminate`](https://code.claude.com/docs/en/settings-reference#sandbox-network-tlsterminate) setting makes the built-in proxy terminate TLS itself, which [`mask` credential entries](#mask-credentials) require. See [Security limitations](#security-limitations) for the implications of the default, and [Custom proxy configuration](#custom-proxy-configuration) if your threat model requires TLS inspection.
584584 
585585#### Hosts outside your allowed domains
586586 
from line 588
588588 
589589| Permission mode | What happens to the connection |
590590| :- | :- |
591| `bypassPermissions` mode, and plan mode with [bypass permissions available](/docs/en/permission-modes.md#skip-all-checks-with-bypasspermissions-mode) | Allowed without a prompt |
591| `bypassPermissions` mode, and plan mode with [bypass permissions available](https://code.claude.com/docs/en/permission-modes.md#skip-all-checks-with-bypasspermissions-mode) | Allowed without a prompt |
592592| Manual mode, `acceptEdits` mode, and plan mode otherwise | You get a prompt |
593593| Auto mode | Refused unless the command [listed the host](#per-command-allowed-domains-in-auto-mode) and the classifier approved the list |
594594| `dontAsk` mode | Refused |
595595 
596With [`strictAllowlist`](/docs/en/settings-reference#sandbox-network-strictallowlist) or [`allowManagedDomainsOnly`](/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly) on, the built-in sandbox proxy refuses the connection in every permission mode. In `bypassPermissions` mode, hosts outside your allowed domains are allowed unless one of them is on. [The unsandboxed retry escape hatch](#the-unsandboxed-retry-escape-hatch) covers when a command can leave the sandbox in that mode. A connection to a host in [`deniedDomains`](/docs/en/settings-reference#sandbox-network-denieddomains) is refused in every permission mode too.
596With [`strictAllowlist`](https://code.claude.com/docs/en/settings-reference#sandbox-network-strictallowlist) or [`allowManagedDomainsOnly`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly) on, the built-in sandbox proxy refuses the connection in every permission mode. In `bypassPermissions` mode, hosts outside your allowed domains are allowed unless one of them is on. [The unsandboxed retry escape hatch](#the-unsandboxed-retry-escape-hatch) covers when a command can leave the sandbox in that mode. A connection to a host in [`deniedDomains`](https://code.claude.com/docs/en/settings-reference#sandbox-network-denieddomains) is refused in every permission mode too.
597597 
598598#### Hostnames that resolve to local addresses
599599 
from line 605
605605 
606606#### Per-command allowed domains in auto mode
607607 
608In [auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) with sandboxing on, Claude names the hosts a command needs on the command itself instead of triggering a network approval for each connection. Each Bash, PowerShell, or [Monitor](/docs/en/tools-reference.md#monitor-tool) command that runs in the sandbox can carry a list of hosts beyond the sandbox's allowlist: a domain such as `registry.npmjs.org`, a wildcard such as `*.pythonhosted.org`, or an IP address, each with an optional `:port`. The classifier reviews the hosts together with the command. Requires Claude Code v2.1.271 or later.
608In [auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) with sandboxing on, Claude names the hosts a command needs on the command itself instead of triggering a network approval for each connection. Each Bash, PowerShell, or [Monitor](https://code.claude.com/docs/en/tools-reference.md#monitor-tool) command that runs in the sandbox can carry a list of hosts beyond the sandbox's allowlist: a domain such as `registry.npmjs.org`, a wildcard such as `*.pythonhosted.org`, or an IP address, each with an optional `:port`. The classifier reviews the hosts together with the command. Requires Claude Code v2.1.271 or later.
609609 
610610An approved list opens those hosts for that one command alone, for as long as it runs. Nothing is added to your session's allowed hosts or your settings; the next command names its own hosts.
611611 
612A command that carries hosts goes to the classifier instead of being approved by a permission rule or the sandbox's [auto-allow mode](#sandbox-modes). If an [ask rule](/docs/en/permissions.md#manage-permissions) forces a prompt for the command, the permission dialog in your terminal lists the hosts beside it, and approving there covers both.
612A command that carries hosts goes to the classifier instead of being approved by a permission rule or the sandbox's [auto-allow mode](#sandbox-modes). If an [ask rule](https://code.claude.com/docs/en/permissions.md#manage-permissions) forces a prompt for the command, the permission dialog in your terminal lists the hosts beside it, and approving there covers both.
613613 
614A per-command list widens only what the sandbox denies by default. [`deniedDomains`](/docs/en/settings-reference#sandbox-network-denieddomains) entries still block. When [`strictAllowlist`](/docs/en/settings-reference#sandbox-network-strictallowlist) or [`allowManagedDomainsOnly`](/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly) locks the allowlist, Claude Code refuses per-command lists.
614A per-command list widens only what the sandbox denies by default. [`deniedDomains`](https://code.claude.com/docs/en/settings-reference#sandbox-network-denieddomains) entries still block. When [`strictAllowlist`](https://code.claude.com/docs/en/settings-reference#sandbox-network-strictallowlist) or [`allowManagedDomainsOnly`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly) locks the allowlist, Claude Code refuses per-command lists.
615615 
616616While per-command lists apply, Claude Code refuses a connection to a host that no approved command listed, without a prompt or a classifier check. The refusal names the host in the command's result, and Claude re-runs the command with the host added.
617617 
from line 634
634634* **Linux**: uses [bubblewrap](https://github.com/containers/bubblewrap) for isolation
635635* **WSL2**: uses bubblewrap, same as Linux
636636 
637You can also run the [`@anthropic-ai/sandbox-runtime`](https://github.com/anthropics/sandbox-runtime) package on its own to wrap the Claude Code process. See [Sandbox runtime](/docs/en/sandbox-environments.md#sandbox-runtime).
637You can also run the [`@anthropic-ai/sandbox-runtime`](https://github.com/anthropics/sandbox-runtime) package on its own to wrap the Claude Code process. See [Sandbox runtime](https://code.claude.com/docs/en/sandbox-environments.md#sandbox-runtime).
638638 
639639## How sandboxing relates to permissions and permission modes
640640 
641Sandboxing, [permission rules](/docs/en/permissions.md), and [permission modes](/docs/en/permission-modes.md) are complementary layers. The sections below cover how the sandbox interacts with each.
641Sandboxing, [permission rules](https://code.claude.com/docs/en/permissions.md), and [permission modes](https://code.claude.com/docs/en/permission-modes.md) are complementary layers. The sections below cover how the sandbox interacts with each.
642642 
643643### Permission rules
644644 
645645Permission rules and sandboxing control different things:
646646 
647* **Permission rules** control which tools Claude Code can use and are evaluated before any tool runs. They apply to every tool: Bash, Read, Edit, WebFetch, MCP, and others, except that a deny or ask rule can't block [`EndConversation`](/docs/en/tools-reference#endconversation-tool-behavior) while any other tool remains.
648* **Sandboxing** provides OS-level enforcement that restricts what shell commands can access at the filesystem and network level. It applies to Bash, PowerShell, and [Monitor](/docs/en/tools-reference.md#monitor-tool) tool commands and their child processes.
647* **Permission rules** control which tools Claude Code can use and are evaluated before any tool runs. They apply to every tool: Bash, Read, Edit, WebFetch, MCP, and others, except that a deny or ask rule can't block [`EndConversation`](https://code.claude.com/docs/en/tools-reference#endconversation-tool-behavior) while any other tool remains.
648* **Sandboxing** provides OS-level enforcement that restricts what shell commands can access at the filesystem and network level. It applies to Bash, PowerShell, and [Monitor](https://code.claude.com/docs/en/tools-reference.md#monitor-tool) tool commands and their child processes.
649649 
650650The two layers also differ in how they are enforced. Claude Code evaluates permission decisions before a command runs, based on the command string and, in auto mode, a separate classifier's judgment about whether the command is safe. The operating system enforces the sandbox boundary on the running process, so it holds regardless of what the model chose to run and even if an allowed command does more than its name suggests.
651651 
from line 669
669669 
670670### Permission modes
671671 
672`/sandbox` is not a [permission mode](/docs/en/permission-modes.md). Permission modes decide whether a tool call runs and whether you are prompted first, while the sandbox restricts what a Bash command can access once it runs. They differ in what they control and what replaces the per-action prompt:
672`/sandbox` is not a [permission mode](https://code.claude.com/docs/en/permission-modes.md). Permission modes decide whether a tool call runs and whether you are prompted first, while the sandbox restricts what a Bash command can access once it runs. They differ in what they control and what replaces the per-action prompt:
673673 
674674| | What it controls | What replaces the prompt |
675675| :- | :- | :- |
676676| `/sandbox` | What a Bash command can access once it runs | The sandbox boundary itself, in [auto-allow mode](#sandbox-modes) |
677| [Auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) | Whether each tool call runs | A classifier that reviews actions |
678| `--dangerously-skip-permissions` | Whether each tool call runs | Nothing. [Protected path](/docs/en/permission-modes.md#protected-paths) checks are also skipped; the [actions no mode auto-approves](/docs/en/permission-modes.md#actions-no-mode-auto-approves) still apply |
677| [Auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) | Whether each tool call runs | A classifier that reviews actions |
678| `--dangerously-skip-permissions` | Whether each tool call runs | Nothing. [Protected path](https://code.claude.com/docs/en/permission-modes.md#protected-paths) checks are also skipped; the [actions no mode auto-approves](https://code.claude.com/docs/en/permission-modes.md#actions-no-mode-auto-approves) still apply |
679679 
680The sandbox's [auto-allow mode](#sandbox-modes) is separate from [auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode): auto-allow approves Bash commands because the sandbox boundary contains them, while auto mode uses a classifier to review actions. The two work independently and can be combined, with the exceptions listed under [Sandbox modes](#sandbox-modes). To choose an isolation boundary for unattended runs, see [Sandbox environments](/docs/en/sandbox-environments.md#how-isolation-relates-to-permission-modes). For a table of common permission mode and sandbox pairings with the flags that start each one, see [Common setups](/docs/en/permission-modes.md#common-setups).
680The sandbox's [auto-allow mode](#sandbox-modes) is separate from [auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode): auto-allow approves Bash commands because the sandbox boundary contains them, while auto mode uses a classifier to review actions. The two work independently and can be combined, with the exceptions listed under [Sandbox modes](#sandbox-modes). To choose an isolation boundary for unattended runs, see [Sandbox environments](https://code.claude.com/docs/en/sandbox-environments.md#how-isolation-relates-to-permission-modes). For a table of common permission mode and sandbox pairings with the flags that start each one, see [Common setups](https://code.claude.com/docs/en/permission-modes.md#common-setups).
681681 
682682## Configure the sandbox for your organization
683683 
from line 685
685685 
686686### Enforce sandboxing with managed settings
687687 
688To require the sandbox for every developer, deliver the `sandbox` keys through [managed settings](/docs/en/managed-settings.md#delivery-mechanisms), either as a file managed by your MDM or through [server-managed settings](/docs/en/server-managed-settings.md) on claude.ai.
688To require the sandbox for every developer, deliver the `sandbox` keys through [managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms), either as a file managed by your MDM or through [server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md) on claude.ai.
689689 
690690The following managed settings configuration enables the sandbox, refuses to start Claude Code when the platform is unsupported or a dependency is missing, and prevents the model from retrying commands outside the sandbox:
691691 
from line 709
709709* Add `excludedCommands` for any organization-approved tools that must run without isolation, because this configuration [stops a repository's settings from taking commands out of the sandbox](#repository-settings-under-an-admin-required-sandbox)
710710* Add [`sandbox.credentials`](#protect-credentials) entries for credential directories such as `~/.aws` and `~/.ssh` and for secret environment variables, since the default read policy still allows them
711711 
712This configuration sandboxes the commands Claude runs. A developer can still type a command at the [`!` shell-mode prompt](/docs/en/interactive-mode#shell-mode-with-prefix) and run it outside the sandbox, with the same access they already have in any terminal outside Claude Code. See [strict sandbox mode](#turn-off-the-retry-with-strict-sandbox-mode) for the sessions where typed commands run sandboxed.
712This configuration sandboxes the commands Claude runs. A developer can still type a command at the [`!` shell-mode prompt](https://code.claude.com/docs/en/interactive-mode#shell-mode-with-prefix) and run it outside the sandbox, with the same access they already have in any terminal outside Claude Code. See [strict sandbox mode](#turn-off-the-retry-with-strict-sandbox-mode) for the sessions where typed commands run sandboxed.
713713 
714714The sandbox doesn't run on native Windows, so with `failIfUnavailable` set, Claude Code exits at startup on those machines. If your fleet includes Windows hosts, you can:
715715 
716* **Deliver the configuration by operating system**: deploy it through your MDM or as a [managed settings file](/docs/en/managed-settings.md#delivery-mechanisms) on macOS and Linux machines only. [Server-managed settings](/docs/en/server-managed-settings.md#current-limitations) apply to all users in the organization
716* **Deliver the configuration by operating system**: deploy it through your MDM or as a [managed settings file](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) on macOS and Linux machines only. [Server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md#current-limitations) apply to all users in the organization
717717* **Move Windows users into a supported environment**: have them run Claude Code inside WSL2 or a container
718718 
719719### Keep developers from widening the policy
from line 722
722722 
723723Unless managed settings set them, a developer's user settings or `--settings` can turn on the following keys. A repository's `.claude/settings.json` can too, unless the sandbox is [admin-required](#repository-settings-under-an-admin-required-sandbox). Each one weakens the sandbox, so set it to `false` in managed settings if you don't want it used:
724724 
725* [`enableWeakerNestedSandbox`](/docs/en/settings-reference#sandbox-enableweakernestedsandbox)
726* [`enableWeakerNetworkIsolation`](/docs/en/settings-reference#sandbox-enableweakernetworkisolation)
727* [`network.allowAllUnixSockets`](/docs/en/settings-reference#sandbox-network-allowallunixsockets)
728* [`network.allowLocalBinding`](/docs/en/settings-reference#sandbox-network-allowlocalbinding)
729* [`allowAppleEvents`](/docs/en/settings-reference#sandbox-allowappleevents), which a repository can't turn on
730* [`allowNestedUserNamespaces`](/docs/en/settings-reference#sandbox-allownestedusernamespaces), which a repository can't turn on
725* [`enableWeakerNestedSandbox`](https://code.claude.com/docs/en/settings-reference#sandbox-enableweakernestedsandbox)
726* [`enableWeakerNetworkIsolation`](https://code.claude.com/docs/en/settings-reference#sandbox-enableweakernetworkisolation)
727* [`network.allowAllUnixSockets`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowallunixsockets)
728* [`network.allowLocalBinding`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowlocalbinding)
729* [`allowAppleEvents`](https://code.claude.com/docs/en/settings-reference#sandbox-allowappleevents), which a repository can't turn on
730* [`allowNestedUserNamespaces`](https://code.claude.com/docs/en/settings-reference#sandbox-allownestedusernamespaces), which a repository can't turn on
731731 
732Set `allowManagedReadPathsOnly` to `true` in managed settings so that only `allowRead` entries from managed settings are honored. This prevents developers from widening read access beyond the organization-approved paths. To lock network domains to the managed values the same way, set [`allowManagedDomainsOnly`](/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly). With `allowManagedDomainsOnly` on, only managed settings can set a [proxy port](#custom-proxy-configuration).
732Set `allowManagedReadPathsOnly` to `true` in managed settings so that only `allowRead` entries from managed settings are honored. This prevents developers from widening read access beyond the organization-approved paths. To lock network domains to the managed values the same way, set [`allowManagedDomainsOnly`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly). With `allowManagedDomainsOnly` on, only managed settings can set a [proxy port](#custom-proxy-configuration).
733733 
734When managed settings configure `sandbox.filesystem` or list any `sandbox.credentials.files` entry with `"mode": "deny"`, only managed settings can set [`filesystem.disabled`](#disable-filesystem-isolation), so developers can't turn filesystem isolation off with that key. A [valid](/docs/en/settings-reference.md#invalid-credential-entries-in-managed-settings) `mask` entry doesn't lock the key. See [Which settings can disable it](#which-settings-can-disable-it).
734When managed settings configure `sandbox.filesystem` or list any `sandbox.credentials.files` entry with `"mode": "deny"`, only managed settings can set [`filesystem.disabled`](#disable-filesystem-isolation), so developers can't turn filesystem isolation off with that key. A [valid](https://code.claude.com/docs/en/settings-reference.md#invalid-credential-entries-in-managed-settings) `mask` entry doesn't lock the key. See [Which settings can disable it](#which-settings-can-disable-it).
735735 
736736#### Repository settings under an admin-required sandbox
737737 
738738The sandbox is admin-required while one of these settings is in effect:
739739 
740* [`allowUnsandboxedCommands`](/docs/en/settings-reference#sandbox-allowunsandboxedcommands) set to `false` in managed settings, or with the `--settings` flag unless managed settings set it to `true`
741* [`allowManagedDomainsOnly`](/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly) set to `true` in managed settings
740* [`allowUnsandboxedCommands`](https://code.claude.com/docs/en/settings-reference#sandbox-allowunsandboxedcommands) set to `false` in managed settings, or with the `--settings` flag unless managed settings set it to `true`
741* [`allowManagedDomainsOnly`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowmanageddomainsonly) set to `true` in managed settings
742742 
743743These settings don't turn the sandbox on, so set `enabled` as well.
744744 
from line 780
780780 
781781To route sandbox traffic through a corporate proxy elsewhere on your network, set `HTTPS_PROXY` instead, as the **Corporate proxy** entry under [Network isolation](#network-isolation) describes. That way, Claude Code's allowlist still applies.
782782 
783To direct sandboxed commands to your proxy, set the localhost ports it listens on in [sandbox settings](/docs/en/settings-reference.md#sandbox-settings):
783To direct sandboxed commands to your proxy, set the localhost ports it listens on in [sandbox settings](https://code.claude.com/docs/en/settings-reference.md#sandbox-settings):
784784 
785785```json theme={null}
786786{
from line 817
817817 
818818### Commands fail with a host-not-allowed error
819819 
820Many CLI tools need to reach specific hosts. Approve the host when prompted, or add it to [`allowedDomains`](/docs/en/settings-reference#sandbox-network-alloweddomains). If your organization locks the allowlist with `allowManagedDomainsOnly`, there's no prompt, so ask your administrator to add the host.
820Many CLI tools need to reach specific hosts. Approve the host when prompted, or add it to [`allowedDomains`](https://code.claude.com/docs/en/settings-reference#sandbox-network-alloweddomains). If your organization locks the allowlist with `allowManagedDomainsOnly`, there's no prompt, so ask your administrator to add the host.
821821 
822822### `jest` hangs or fails
823823 
from line 825
825825 
826826### Go-based CLIs fail TLS verification on macOS
827827 
828Tools such as `gh`, `gcloud`, and `terraform` may fail TLS verification under [Seatbelt](#os-level-enforcement). To run these tools outside the sandbox, add a pattern for each tool, such as `gh *`, to [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands). The tool then runs with your full access and its stored credentials. If you are using `httpProxyPort` with a MITM proxy and custom CA, set [`enableWeakerNetworkIsolation`](/docs/en/settings-reference#sandbox-enableweakernetworkisolation) to `true` instead.
828Tools such as `gh`, `gcloud`, and `terraform` may fail TLS verification under [Seatbelt](#os-level-enforcement). To run these tools outside the sandbox, add a pattern for each tool, such as `gh *`, to [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands). The tool then runs with your full access and its stored credentials. If you are using `httpProxyPort` with a MITM proxy and custom CA, set [`enableWeakerNetworkIsolation`](https://code.claude.com/docs/en/settings-reference#sandbox-enableweakernetworkisolation) to `true` instead.
829829 
830830### `open`, `osascript`, or browser-based auth flows fail with error `-600` on macOS
831831 
832The sandbox blocks Apple Events by default. Set [`allowAppleEvents`](/docs/en/settings-reference#sandbox-allowappleevents) to `true` in your user, managed, or CLI settings to allow them. Claude Code ignores this key in project settings.
832The sandbox blocks Apple Events by default. Set [`allowAppleEvents`](https://code.claude.com/docs/en/settings-reference#sandbox-allowappleevents) to `true` in your user, managed, or CLI settings to allow them. Claude Code ignores this key in project settings.
833833 
834834Enabling `allowAppleEvents` removes code-execution isolation, since sandboxed commands can then launch other applications unsandboxed with no user prompt, and can send AppleScript commands to running applications, subject to the macOS automation-consent prompt (TCC). Alternatively, add a pattern such as `open *` to [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands). Each `open` call then goes through the permission flow, and `open` can launch any file or app, including one Claude wrote.
835835 
from line 842
842842On Linux and WSL2, commands in the sandbox can't create user namespaces by default, so tools that rely on them fail:
843843 
844844* Chromium exits with `No usable sandbox!`
845* `unshare -U true` prints `unshare: unshare failed: Operation not permitted`. With [`allowAllUnixSockets`](/docs/en/settings-reference#sandbox-network-allowallunixsockets) on, it can print `No space left on device` instead
845* `unshare -U true` prints `unshare: unshare failed: Operation not permitted`. With [`allowAllUnixSockets`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowallunixsockets) on, it can print `No space left on device` instead
846846* Rootless Podman prints `cannot clone: Operation not permitted`, or stops earlier with `read-only file system` on one of its own folders
847847 
848In a session with [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](/docs/en/env-vars#variables) set to `1`, start Chromium or Electron with `--no-sandbox`, and run any other tool yourself outside Claude Code. In that session, [every command runs sandboxed](#turn-off-the-retry-with-strict-sandbox-mode), and the settings that the other fixes below name have no effect.
848In a session with [`CLAUDE_CODE_SUBPROCESS_ENV_SCRUB`](https://code.claude.com/docs/en/env-vars#variables) set to `1`, start Chromium or Electron with `--no-sandbox`, and run any other tool yourself outside Claude Code. In that session, [every command runs sandboxed](#turn-off-the-retry-with-strict-sandbox-mode), and the settings that the other fixes below name have no effect.
849849 
850850Each fix works for the tools it names and gives up some isolation:
851851 
852852* For Chromium or Electron, start the browser with `--no-sandbox`. The Claude Code sandbox stays as it is, but the browser runs web content without its own sandbox.
853853* For rootless Podman, Buildah, or a script or test command that runs a browser or `unshare`, add a pattern to [`sandbox.excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands), such as `podman *`. Commands that match run outside the sandbox with your full access, and Claude can edit a script in your working directory before running it. The pattern has to match the command Claude runs, so for a browser, match the test command rather than the browser.
854* To keep Chromium's or Electron's own sandbox, or to run a nested bubblewrap or `unshare`, set [`sandbox.allowNestedUserNamespaces`](/docs/en/settings-reference#sandbox-allownestedusernamespaces) to `true` in `~/.claude/settings.json`, not the project's `.claude/settings.json`. Requires Claude Code v2.1.305 or later. A command that begins with `unshare` stays sandboxed whatever `excludedCommands` pattern you list, because `unshare` can start any other program. This setting alone isn't enough for rootless Podman or Buildah, which the sandbox stops in other ways too.
854* To keep Chromium's or Electron's own sandbox, or to run a nested bubblewrap or `unshare`, set [`sandbox.allowNestedUserNamespaces`](https://code.claude.com/docs/en/settings-reference#sandbox-allownestedusernamespaces) to `true` in `~/.claude/settings.json`, not the project's `.claude/settings.json`. Requires Claude Code v2.1.305 or later. A command that begins with `unshare` stays sandboxed whatever `excludedCommands` pattern you list, because `unshare` can start any other program. This setting alone isn't enough for rootless Podman or Buildah, which the sandbox stops in other ways too.
855855 
856856> [!WARNING]
857857>
858858> If you set `allowNestedUserNamespaces` to `true`, you turn off a security boundary: code from a sandboxed command can end up running outside the sandbox.
859859>
860> Don't rely on any of the sandbox's restrictions, including [protected paths](#protected-paths) and [`credentials.files`](/docs/en/settings-reference#sandbox-credentials-files) entries, to stop a malicious command. In [auto-allow mode](#auto-allow-mode), that command can run without a permission prompt. If your organization has a security team, check with them before you turn this setting on.
860> Don't rely on any of the sandbox's restrictions, including [protected paths](#protected-paths) and [`credentials.files`](https://code.claude.com/docs/en/settings-reference#sandbox-credentials-files) entries, to stop a malicious command. In [auto-allow mode](#auto-allow-mode), that command can run without a permission prompt. If your organization has a security team, check with them before you turn this setting on.
861861 
862862### `pbcopy`, `xclip`, or `wl-copy` doesn't update the clipboard
863863 
864864The `pbcopy`, `xclip`, and `wl-copy` clipboard utilities can fail to reach the system clipboard from inside the sandbox, in which case the text piped to them doesn't arrive.
865865 
866To put Claude's output on your clipboard, ask Claude to print it in its response, then run [`/copy`](/docs/en/commands). `/copy` writes to the clipboard from the Claude Code process rather than from a sandboxed command.
866To put Claude's output on your clipboard, ask Claude to print it in its response, then run [`/copy`](https://code.claude.com/docs/en/commands). `/copy` writes to the clipboard from the Claude Code process rather than from a sandboxed command.
867867 
868When Claude pipes text to one of these tools, adding the tool to [`excludedCommands`](/docs/en/settings-reference#sandbox-excludedcommands) doesn't take that call out of the sandbox on its own.
868When Claude pipes text to one of these tools, adding the tool to [`excludedCommands`](https://code.claude.com/docs/en/settings-reference#sandbox-excludedcommands) doesn't take that call out of the sandbox on its own.
869869 
870870### A git command fails with `unable to unlink old`
871871 
from line 879
879879 
880880### Bubblewrap fails to start inside a container
881881 
882In an unprivileged container, [bubblewrap](#os-level-enforcement) can't mount a fresh `/proc` filesystem, so sandboxed commands fail with a `bwrap` error such as `Can't mount proc on /newroot/proc: Operation not permitted`. Set [`enableWeakerNestedSandbox`](/docs/en/settings-reference#sandbox-enableweakernestedsandbox) to `true` so the sandbox bind-mounts the container's existing `/proc` instead. Only use this setting when the outer container already provides the isolation boundary you need, since the setting exposes process information to sandboxed commands that a fresh `/proc` mount would hide.
882In an unprivileged container, [bubblewrap](#os-level-enforcement) can't mount a fresh `/proc` filesystem, so sandboxed commands fail with a `bwrap` error such as `Can't mount proc on /newroot/proc: Operation not permitted`. Set [`enableWeakerNestedSandbox`](https://code.claude.com/docs/en/settings-reference#sandbox-enableweakernestedsandbox) to `true` so the sandbox bind-mounts the container's existing `/proc` instead. Only use this setting when the outer container already provides the isolation boundary you need, since the setting exposes process information to sandboxed commands that a fresh `/proc` mount would hide.
883883 
884884### 0-byte read-only files appear at `.claude` settings paths, and "Yes, and don't ask again" doesn't save
885885 
886886On Linux and WSL2, the sandbox holds a write denial on a file that doesn't exist yet by creating a 0-byte read-only placeholder there while a sandboxed command runs. The sandbox removes the placeholder afterward. If a session is killed before that cleanup runs, for example by SIGKILL, the placeholders stay behind. Later sessions bind the placeholders read-only again on every start, so a settings write such as saving a permission choice fails at a path where a placeholder remains.
887887 
888Run `claude doctor` in your terminal to list the leftover placeholder files. The [`Sandbox mask files left behind at write-deny paths`](/docs/en/errors#stale-sandbox-mask-files-left-by-a-killed-session) warning names some of them and counts the rest. Delete each file with `rm` while no other Claude Code session is running in that project. Before v2.1.257, Claude Code left the same placeholders behind without flagging them.
888Run `claude doctor` in your terminal to list the leftover placeholder files. The [`Sandbox mask files left behind at write-deny paths`](https://code.claude.com/docs/en/errors#stale-sandbox-mask-files-left-by-a-killed-session) warning names some of them and counts the rest. Delete each file with `rm` while no other Claude Code session is running in that project. Before v2.1.257, Claude Code left the same placeholders behind without flagging them.
889889 
890890### `git` over SSH fails with the sandbox on
891891 
from line 928
928928 
929929A tool that uses the proxy fails differently when its host isn't allowed. You get a network prompt, or the tool receives a `403` response from the proxy.
930930 
931To let the tool connect, run the command that needs it outside the sandbox with [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands). This example excludes one script and adds an [ask rule](/docs/en/permissions.md) so that you approve each run:
931To let the tool connect, run the command that needs it outside the sandbox with [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands). This example excludes one script and adds an [ask rule](https://code.claude.com/docs/en/permissions.md) so that you approve each run:
932932 
933933```json theme={null}
934934{
from line 947
947947 
948948By default, a sandboxed command can't connect directly to a server that's running on your machine outside the sandbox, such as a dev server or a database in a container. What you can change depends on your platform:
949949 
950* **macOS**: set [`network.allowLocalBinding`](/docs/en/settings-reference#sandbox-network-allowlocalbinding) to `true`. Sandboxed commands can then listen on network ports and connect to any port on localhost, which includes every other service listening there. A localhost service that doesn't require authentication, such as a debugger, can then act for the command outside the sandbox, and a command that listens on a non-loopback address accepts connections from other machines
950* **macOS**: set [`network.allowLocalBinding`](https://code.claude.com/docs/en/settings-reference#sandbox-network-allowlocalbinding) to `true`. Sandboxed commands can then listen on network ports and connect to any port on localhost, which includes every other service listening there. A localhost service that doesn't require authentication, such as a debugger, can then act for the command outside the sandbox, and a command that listens on a non-loopback address accepts connections from other machines
951951* **Linux and WSL2**: a sandboxed command's `localhost` is private to that command. The command can listen on a port and reach servers it started itself. A direct connection to `localhost` or `127.0.0.1` doesn't reach servers on the host, and `allowLocalBinding` has no effect. Run the command that needs the host's server outside the sandbox with [`excludedCommands`](#run-commands-outside-the-sandbox-with-excludedcommands), where it has no filesystem or network limits. For connections that go through the sandbox proxy, see [Hostnames that resolve to local addresses](#hostnames-that-resolve-to-local-addresses)
952952 
953953This example turns the setting on for macOS:
from line 986
986986 
987987### `/sandbox` fails with `Sandbox settings are overridden by a higher-priority configuration`
988988 
989`/sandbox` prints `Error: Sandbox settings are overridden by a higher-priority configuration and cannot be changed locally.` instead of opening its panel when a higher [settings level](/docs/en/settings.md#settings-precedence) sets `sandbox.enabled`, `sandbox.autoAllowBashIfSandboxed`, or `sandbox.allowUnsandboxedCommands`. The panel saves your choices to `.claude/settings.local.json`, and a value saved there can't override those levels.
989`/sandbox` prints `Error: Sandbox settings are overridden by a higher-priority configuration and cannot be changed locally.` instead of opening its panel when a higher [settings level](https://code.claude.com/docs/en/settings.md#settings-precedence) sets `sandbox.enabled`, `sandbox.autoAllowBashIfSandboxed`, or `sandbox.allowUnsandboxedCommands`. The panel saves your choices to `.claude/settings.local.json`, and a value saved there can't override those levels.
990990 
991991Managed settings and `--settings` rank above local settings. To see which of them this session loaded, run `/status` and read the `Setting sources` line:
992992 
993* **`Command line arguments`**: if you started Claude Code with [`--settings`](/docs/en/settings#change-a-setting-for-one-session), check whether the file or JSON you passed sets one of those keys. If it does, change the value there, or start Claude Code again without those keys.
993* **`Command line arguments`**: if you started Claude Code with [`--settings`](https://code.claude.com/docs/en/settings#change-a-setting-for-one-session), check whether the file or JSON you passed sets one of those keys. If it does, change the value there, or start Claude Code again without those keys.
994994* **`Enterprise managed settings`**: your organization's managed settings are loaded. If they set one of those keys, you can't change that key from `/sandbox` or from any settings file you control, so ask your administrator.
995995 
996996### `/sandbox` prints `Sandbox settings not changed`
from line 1013
10131013}
10141014```
10151015 
1016This turns on [auto-allow mode](#sandbox-modes). For regular permissions mode, also set [`sandbox.autoAllowBashIfSandboxed`](/docs/en/settings-reference#sandbox-autoallowbashifsandboxed) to `false`. The change takes effect in your running session at the next shell command Claude runs, and the **Mode** tab of `/sandbox` then marks the mode in effect.
1016This turns on [auto-allow mode](#sandbox-modes). For regular permissions mode, also set [`sandbox.autoAllowBashIfSandboxed`](https://code.claude.com/docs/en/settings-reference#sandbox-autoallowbashifsandboxed) to `false`. The change takes effect in your running session at the next shell command Claude runs, and the **Mode** tab of `/sandbox` then marks the mode in effect.
10171017 
10181018## Limitations
10191019 
from line 1021
10211021 
10221022### Security limitations
10231023 
1024* **Network filtering**: the sandbox restricts which domains processes can connect to. By default the built-in proxy does not terminate or inspect TLS on outbound traffic, so the contents of encrypted connections are not examined. The experimental [`network.tlsTerminate`](/docs/en/settings-reference#sandbox-network-tlsterminate) setting terminates TLS at the proxy for [`mask` credential substitution](#mask-credentials) but does not add content filtering. You are responsible for ensuring that only trusted domains are allowed in your policy.
1024* **Network filtering**: the sandbox restricts which domains processes can connect to. By default the built-in proxy does not terminate or inspect TLS on outbound traffic, so the contents of encrypted connections are not examined. The experimental [`network.tlsTerminate`](https://code.claude.com/docs/en/settings-reference#sandbox-network-tlsterminate) setting terminates TLS at the proxy for [`mask` credential substitution](#mask-credentials) but does not add content filtering. You are responsible for ensuring that only trusted domains are allowed in your policy.
10251025 
10261026> [!WARNING]
10271027>
from line 1031
10311031* **Filesystem permission escalation**: overly broad filesystem write permissions can enable privilege escalation attacks. Allowing writes to directories containing executables in `$PATH`, system configuration directories, or user shell configuration files such as `.bashrc` or `.zshrc` can lead to code execution in different security contexts when other users or system processes access these files.
10321032* **Linux sandbox strength**: the Linux implementation provides strong filesystem and network isolation but includes an `enableWeakerNestedSandbox` mode that enables it to work inside Docker environments without privileged namespaces. This option considerably weakens security and should only be used when additional isolation is otherwise enforced.
10331033* **Apple Events on macOS**: the macOS sandbox blocks Apple Events by default. The `allowAppleEvents` setting lifts this restriction so tools such as `open` and `osascript` work, but it removes code-execution isolation: sandboxed commands can launch other applications unsandboxed with no user prompt, and can send AppleScript commands to running applications, subject to the per-app macOS automation-consent prompt (TCC). It is only honored from user, managed, or CLI settings. Project settings cannot enable it.
1034* **User namespaces on Linux and WSL2**: by default, sandboxed commands can't create user namespaces. If you set [`allowNestedUserNamespaces`](/docs/en/settings-reference#sandbox-allownestedusernamespaces) to `true`, you turn off a security boundary: code from a sandboxed command can end up running outside the sandbox. Before you turn this setting on, read the warning in [Chromium or rootless Podman fails inside the sandbox on Linux](#chromium-or-rootless-podman-fails-inside-the-sandbox-on-linux).
1034* **User namespaces on Linux and WSL2**: by default, sandboxed commands can't create user namespaces. If you set [`allowNestedUserNamespaces`](https://code.claude.com/docs/en/settings-reference#sandbox-allownestedusernamespaces) to `true`, you turn off a security boundary: code from a sandboxed command can end up running outside the sandbox. Before you turn this setting on, read the warning in [Chromium or rootless Podman fails inside the sandbox on Linux](#chromium-or-rootless-podman-fails-inside-the-sandbox-on-linux).
10351035 
10361036### Scope
10371037 
10381038The sandbox isolates shell commands and their child processes. [What runs outside the sandbox](#what-runs-outside-the-sandbox) lists the tools and helper processes it doesn't cover. Computer use and subagents relate to the sandbox as follows:
10391039 
1040* **Computer use**: when Claude opens apps and controls your screen, it runs on your actual desktop rather than in an isolated environment. Per-app permission prompts gate each application. See [computer use in the CLI](/docs/en/computer-use.md) or [computer use in Desktop](/docs/en/desktop.md#let-claude-use-your-computer).
1041* **Subagents**: [subagents](/docs/en/sub-agents.md) run in the same process as the parent session and use the same sandbox configuration. Bash commands inside a subagent are sandboxed when sandboxing is enabled in the parent session.
1042* **Background sessions**: a [background session](/docs/en/agent-view.md) runs in its own process, and its Bash commands are sandboxed when [its settings](/docs/en/agent-view.md#settings-and-provider) enable sandboxing.
1043* **A boundary around the whole process**: to put the processes in [What runs outside the sandbox](#what-runs-outside-the-sandbox) behind a boundary too, run Claude Code (local mode) inside the [sandbox runtime](/docs/en/sandbox-environments.md#sandbox-runtime) with a network allowlist limited to the hosts you approve, or in the [dev container](/docs/en/devcontainer.md) with its firewall script. For the background service and the sessions it hosts, see [Run Claude Code behind a corporate launcher](/docs/en/corporate-launcher.md).
1044* **Mods**: a [mod](/docs/en/plugins/mods/overview.md) is a plugin that runs its own code inside Claude Code, and a process that a mod starts runs outside the sandbox. See [What a mod can reach](/docs/en/plugins/mods/overview.md#what-a-mod-can-reach).
1040* **Computer use**: when Claude opens apps and controls your screen, it runs on your actual desktop rather than in an isolated environment. Per-app permission prompts gate each application. See [computer use in the CLI](https://code.claude.com/docs/en/computer-use.md) or [computer use in Desktop](https://code.claude.com/docs/en/desktop.md#let-claude-use-your-computer).
1041* **Subagents**: [subagents](https://code.claude.com/docs/en/sub-agents.md) run in the same process as the parent session and use the same sandbox configuration. Bash commands inside a subagent are sandboxed when sandboxing is enabled in the parent session.
1042* **Background sessions**: a [background session](https://code.claude.com/docs/en/agent-view.md) runs in its own process, and its Bash commands are sandboxed when [its settings](https://code.claude.com/docs/en/agent-view.md#settings-and-provider) enable sandboxing.
1043* **A boundary around the whole process**: to put the processes in [What runs outside the sandbox](#what-runs-outside-the-sandbox) behind a boundary too, run Claude Code (local mode) inside the [sandbox runtime](https://code.claude.com/docs/en/sandbox-environments.md#sandbox-runtime) with a network allowlist limited to the hosts you approve, or in the [dev container](https://code.claude.com/docs/en/devcontainer.md) with its firewall script. For the background service and the sessions it hosts, see [Run Claude Code behind a corporate launcher](https://code.claude.com/docs/en/corporate-launcher.md).
1044* **Mods**: a [mod](https://code.claude.com/docs/en/plugins/mods/overview.md) is a plugin that runs its own code inside Claude Code, and a process that a mod starts runs outside the sandbox. See [What a mod can reach](https://code.claude.com/docs/en/plugins/mods/overview.md#what-a-mod-can-reach).
10451045 
10461046> [!WARNING]
10471047>
from line 1049
10491049 
10501050## See also
10511051 
1052* [Sandbox environments](/docs/en/sandbox-environments.md): compare the built-in sandbox with dev containers, containers, and VMs
1053* [Security](/docs/en/security.md): security features and best practices
1054* [Permissions](/docs/en/permissions.md): permission configuration and access control
1055* [All settings](/docs/en/settings-reference.md): every settings key
1056* [CLI reference](/docs/en/cli-reference.md): command-line options
1052* [Sandbox environments](https://code.claude.com/docs/en/sandbox-environments.md): compare the built-in sandbox with dev containers, containers, and VMs
1053* [Security](https://code.claude.com/docs/en/security.md): security features and best practices
1054* [Permissions](https://code.claude.com/docs/en/permissions.md): permission configuration and access control
1055* [All settings](https://code.claude.com/docs/en/settings-reference.md): every settings key
1056* [CLI reference](https://code.claude.com/docs/en/cli-reference.md): command-line options
10571057 

scheduled-tasks Changed · +27 / -27 lines

from line 8
88>
99> These cases are covered on other pages:
1010>
11> * **Reacting to events as they happen instead of polling**: see [Channels](/docs/en/channels.md). Your CI can push the failure into the session directly.
12> * **Keeping the session working turn after turn toward a condition rather than on an interval**: see [`/goal`](/docs/en/goal).
11> * **Reacting to events as they happen instead of polling**: see [Channels](https://code.claude.com/docs/en/channels.md). Your CI can push the failure into the session directly.
12> * **Keeping the session working turn after turn toward a condition rather than on an interval**: see [`/goal`](https://code.claude.com/docs/en/goal).
1313 
14Tasks are session-scoped. When you resume with `--resume` or `--continue`, Claude Code restores tasks that haven't [expired](#seven-day-expiry), except those listed under [Limitations](#limitations). For scheduling that survives independently of any session, create a [cloud scheduled task](/docs/en/routines.md), set up a [Desktop scheduled task](/docs/en/desktop-scheduled-tasks.md), or use [GitHub Actions](/docs/en/github-actions.md).
14Tasks are session-scoped. When you resume with `--resume` or `--continue`, Claude Code restores tasks that haven't [expired](#seven-day-expiry), except those listed under [Limitations](#limitations). For scheduling that survives independently of any session, create a [cloud scheduled task](https://code.claude.com/docs/en/routines.md), set up a [Desktop scheduled task](https://code.claude.com/docs/en/desktop-scheduled-tasks.md), or use [GitHub Actions](https://code.claude.com/docs/en/github-actions.md).
1515 
1616## Compare scheduling options
1717 
1818Claude Code offers three ways to schedule recurring or one-off work:
1919 
20| | [Cloud](/docs/en/routines.md) | [Desktop](/docs/en/desktop-scheduled-tasks.md) | [`/loop`](/docs/en/scheduled-tasks) |
20| | [Cloud](https://code.claude.com/docs/en/routines.md) | [Desktop](https://code.claude.com/docs/en/desktop-scheduled-tasks.md) | [`/loop`](https://code.claude.com/docs/en/scheduled-tasks) |
2121| :- | :- | :- | :- |
2222| Runs on | Cloud, Anthropic-managed by default | Your machine | Your machine |
2323| Requires machine on | No | Yes | Yes |
2424| Requires open session | No | No | Yes |
25| Persistent across restarts | Yes | Yes | Restored on `--resume`, with [exceptions](/docs/en/scheduled-tasks.md#limitations) |
25| Persistent across restarts | Yes | Yes | Restored on `--resume`, with [exceptions](https://code.claude.com/docs/en/scheduled-tasks.md#limitations) |
2626| Access to local files | No (fresh clone) | Yes | Yes |
27| MCP servers | Connectors configured per task | [Config files](/docs/en/mcp.md) and connectors | Inherits from session |
28| Permission prompts | [Auto mode](/docs/en/routines.md#permission-prompts-during-a-run) by default | Configurable per task | Inherits from session |
27| MCP servers | Connectors configured per task | [Config files](https://code.claude.com/docs/en/mcp.md) and connectors | Inherits from session |
28| Permission prompts | [Auto mode](https://code.claude.com/docs/en/routines.md#permission-prompts-during-a-run) by default | Configurable per task | Inherits from session |
2929| Customizable schedule | Yes | Yes | Yes |
30| Minimum interval | 1 hour | 1 minute when Claude sets it, [1 hour in the forms](/docs/en/desktop-scheduled-tasks.md#schedule-options) | 1 minute |
30| Minimum interval | 1 hour | 1 minute when Claude sets it, [1 hour in the forms](https://code.claude.com/docs/en/desktop-scheduled-tasks.md#schedule-options) | 1 minute |
3131 
3232> [!TIP]
3333>
from line 35
3535 
3636## Run a prompt repeatedly with /loop
3737 
38The `/loop` [bundled skill](/docs/en/commands.md) is the quickest way to run a prompt on repeat while the session stays open. Both the interval and the prompt are optional, and what you provide determines how the loop behaves.
38The `/loop` [bundled skill](https://code.claude.com/docs/en/commands.md) is the quickest way to run a prompt on repeat while the session stays open. Both the interval and the prompt are optional, and what you provide determines how the loop behaves.
3939 
4040| What you provide | Example | What happens |
4141| :- | :- | :- |
from line 43
4343| Prompt only | `/loop check the deploy` | Your prompt runs at an [interval Claude chooses](#let-claude-choose-the-interval) each iteration |
4444| Interval only, or nothing | `/loop` | The [built-in maintenance prompt](#run-the-built-in-maintenance-prompt) runs, or your `loop.md` if one exists |
4545 
46You can also pass a skill as the prompt, for example `/loop 20m /review-pr 1234`, to re-run that skill each iteration. A scheduled fire only runs skills that Claude is [allowed to invoke on its own](/docs/en/skills.md#control-who-invokes-a-skill). The following reach Claude as plain text instead of executing:
46You can also pass a skill as the prompt, for example `/loop 20m /review-pr 1234`, to re-run that skill each iteration. A scheduled fire only runs skills that Claude is [allowed to invoke on its own](https://code.claude.com/docs/en/skills.md#control-who-invokes-a-skill). The following reach Claude as plain text instead of executing:
4747 
4848* Built-in commands such as `/permissions`, `/model`, or `/clear`
49* Skills marked [`disable-model-invocation: true`](/docs/en/skills#frontmatter-reference), including the bundled `/verify` skill
50* Skills withheld from Claude by a [`skillOverrides`](/docs/en/skills#override-skill-visibility-from-settings) setting or a `Skill` [deny rule](/docs/en/skills.md#restrict-claude’s-skill-access)
51* [MCP prompts](/docs/en/mcp.md#use-mcp-prompts-as-commands) such as `/mcp__github__list_prs`
49* Skills marked [`disable-model-invocation: true`](https://code.claude.com/docs/en/skills#frontmatter-reference), including the bundled `/verify` skill
50* Skills withheld from Claude by a [`skillOverrides`](https://code.claude.com/docs/en/skills#override-skill-visibility-from-settings) setting or a `Skill` [deny rule](https://code.claude.com/docs/en/skills.md#restrict-claude’s-skill-access)
51* [MCP prompts](https://code.claude.com/docs/en/mcp.md#use-mcp-prompts-as-commands) such as `/mcp__github__list_prs`
5252 
5353### Run on a fixed interval
5454 
from line 72
7272/loop check whether CI passed and address any review comments
7373```
7474 
75In a session where the [Monitor tool is available](/docs/en/tools-reference.md#monitor-tool), Claude may use it directly when you ask for a dynamic `/loop` schedule. Monitor runs a background script and streams each output line back, which avoids polling altogether and is often more token-efficient and responsive than re-running a prompt on an interval.
75In a session where the [Monitor tool is available](https://code.claude.com/docs/en/tools-reference.md#monitor-tool), Claude may use it directly when you ask for a dynamic `/loop` schedule. Monitor runs a background script and streams each output line back, which avoids polling altogether and is often more token-efficient and responsive than re-running a prompt on an interval.
7676 
7777A dynamically scheduled loop appears in your [scheduled task list](#manage-scheduled-tasks) like any other task, so you can list or cancel it the same way. The list shows its pending wakeup as a one-shot task, so a wakeup scheduled for `:00` or `:30` can [run a little early](#when-a-one-shot-task-runs-early). The [seven-day expiry](#seven-day-expiry) applies to the loop too.
7878 
7979> [!NOTE]
8080>
81> Dynamically chosen intervals and the [built-in maintenance prompt](#run-the-built-in-maintenance-prompt) work on every provider, and with [feature-flag fetching](/docs/en/env-vars.md#features-that-need-feature-flag-fetching) turned off. On Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry, or with fetching turned off, both require Claude Code v2.1.248 or later.
81> Dynamically chosen intervals and the [built-in maintenance prompt](#run-the-built-in-maintenance-prompt) work on every provider, and with [feature-flag fetching](https://code.claude.com/docs/en/env-vars.md#features-that-need-feature-flag-fetching) turned off. On Amazon Bedrock, Claude Platform on AWS, Google Cloud's Agent Platform, and Microsoft Foundry, or with fetching turned off, both require Claude Code v2.1.248 or later.
8282 
8383### Run the built-in maintenance prompt
8484 
from line 123
123123* **[Self-paced loop](#let-claude-choose-the-interval)**: while the loop is waiting for its next iteration, press `Esc` to stop it. You can also stop it with `Ctrl+C` while Claude is idle, except in a [background session you've attached to](#detach-without-stopping-a-loop). A `Pending wakeup cancelled` notice confirms that the loop won't fire again.
124124* **[Fixed-interval loop](#run-on-a-fixed-interval)**: to stop a loop such as `/loop 5m`, ask Claude to cancel it, for example `cancel the deploy check loop`. `Esc` and `Ctrl+C` don't stop it, or any task you [scheduled by asking Claude directly](#manage-scheduled-tasks). A loop you don't cancel ends when [seven days elapse](#seven-day-expiry).
125125 
126In [self-paced mode](#let-claude-choose-the-interval), Claude can also end the loop on its own once the task is complete. Claude calls the [`ScheduleWakeup` tool](/docs/en/tools-reference) with `stop: true`, which cancels the pending wakeup immediately. If an iteration ends without either rescheduling or stopping, Claude Code schedules one fallback wakeup about 20 minutes later and ends the loop when that iteration doesn't reschedule either.
126In [self-paced mode](#let-claude-choose-the-interval), Claude can also end the loop on its own once the task is complete. Claude calls the [`ScheduleWakeup` tool](https://code.claude.com/docs/en/tools-reference) with `stop: true`, which cancels the pending wakeup immediately. If an iteration ends without either rescheduling or stopping, Claude Code schedules one fallback wakeup about 20 minutes later and ends the loop when that iteration doesn't reschedule either.
127127 
128128#### Detach without stopping a loop
129129 
130When you're [attached to a background session](/docs/en/agent-view.md#attach-to-a-session) and Claude is idle, press `Ctrl+C` twice on an empty prompt to detach. A self-paced loop waiting there for its next iteration keeps running, and that iteration starts on schedule. To stop the loop instead, press `Esc` before you detach. Detaching with `Ctrl+C` without stopping the loop requires Claude Code v2.1.295 or later.
130When you're [attached to a background session](https://code.claude.com/docs/en/agent-view.md#attach-to-a-session) and Claude is idle, press `Ctrl+C` twice on an empty prompt to detach. A self-paced loop waiting there for its next iteration keeps running, and that iteration starts on schedule. To stop the loop instead, press `Esc` before you detach. Detaching with `Ctrl+C` without stopping the loop requires Claude Code v2.1.295 or later.
131131 
132132## Set a one-time reminder
133133 
from line 183
183183 
184184* **Evenly spaced runs**: a task with equal gaps, such as hourly, daily, or every 10 minutes, runs the same number of minutes late every time, including when the session is idle.
185185* **Unevenly spaced runs**: a task with uneven gaps, such as one scheduled for `:00` and `:15` each hour, can run a different number of minutes late at each.
186* **Every five minutes**: a task written `*/5 * * * *` isn't delayed. A run exactly five minutes after the last one would start after a five-minute [prompt cache](/docs/en/prompt-caching.md#which-ttl-each-request-gets) had expired, so each run starts 15 seconds sooner.
186* **Every five minutes**: a task written `*/5 * * * *` isn't delayed. A run exactly five minutes after the last one would start after a five-minute [prompt cache](https://code.claude.com/docs/en/prompt-caching.md#which-ttl-each-request-gets) had expired, so each run starts 15 seconds sooner.
187187 
188188Find your schedule in the first column of this table to see when each of its runs starts.
189189 
from line 202
202202 
203203### Seven-day expiry
204204 
205Recurring tasks automatically expire 7 days after creation. The task fires one final time, then deletes itself. This bounds how long a forgotten loop can run. If you need a recurring task to last longer, cancel and recreate it before it expires, or use [cloud scheduled tasks](/docs/en/routines.md) or [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks.md) for durable scheduling.
205Recurring tasks automatically expire 7 days after creation. The task fires one final time, then deletes itself. This bounds how long a forgotten loop can run. If you need a recurring task to last longer, cancel and recreate it before it expires, or use [cloud scheduled tasks](https://code.claude.com/docs/en/routines.md) or [Desktop scheduled tasks](https://code.claude.com/docs/en/desktop-scheduled-tasks.md) for durable scheduling.
206206 
207207## Cron expression reference
208208 
from line 223
223223 
224224## Disable session-scoped scheduled tasks
225225 
226Set `CLAUDE_CODE_DISABLE_CRON=1` in your environment to disable the scheduler entirely. The cron tools and `/loop` become unavailable, and any already-scheduled tasks stop firing. See [Environment variables](/docs/en/env-vars.md) for the full list of disable flags.
226Set `CLAUDE_CODE_DISABLE_CRON=1` in your environment to disable the scheduler entirely. The cron tools and `/loop` become unavailable, and any already-scheduled tasks stop firing. See [Environment variables](https://code.claude.com/docs/en/env-vars.md) for the full list of disable flags.
227227 
228228## Limitations
229229 
230230Session-scoped scheduling has inherent constraints:
231231 
232* Tasks only fire while Claude Code is running and idle. Closing the terminal or letting the session exit stops them firing. [Backgrounding the session](/docs/en/agent-view.md#from-inside-a-session) carries `/loop` tasks over to a background session, which keeps running without a terminal.
232* Tasks only fire while Claude Code is running and idle. Closing the terminal or letting the session exit stops them firing. [Backgrounding the session](https://code.claude.com/docs/en/agent-view.md#from-inside-a-session) carries `/loop` tasks over to a background session, which keeps running without a terminal.
233233* No catch-up for missed fires. If a task's scheduled time passes while Claude is busy on a long-running request, it fires once when Claude becomes idle, not once per missed interval.
234* In a [background session](/docs/en/agent-view.md#from-inside-a-session), a [self-paced loop](#let-claude-choose-the-interval) stops if its next wakeup comes due while the session's process isn't running, such as after a crash. When the process starts again, the transcript shows a [notice that the wakeup won't fire](/docs/en/errors.md#restarted-after-its-next-loop-wakeup-was-due). To continue the loop, [reply to the session](/docs/en/agent-view.md#peek-and-reply) and ask Claude to keep it running.
234* In a [background session](https://code.claude.com/docs/en/agent-view.md#from-inside-a-session), a [self-paced loop](#let-claude-choose-the-interval) stops if its next wakeup comes due while the session's process isn't running, such as after a crash. When the process starts again, the transcript shows a [notice that the wakeup won't fire](https://code.claude.com/docs/en/errors.md#restarted-after-its-next-loop-wakeup-was-due). To continue the loop, [reply to the session](https://code.claude.com/docs/en/agent-view.md#peek-and-reply) and ask Claude to keep it running.
235235* When you resume a session with `claude --resume` or `claude --continue`, Claude Code restores the tasks scheduled with `CronCreate`, except recurring tasks that have [expired](#seven-day-expiry) and one-shot tasks whose scheduled time has passed. A [self-paced `/loop`](#let-claude-choose-the-interval) isn't restored, so run `/loop` again to restart it.
236236* A restored recurring task doesn't fire for the runs it missed while the session wasn't running. Its next run is the first one due after you resume, and [jitter](#jitter) applies to that run.
237237* When you resume with `claude --resume` or `claude --continue`, Claude Code doesn't restart the background Bash commands and monitors that were running when the session ended. If you still need one, ask Claude to start it again.
238* With [feature-flag fetching off](/docs/en/env-vars.md#features-that-need-feature-flag-fetching), Claude Code stores a task you asked to keep across sessions in the project's `.claude/scheduled_tasks.json` file. When the `.claude` directory or that file is a symlink, Claude Code returns an error instead of scheduling the task. A saved task runs only in the project folder where you created it. If you copy the file into another folder, such as a new worktree, sessions there list the copied tasks but don't run them, so create the task again in that folder.
238* With [feature-flag fetching off](https://code.claude.com/docs/en/env-vars.md#features-that-need-feature-flag-fetching), Claude Code stores a task you asked to keep across sessions in the project's `.claude/scheduled_tasks.json` file. When the `.claude` directory or that file is a symlink, Claude Code returns an error instead of scheduling the task. A saved task runs only in the project folder where you created it. If you copy the file into another folder, such as a new worktree, sessions there list the copied tasks but don't run them, so create the task again in that folder.
239239 
240240For cron-driven automation that needs to run unattended:
241241 
242* [Cloud scheduled tasks](/docs/en/routines.md): run in the cloud on a schedule, via API call, or on GitHub events
243* [GitHub Actions](/docs/en/github-actions.md): use a `schedule` trigger in CI
244* [Desktop scheduled tasks](/docs/en/desktop-scheduled-tasks.md): run locally on your machine
242* [Cloud scheduled tasks](https://code.claude.com/docs/en/routines.md): run in the cloud on a schedule, via API call, or on GitHub events
243* [GitHub Actions](https://code.claude.com/docs/en/github-actions.md): use a `schedule` trigger in CI
244* [Desktop scheduled tasks](https://code.claude.com/docs/en/desktop-scheduled-tasks.md): run locally on your machine
245245 

security Changed · +32 / -32 lines

from line 10
1010 
1111### Permission-based architecture
1212 
13A session's permission mode sets which actions Claude can take without asking you first. Auto mode is the built-in starting permission mode for interactive terminal and VS Code sessions. [Which mode a session starts in](/docs/en/permission-modes.md#which-mode-a-session-starts-in) covers earlier versions, other surfaces, and the settings that change the starting permission mode.
13A session's permission mode sets which actions Claude can take without asking you first. Auto mode is the built-in starting permission mode for interactive terminal and VS Code sessions. [Which mode a session starts in](https://code.claude.com/docs/en/permission-modes.md#which-mode-a-session-starts-in) covers earlier versions, other surfaces, and the settings that change the starting permission mode.
1414 
15* **Auto mode**: A separate classifier model reviews actions instead of you and blocks the ones it judges unsafe. [How the classifier evaluates actions](/docs/en/permission-modes.md#how-the-classifier-evaluates-actions) lists which actions Claude Code approves outright, which it sends to the classifier, and which Claude Code still asks you about. Your explicit ask and deny rules still apply, and your organization can [turn auto mode off](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode)
16* **Manual mode**: Claude Code starts with read-only permissions. When it needs to edit files, run tests, or execute commands, it asks you first, and you choose whether to approve the action once or allow it from then on. It runs a built-in set of [read-only commands](/docs/en/permissions.md#read-only-commands) such as `ls`, `cat`, and `git status` without asking
15* **Auto mode**: A separate classifier model reviews actions instead of you and blocks the ones it judges unsafe. [How the classifier evaluates actions](https://code.claude.com/docs/en/permission-modes.md#how-the-classifier-evaluates-actions) lists which actions Claude Code approves outright, which it sends to the classifier, and which Claude Code still asks you about. Your explicit ask and deny rules still apply, and your organization can [turn auto mode off](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode)
16* **Manual mode**: Claude Code starts with read-only permissions. When it needs to edit files, run tests, or execute commands, it asks you first, and you choose whether to approve the action once or allow it from then on. It runs a built-in set of [read-only commands](https://code.claude.com/docs/en/permissions.md#read-only-commands) such as `ls`, `cat`, and `git status` without asking
1717 
18You and your organization configure these permissions directly. For detailed permission configuration, see [Permissions](/docs/en/permissions.md).
18You and your organization configure these permissions directly. For detailed permission configuration, see [Permissions](https://code.claude.com/docs/en/permissions.md).
1919 
2020### Built-in protections
2121 
2222To mitigate risks in agentic systems:
2323 
24* **Sandboxed bash tool**: [Sandbox](/docs/en/sandboxing.md) bash commands with filesystem and network isolation, reducing permission prompts while maintaining security. Configure with `/sandbox` to define boundaries where Claude Code can work autonomously
24* **Sandboxed bash tool**: [Sandbox](https://code.claude.com/docs/en/sandboxing.md) bash commands with filesystem and network isolation, reducing permission prompts while maintaining security. Configure with `/sandbox` to define boundaries where Claude Code can work autonomously
2525* **Working directory boundary**: In Manual mode, Claude Code asks you before its file tools read or write outside the folder where it was started and its subfolders. The boundary is a permission prompt, so a Bash command you approve can still write anywhere your user account can
26 * To read a folder without the prompt, add it as an [additional directory](/docs/en/permissions.md#working-directories)
27 * To restrict Bash commands at the operating system level, turn on [sandboxing](/docs/en/sandboxing.md#filesystem-isolation)
26 * To read a folder without the prompt, add it as an [additional directory](https://code.claude.com/docs/en/permissions.md#working-directories)
27 * To restrict Bash commands at the operating system level, turn on [sandboxing](https://code.claude.com/docs/en/sandboxing.md#filesystem-isolation)
2828* **Prompt fatigue mitigation**: Support for allowlisting frequently used safe commands per-user, per-codebase, or per-organization
2929* **Accept Edits mode**: Auto-approves file edits and a fixed set of filesystem Bash commands like `mkdir`, `touch`, `rm`, `mv`, `cp`, and `sed` for paths in the working directory. Other Bash commands and out-of-scope paths still prompt
3030 
from line 39
3939### Core protections
4040 
4141* **Permission system**: In Manual mode, sensitive operations require explicit approval
42* **Network command approval**: Commands that fetch content from the web such as `curl` and `wget` are not auto-approved by default. In Manual mode they prompt like any other non-read-only Bash command, so you can still approve once or add an explicit allow rule like `Bash(curl *)`. To stop Claude from running them, add them to [`permissions.deny`](/docs/en/permissions#tool-specific-permission-rules). A deny rule matches the command [as written](/docs/en/permissions.md#bash-rule-limits); for network enforcement that doesn't depend on the command text, see [sandbox network isolation](/docs/en/sandboxing.md#network-isolation)
42* **Network command approval**: Commands that fetch content from the web such as `curl` and `wget` are not auto-approved by default. In Manual mode they prompt like any other non-read-only Bash command, so you can still approve once or add an explicit allow rule like `Bash(curl *)`. To stop Claude from running them, add them to [`permissions.deny`](https://code.claude.com/docs/en/permissions#tool-specific-permission-rules). A deny rule matches the command [as written](https://code.claude.com/docs/en/permissions.md#bash-rule-limits); for network enforcement that doesn't depend on the command text, see [sandbox network isolation](https://code.claude.com/docs/en/sandboxing.md#network-isolation)
4343 
4444### Privacy safeguards
4545 
from line 54
5454### Additional safeguards
5555 
5656* **Network request approval**: In Manual mode, most tools that make network requests require user approval by default
57* **Web page summaries**: For most fetches, WebFetch runs a separate model call over the page, and Claude receives that call's answer instead of the raw page. See [WebFetch tool behavior](/docs/en/tools-reference.md#webfetch-tool-behavior)
58* **Trust verification**: In an interactive session, Claude Code shows the workspace trust dialog when you start it in a folder you haven't trusted. Servers in a project's `.mcp.json` have their own approval prompt, and [Project scope](/docs/en/mcp.md#project-scope) lists the sessions that skip it
59 * Note: A `-p` session shows neither prompt. [What runs before you trust a folder](/docs/en/permissions.md#what-runs-before-you-trust-a-folder) lists what a repository's files can run there
57* **Web page summaries**: For most fetches, WebFetch runs a separate model call over the page, and Claude receives that call's answer instead of the raw page. See [WebFetch tool behavior](https://code.claude.com/docs/en/tools-reference.md#webfetch-tool-behavior)
58* **Trust verification**: In an interactive session, Claude Code shows the workspace trust dialog when you start it in a folder you haven't trusted. Servers in a project's `.mcp.json` have their own approval prompt, and [Project scope](https://code.claude.com/docs/en/mcp.md#project-scope) lists the sessions that skip it
59 * Note: A `-p` session shows neither prompt. [What runs before you trust a folder](https://code.claude.com/docs/en/permissions.md#what-runs-before-you-trust-a-folder) lists what a repository's files can run there
6060 * Note: When you start Claude Code directly in your home directory, trust acceptance is held for the current session only and is not written to disk, so the prompt reappears on each launch. There is no setting to persist it. Start Claude Code from a project subdirectory instead, where trust acceptance is saved per directory
61* **Command injection detection**: In Manual mode, Claude Code asks before running a Bash command it can't fully analyze. An allow rule for part of a command, such as `Bash(git *)`, doesn't skip that prompt. [Sandboxed commands](/docs/en/permissions.md#how-permissions-interact-with-sandboxing) can run without it
61* **Command injection detection**: In Manual mode, Claude Code asks before running a Bash command it can't fully analyze. An allow rule for part of a command, such as `Bash(git *)`, doesn't skip that prompt. [Sandboxed commands](https://code.claude.com/docs/en/permissions.md#how-permissions-interact-with-sandboxing) can run without it
6262* **Fail-closed matching**: In Manual mode, unmatched commands require approval by default
63* **Secure credential storage**: API keys and tokens are stored in the macOS Keychain when available. On Linux they're stored in a file with mode `0600`, and on Windows in a file that inherits the access controls of your user profile directory. See [Credential Management](/docs/en/authentication.md#credential-management)
63* **Secure credential storage**: API keys and tokens are stored in the macOS Keychain when available. On Linux they're stored in a file with mode `0600`, and on Windows in a file that inherits the access controls of your user profile directory. See [Credential Management](https://code.claude.com/docs/en/authentication.md#credential-management)
6464 
6565> [!WARNING]
6666>
from line 82
8282 
8383## MCP security
8484 
85You can connect Claude Code to Model Context Protocol (MCP) servers. Project-scoped servers are defined in `.mcp.json`, which you can check into source control. Servers at [other scopes](/docs/en/mcp.md#mcp-installation-scopes) and [claude.ai connectors](/docs/en/mcp.md#how-connectors-reach-claude-code) are configured outside the repository, and plugins can add servers too, so reviewing `.mcp.json` doesn't show every server a session can load. To restrict which servers run in your organization, see [Managed MCP configuration](/docs/en/managed-mcp.md).
85You can connect Claude Code to Model Context Protocol (MCP) servers. Project-scoped servers are defined in `.mcp.json`, which you can check into source control. Servers at [other scopes](https://code.claude.com/docs/en/mcp.md#mcp-installation-scopes) and [claude.ai connectors](https://code.claude.com/docs/en/mcp.md#how-connectors-reach-claude-code) are configured outside the repository, and plugins can add servers too, so reviewing `.mcp.json` doesn't show every server a session can load. To restrict which servers run in your organization, see [Managed MCP configuration](https://code.claude.com/docs/en/managed-mcp.md).
8686 
8787We encourage either writing your own MCP servers or using MCP servers from providers that you trust. You are able to configure Claude Code permissions for MCP servers. Anthropic reviews connectors against its [listing criteria](https://claude.com/docs/connectors/building/review-criteria) before adding them to the [Anthropic Directory](https://claude.ai/directory), but does not security-audit or manage any MCP server.
8888 
8989## IDE security
9090 
91See [The built-in IDE MCP server](/docs/en/vs-code.md#the-built-in-ide-mcp-server) for more information on running Claude Code in an IDE.
91See [The built-in IDE MCP server](https://code.claude.com/docs/en/vs-code.md#the-built-in-ide-mcp-server) for more information on running Claude Code in an IDE.
9292 
9393## Cloud execution security
9494 
95When you use [cloud sessions](/docs/en/claude-code-on-the-web.md), additional security controls are in place. Sessions your organization routes to a [self-hosted environment](/docs/en/self-hosted-environments) run on your own infrastructure, where isolation, network egress, and git credentials are your deployment's responsibility. In Anthropic-hosted environments:
95When you use [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md), additional security controls are in place. Sessions your organization routes to a [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) run on your own infrastructure, where isolation, network egress, and git credentials are your deployment's responsibility. In Anthropic-hosted environments:
9696 
9797* **Isolated virtual machines**: Each cloud session runs in an isolated, Anthropic-managed VM
9898* **Network access controls**: Network access is limited by default and can be configured to be disabled or allow only specific domains
99* **Credential protection**: GitHub credentials are stored encrypted on Anthropic's servers and never enter the session VM. The VM holds a short-lived credential scoped to that session, and GitHub traffic goes through an [Anthropic proxy](/docs/en/cloud-environments.md#github-proxy) that attaches the GitHub credential on the server side. See [GitHub authentication options](/docs/en/claude-code-on-the-web.md#github-authentication-options) for how you grant access
100* **Push restrictions**: The [GitHub proxy](/docs/en/cloud-environments.md#github-proxy) rejects branch deletions and pushes of anything other than a branch, such as a tag. GitHub decides which branches a session can update by applying your repository's branch protection rules and rulesets to the GitHub access you connected. A rule that access can bypass doesn't block a session's push
99* **Credential protection**: GitHub credentials are stored encrypted on Anthropic's servers and never enter the session VM. The VM holds a short-lived credential scoped to that session, and GitHub traffic goes through an [Anthropic proxy](https://code.claude.com/docs/en/cloud-environments.md#github-proxy) that attaches the GitHub credential on the server side. See [GitHub authentication options](https://code.claude.com/docs/en/claude-code-on-the-web.md#github-authentication-options) for how you grant access
100* **Push restrictions**: The [GitHub proxy](https://code.claude.com/docs/en/cloud-environments.md#github-proxy) rejects branch deletions and pushes of anything other than a branch, such as a tag. GitHub decides which branches a session can update by applying your repository's branch protection rules and rulesets to the GitHub access you connected. A rule that access can bypass doesn't block a session's push
101101* **Audit logging**: All operations in cloud sessions are logged for compliance and audit purposes
102102* **Automatic cleanup**: Session VMs are reclaimed after a period of inactivity
103* **Deletion**: You can [delete a session](/docs/en/claude-code-on-the-web.md#delete-sessions) at any time. See [Cloud execution data flow](/docs/en/data-usage.md#cloud-execution-data-flow-and-dependencies) for what Anthropic stores for a cloud session
103* **Deletion**: You can [delete a session](https://code.claude.com/docs/en/claude-code-on-the-web.md#delete-sessions) at any time. See [Cloud execution data flow](https://code.claude.com/docs/en/data-usage.md#cloud-execution-data-flow-and-dependencies) for what Anthropic stores for a cloud session
104104 
105For more details on cloud execution, see [Use Claude Code in the cloud](/docs/en/claude-code-on-the-web.md); to configure network access for cloud sessions, see [Configure cloud environments](/docs/en/cloud-environments.md#network-access).
105For more details on cloud execution, see [Use Claude Code in the cloud](https://code.claude.com/docs/en/claude-code-on-the-web.md); to configure network access for cloud sessions, see [Configure cloud environments](https://code.claude.com/docs/en/cloud-environments.md#network-access).
106106 
107[Remote Control](/docs/en/remote-control.md) sessions work differently: the web interface connects to a Claude Code process running on your local machine. All code execution and file access stays local, and session traffic travels through the Anthropic API over TLS; while connected, the session transcript is stored on Anthropic servers to sync the conversation across devices, as described in [Connection and security](/docs/en/remote-control.md#connection-and-security). No cloud VMs or sandboxing are involved. The connection uses multiple short-lived, narrowly scoped credentials, each limited to a specific purpose and expiring independently, to limit the blast radius of any single compromised credential.
107[Remote Control](https://code.claude.com/docs/en/remote-control.md) sessions work differently: the web interface connects to a Claude Code process running on your local machine. All code execution and file access stays local, and session traffic travels through the Anthropic API over TLS; while connected, the session transcript is stored on Anthropic servers to sync the conversation across devices, as described in [Connection and security](https://code.claude.com/docs/en/remote-control.md#connection-and-security). No cloud VMs or sandboxing are involved. The connection uses multiple short-lived, narrowly scoped credentials, each limited to a specific purpose and expiring independently, to limit the blast radius of any single compromised credential.
108108 
109109## Security best practices
110110 
from line 112
112112 
113113* Review all suggested changes before approval
114114* Use project-specific permission settings for sensitive repositories
115* For additional isolation, run the whole Claude Code (local mode) process inside the [sandbox runtime](/docs/en/sandbox-environments.md#sandbox-runtime) or a [dev container](/docs/en/devcontainer.md)
115* For additional isolation, run the whole Claude Code (local mode) process inside the [sandbox runtime](https://code.claude.com/docs/en/sandbox-environments.md#sandbox-runtime) or a [dev container](https://code.claude.com/docs/en/devcontainer.md)
116116* Regularly audit your permission settings with `/permissions`
117117 
118118### Team security
119119 
120* Use [managed settings](/docs/en/settings.md#where-settings-live) to enforce organizational standards
120* Use [managed settings](https://code.claude.com/docs/en/settings.md#where-settings-live) to enforce organizational standards
121121* Share approved permission configurations through version control
122122* Train team members on security best practices
123* Monitor Claude Code usage through [OpenTelemetry metrics](/docs/en/monitoring-usage.md)
124* Audit or block settings changes during sessions with [`ConfigChange` hooks](/docs/en/hooks#configchange)
123* Monitor Claude Code usage through [OpenTelemetry metrics](https://code.claude.com/docs/en/monitoring-usage.md)
124* Audit or block settings changes during sessions with [`ConfigChange` hooks](https://code.claude.com/docs/en/hooks#configchange)
125125 
126126### Reporting security issues
127127 
from line 134
134134 
135135## Related resources
136136 
137* [Security guidance plugin](/docs/en/security-guidance.md): have Claude review and fix vulnerabilities in its own code changes during the session
138* [`/security-review`](/docs/en/commands#all-commands): run an on-demand security pass over the changes on your current branch
139* [Sandbox environments](/docs/en/sandbox-environments.md): compare isolation approaches and choose one for your threat model
140* [Sandboxing](/docs/en/sandboxing.md): filesystem and network isolation for Bash commands
141* [Permissions](/docs/en/permissions.md): configure permissions and access controls
142* [Monitoring usage](/docs/en/monitoring-usage.md): track and audit Claude Code activity
143* [Development containers](/docs/en/devcontainer.md): secure, isolated environments
137* [Security guidance plugin](https://code.claude.com/docs/en/security-guidance.md): have Claude review and fix vulnerabilities in its own code changes during the session
138* [`/security-review`](https://code.claude.com/docs/en/commands#all-commands): run an on-demand security pass over the changes on your current branch
139* [Sandbox environments](https://code.claude.com/docs/en/sandbox-environments.md): compare isolation approaches and choose one for your threat model
140* [Sandboxing](https://code.claude.com/docs/en/sandboxing.md): filesystem and network isolation for Bash commands
141* [Permissions](https://code.claude.com/docs/en/permissions.md): configure permissions and access controls
142* [Monitoring usage](https://code.claude.com/docs/en/monitoring-usage.md): track and audit Claude Code activity
143* [Development containers](https://code.claude.com/docs/en/devcontainer.md): secure, isolated environments
144144* [Anthropic Trust Center](https://trust.anthropic.com): security certifications and compliance
145145* [CISO's guide to agentic AI](https://claude.com/blog/ciso-guide-to-agentic-ai): a security leader's framework for assessing agentic AI deployments
146146 

security-guidance Changed · +20 / -20 lines

from line 6
66 
77Once installed, the plugin runs automatically. There is nothing to invoke and no separate command to remember.
88 
9The plugin is the in-session companion to [Code Review](/docs/en/code-review.md), which runs on pull requests. This plugin reduces what reaches the PR. Code Review catches what does. For how the plugin layers with on-demand review and CI scanning, or to scan code you already have rather than changes Claude is writing, see [How this fits with other security tools](#how-this-fits-with-other-security-tools).
9The plugin is the in-session companion to [Code Review](https://code.claude.com/docs/en/code-review.md), which runs on pull requests. This plugin reduces what reaches the PR. Code Review catches what does. For how the plugin layers with on-demand review and CI scanning, or to scan code you already have rather than changes Claude is writing, see [How this fits with other security tools](#how-this-fits-with-other-security-tools).
1010 
1111## Prerequisites
1212 
from line 17
1717 
1818## Install the plugin
1919 
20In a terminal Claude Code session, install from the [official Anthropic marketplace](/docs/en/plugins/anthropic-marketplaces.md):
20In a terminal Claude Code session, install from the [official Anthropic marketplace](https://code.claude.com/docs/en/plugins/anthropic-marketplaces.md):
2121 
2222```text theme={null}
2323/plugin install security-guidance@claude-plugins-official
from line 25
2525 
2626`/plugin` opens an interactive panel in the terminal CLI. If Claude replies that `/plugin` isn't available in this environment, install another way:
2727 
28* **Claude desktop app, local or SSH session**: use the **Desktop app** tab under [Install a plugin](/docs/en/plugins/install.md#install-a-plugin)
29* **VS Code extension**: install from the [**Manage plugins** dialog](/docs/en/vs-code.md#manage-plugins)
30* **Cloud sessions**: a cloud session doesn't load plugins from your user settings or from the repository's `.claude/settings.json`, as [What carries over from your setup](/docs/en/cloud-environments.md#what-carries-over-from-your-setup) explains. For plugins your organization distributes through managed settings, see [Manage plugins for your organization](/docs/en/plugins/org.md)
28* **Claude desktop app, local or SSH session**: use the **Desktop app** tab under [Install a plugin](https://code.claude.com/docs/en/plugins/install.md#install-a-plugin)
29* **VS Code extension**: install from the [**Manage plugins** dialog](https://code.claude.com/docs/en/vs-code.md#manage-plugins)
30* **Cloud sessions**: a cloud session doesn't load plugins from your user settings or from the repository's `.claude/settings.json`, as [What carries over from your setup](https://code.claude.com/docs/en/cloud-environments.md#what-carries-over-from-your-setup) explains. For plugins your organization distributes through managed settings, see [Manage plugins for your organization](https://code.claude.com/docs/en/plugins/org.md)
3131 
3232The terminal install prompts for a scope. Choose user scope to write the plugin to your user settings, so it loads in every new local session you start on this machine.
3333 
from line 34
3434If the install fails, match the message Claude Code reports:
3535 
3636* `Marketplace "claude-plugins-official" not found`: add the marketplace with `/plugin marketplace add anthropics/claude-plugins-official`, then retry the install.
37* `Plugin "<name>" not found in marketplace "claude-plugins-official"`: your local copy of the marketplace catalog may be out of date. Refresh it with `/plugin marketplace update claude-plugins-official`, then retry the install. If the retry prints the same message, see [Plugin not found in marketplace](/docs/en/plugins/troubleshooting.md#plugin-not-found-in-marketplace).
37* `Plugin "<name>" not found in marketplace "claude-plugins-official"`: your local copy of the marketplace catalog may be out of date. Refresh it with `/plugin marketplace update claude-plugins-official`, then retry the install. If the retry prints the same message, see [Plugin not found in marketplace](https://code.claude.com/docs/en/plugins/troubleshooting.md#plugin-not-found-in-marketplace).
3838 
39Check the install summary. If it reports `Run /reload-plugins to apply.`, see [Apply plugin changes without restarting](/docs/en/plugins/cli-reference.md#reload-plugins) to activate the plugin in your current session.
39Check the install summary. If it reports `Run /reload-plugins to apply.`, see [Apply plugin changes without restarting](https://code.claude.com/docs/en/plugins/cli-reference.md#reload-plugins) to activate the plugin in your current session.
4040 
4141### Enable for your team in local sessions
4242 
from line 50
5050}
5151```
5252 
53Administrators can enable the plugin organization-wide by setting [`enabledPlugins`](/docs/en/settings-reference#enabledplugins) in [managed settings](/docs/en/admin-setup.md).
53Administrators can enable the plugin organization-wide by setting [`enabledPlugins`](https://code.claude.com/docs/en/settings-reference#enabledplugins) in [managed settings](https://code.claude.com/docs/en/admin-setup.md).
5454 
5555## What the plugin checks
5656 
from line 121
121121- Use `crypto.timingSafeEqual` for token comparison instead of `===`.
122122```
123123 
124These rules are guidance for the reviewer, not deterministic guardrails. The plugin surfaces violations as findings for Claude to fix, but it does not block writes or guarantee every violation is caught. The guidance is additive only: a rule that says to ignore a vulnerability class does not suppress those findings. For hard enforcement, pair the plugin with a [hook that blocks the edit](/docs/en/hooks-guide.md#block-edits-to-protected-files) or a CI check.
124These rules are guidance for the reviewer, not deterministic guardrails. The plugin surfaces violations as findings for Claude to fix, but it does not block writes or guarantee every violation is caught. The guidance is additive only: a rule that says to ignore a vulnerability class does not suppress those findings. For hard enforcement, pair the plugin with a [hook that blocks the edit](https://code.claude.com/docs/en/hooks-guide.md#block-edits-to-protected-files) or a CI check.
125125 
126126### Add custom per-edit patterns
127127 
from line 163
163163 
164164## Usage cost
165165 
166The [per-edit pattern check](#on-each-file-edit) makes no model call and adds no cost. The [end-of-turn](#at-the-end-of-each-turn) and [commit](#on-each-commit-or-push-claude-makes) reviews each spend additional model usage that counts toward your [usage](/docs/en/costs.md) like any other Claude request. The commit review is agentic and may take several model turns per commit. Expect roughly one review call per turn that changes files and one deeper review per commit, both subject to the caps above.
166The [per-edit pattern check](#on-each-file-edit) makes no model call and adds no cost. The [end-of-turn](#at-the-end-of-each-turn) and [commit](#on-each-commit-or-push-claude-makes) reviews each spend additional model usage that counts toward your [usage](https://code.claude.com/docs/en/costs.md) like any other Claude request. The commit review is agentic and may take several model turns per commit. Expect roughly one review call per turn that changes files and one deeper review per commit, both subject to the caps above.
167167 
168168By default, both reviews run on a Claude Opus model. Set `SECURITY_REVIEW_MODEL` to choose a different model for the end-of-turn review and `SG_AGENTIC_MODEL` for the commit review.
169169 
from line 193
193193/plugin uninstall security-guidance@claude-plugins-official
194194```
195195 
196If the plugin was enabled through a project's `.claude/settings.json`, uninstalling it from `/plugin` writes an override to your `.claude/settings.local.json` rather than editing the checked-in file, so the plugin stays off for you while teammates are unaffected. The same dialog also offers to uninstall the plugin for everyone by removing it from the shared `.claude/settings.json`. If it was enabled through [managed settings](/docs/en/admin-setup.md), only an administrator can disable it.
196If the plugin was enabled through a project's `.claude/settings.json`, uninstalling it from `/plugin` writes an override to your `.claude/settings.local.json` rather than editing the checked-in file, so the plugin stays off for you while teammates are unaffected. The same dialog also offers to uninstall the plugin for everyone by removing it from the shared `.claude/settings.json`. If it was enabled through [managed settings](https://code.claude.com/docs/en/admin-setup.md), only an administrator can disable it.
197197 
198198## How the plugin integrates with Claude Code
199199 
200The plugin is built entirely on [hooks](/docs/en/hooks.md), the mechanism for running your own code at specific points in Claude's loop. It registers:
200The plugin is built entirely on [hooks](https://code.claude.com/docs/en/hooks.md), the mechanism for running your own code at specific points in Claude's loop. It registers:
201201 
202202| Hook event | Purpose |
203203| :- | :- |
from line 216
216216| Stage | Tool | What it covers |
217217| :- | :- | :- |
218218| In session | Security guidance plugin | Common vulnerabilities in code Claude writes, fixed in the same session |
219| On demand, single pass | [`/security-review`](/docs/en/commands#all-commands) | One-time security pass on the current branch, run when you ask |
220| On demand, deep scan | [Claude Security plugin](/docs/en/claude-security.md) | Multi-agent vulnerability scan of a repository or diff, with independently reviewed findings and patches |
221| On pull request | [Code Review](/docs/en/code-review.md), Team and Enterprise plans | Multi-agent correctness and security review with full codebase context |
219| On demand, single pass | [`/security-review`](https://code.claude.com/docs/en/commands#all-commands) | One-time security pass on the current branch, run when you ask |
220| On demand, deep scan | [Claude Security plugin](https://code.claude.com/docs/en/claude-security.md) | Multi-agent vulnerability scan of a repository or diff, with independently reviewed findings and patches |
221| On pull request | [Code Review](https://code.claude.com/docs/en/code-review.md), Team and Enterprise plans | Multi-agent correctness and security review with full codebase context |
222222| In CI | Your existing static analysis and dependency scanners | Language-specific rules, supply-chain checks, and policy enforcement the plugin does not attempt |
223223 
224To find security issues in code you already have, rather than in changes Claude is writing, ask Claude in a session to review a specific file or directory for vulnerabilities, or use the [Claude Security plugin](/docs/en/claude-security.md) for a deeper multi-agent scan of the whole repository; [`/security-review`](/docs/en/commands#all-commands) covers only the changes on your current branch. Either way, the review reads the source code in your checkout, not a running site or deployed service.
224To find security issues in code you already have, rather than in changes Claude is writing, ask Claude in a session to review a specific file or directory for vulnerabilities, or use the [Claude Security plugin](https://code.claude.com/docs/en/claude-security.md) for a deeper multi-agent scan of the whole repository; [`/security-review`](https://code.claude.com/docs/en/commands#all-commands) covers only the changes on your current branch. Either way, the review reads the source code in your checkout, not a running site or deployed service.
225225 
226226## Troubleshooting
227227 
from line 237
237237 
238238To go deeper on the pieces this page touches:
239239 
240* [Code Review](/docs/en/code-review.md): set up the PR-time multi-agent review
241* [Automate actions with hooks](/docs/en/hooks-guide.md): build your own checks at the same lifecycle points
242* [Find plugins in the official marketplace](/docs/en/plugins/anthropic-marketplaces.md#find-plugins-in-the-official-marketplace): where to browse the other official plugins
240* [Code Review](https://code.claude.com/docs/en/code-review.md): set up the PR-time multi-agent review
241* [Automate actions with hooks](https://code.claude.com/docs/en/hooks-guide.md): build your own checks at the same lifecycle points
242* [Find plugins in the official marketplace](https://code.claude.com/docs/en/plugins/anthropic-marketplaces.md#find-plugins-in-the-official-marketplace): where to browse the other official plugins
243243 

self-hosted-environments Changed · +31 / -31 lines

from line 6
66>
77> Self-hosted environments are in public beta on Team and Enterprise plans and are off by default. See [Availability and limitations](#availability-and-limitations) for the enablement path and what's excluded.
88 
9A self-hosted environment executes Claude Code cloud sessions on infrastructure your organization operates. A [cloud session](/docs/en/claude-code-on-the-web.md) is any session that runs somewhere other than the developer's machine. Developers start them from claude.ai, the mobile and desktop apps, the terminal with [`claude --cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-cloud), and [cloud scheduled tasks](/docs/en/routines.md). By default, cloud sessions execute on Anthropic's infrastructure.
9A self-hosted environment executes Claude Code cloud sessions on infrastructure your organization operates. A [cloud session](https://code.claude.com/docs/en/claude-code-on-the-web.md) is any session that runs somewhere other than the developer's machine. Developers start them from claude.ai, the mobile and desktop apps, the terminal with [`claude --cloud`](https://code.claude.com/docs/en/claude-code-on-the-web#from-terminal-to-cloud), and [cloud scheduled tasks](https://code.claude.com/docs/en/routines.md). By default, cloud sessions execute on Anthropic's infrastructure.
1010 
11In a self-hosted environment, those same sessions execute inside your network. The developer experience is otherwise the same apart from the differences in [Availability and limitations](#availability-and-limitations) and the deploy page's [known issues](/docs/en/self-hosted-environments-deploy.md#known-issues-and-limitations).
11In a self-hosted environment, those same sessions execute inside your network. The developer experience is otherwise the same apart from the differences in [Availability and limitations](#availability-and-limitations) and the deploy page's [known issues](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#known-issues-and-limitations).
1212 
13If your team doesn't use cloud sessions, there's nothing here to configure: sessions in a terminal or IDE always run on the developer's own machine. If you want to run Claude Code on your own always-on machine and drive it from other devices, use [Remote Control](/docs/en/remote-control.md), which is also available on Pro and Max plans. When you're ready to set up, go straight to the [quickstart](/docs/en/self-hosted-environments-quickstart.md); to review the security posture first, start with [Deploy to production](/docs/en/self-hosted-environments-deploy.md). The rest of this page explains how self-hosting works and when to choose it.
13If your team doesn't use cloud sessions, there's nothing here to configure: sessions in a terminal or IDE always run on the developer's own machine. If you want to run Claude Code on your own always-on machine and drive it from other devices, use [Remote Control](https://code.claude.com/docs/en/remote-control.md), which is also available on Pro and Max plans. When you're ready to set up, go straight to the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md); to review the security posture first, start with [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md). The rest of this page explains how self-hosting works and when to choose it.
1414 
1515## How self-hosted environments work
1616 
from line 20
2020* **Runner**: a program running on hosts inside your network. Runners execute the sessions; the idea is the same as a self-hosted CI runner.
2121* **Session**: one Claude Code task a developer started.
2222 
23When a developer starts a cloud session, the session-start UI shows an environment picker listing Anthropic-hosted environments alongside any your organization has created. If they choose yours, Anthropic's control plane places the session on your environment's queue, where a runner claims it, clones the repository the developer chose, and starts a Claude Code process on your host to run it. The runner authenticates to your git host with credentials you configure; [Configure git](/docs/en/self-hosted-environments-deploy.md#configure-git) covers the options. Sessions reach your internal services from inside your network, and your git host the same way when it's internal; the traffic to Anthropic, queue polling, the session's event stream, and by default model inference, is outbound HTTPS to `api.anthropic.com`, with the short list of further hosts sessions can reach in [Network requirements](/docs/en/self-hosted-environments-deploy.md#network-requirements). Anthropic never connects into your network.
23When a developer starts a cloud session, the session-start UI shows an environment picker listing Anthropic-hosted environments alongside any your organization has created. If they choose yours, Anthropic's control plane places the session on your environment's queue, where a runner claims it, clones the repository the developer chose, and starts a Claude Code process on your host to run it. The runner authenticates to your git host with credentials you configure; [Configure git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#configure-git) covers the options. Sessions reach your internal services from inside your network, and your git host the same way when it's internal; the traffic to Anthropic, queue polling, the session's event stream, and by default model inference, is outbound HTTPS to `api.anthropic.com`, with the short list of further hosts sessions can reach in [Network requirements](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#network-requirements). Anthropic never connects into your network.
2424 
2525![Architecture diagram of a self-hosted environment: your network boundary contains a runner, two Claude Code session processes inside it, and your git host, with api.anthropic.com outside holding queue, session stream, and inference. The runner polls the queue and reaches the git host, each session process opens its own stream, inference, and git connections, and every connection is outbound from your network, with none inbound.](https://mintcdn.com/claude-code/Y0sJ2uDoOVbOVZrQ/images/self-hosted-network-paths.svg?fit=max\&auto=format\&n=Y0sJ2uDoOVbOVZrQ\&q=85\&s=8056103fc1c5564c7f0ef219d260b99d)
2626 
2727The two Claude Code boxes in the diagram are session processes: one runner executing two sessions at once, up to its configured capacity. A runner serves one [owner](#key-concepts) at a time and locks to that owner when it claims its first session, so checked-out code never mixes between owners; [Runner lifecycle](#runner-lifecycle) covers the rule.
2828 
29You can start runners yourself and keep them running, or run the [autoscaling orchestrator](/docs/en/self-hosted-environments-configuration.md#on-demand-runners), a second process you host, which starts runners as sessions queue; each runner exits on its own when its work finishes. Either way, you set the environment up once, and it appears in the picker on every supported surface.
29You can start runners yourself and keep them running, or run the [autoscaling orchestrator](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), a second process you host, which starts runners as sessions queue; each runner exits on its own when its work finishes. Either way, you set the environment up once, and it appears in the picker on every supported surface.
3030 
3131## Availability and limitations
3232 
3333Check these before planning a rollout:
3434 
35* **Plans**: public beta for Team and Enterprise organizations. Self-hosted environments are off by default; an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) turns on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), which requires [cloud sessions](/docs/en/claude-code-on-the-web.md) to be enabled for the organization.
36* **Zero Data Retention and HIPAA**: unavailable for organizations with [Zero Data Retention](/docs/en/zero-data-retention.md) enabled or with the [HIPAA configuration](/docs/en/hipaa-setup.md) applied.
37* **Model inference**: sessions use the Anthropic API unless you configure a runner to [send model requests to Amazon Bedrock or Google Cloud's Agent Platform](/docs/en/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform). Session content goes to Anthropic in both cases. On a runner configured this way, [server-managed settings](/docs/en/server-managed-settings.md) and organization policies from claude.ai don't reach sessions.
38* **Surfaces**: sessions started from [claude.ai/code](https://claude.ai/code), the mobile and desktop apps, [cloud scheduled tasks](/docs/en/routines.md), and the terminal, with [`claude --cloud`](/docs/en/claude-code-on-the-web#from-terminal-to-cloud) or an [`--environment` dispatch](/docs/en/self-hosted-environments-testing#run-the-test-loop), can run in self-hosted environments. [Claude Tag](https://claude.com/docs/claude-tag/overview) sessions can run in them too, but Claude can't use [Access bundles](https://claude.com/docs/claude-tag/concepts/glossary#access-bundle) in those sessions yet. [Claude Security](/docs/en/claude-security.md) and [Code Review](/docs/en/code-review.md) sessions don't route to them yet. Support for those two surfaces follows separately.
39* **Repositories**: sessions check out repositories from GitHub; see [GitHub authentication options](/docs/en/claude-code-on-the-web.md#github-authentication-options). For a GitHub Enterprise Server host, see its [network requirements](/docs/en/github-enterprise-server.md#network-requirements).
35* **Plans**: public beta for Team and Enterprise organizations. Self-hosted environments are off by default; an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) turns on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), which requires [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md) to be enabled for the organization.
36* **Zero Data Retention and HIPAA**: unavailable for organizations with [Zero Data Retention](https://code.claude.com/docs/en/zero-data-retention.md) enabled or with the [HIPAA configuration](https://code.claude.com/docs/en/hipaa-setup.md) applied.
37* **Model inference**: sessions use the Anthropic API unless you configure a runner to [send model requests to Amazon Bedrock or Google Cloud's Agent Platform](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform). Session content goes to Anthropic in both cases. On a runner configured this way, [server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md) and organization policies from claude.ai don't reach sessions.
38* **Surfaces**: sessions started from [claude.ai/code](https://claude.ai/code), the mobile and desktop apps, [cloud scheduled tasks](https://code.claude.com/docs/en/routines.md), and the terminal, with [`claude --cloud`](https://code.claude.com/docs/en/claude-code-on-the-web#from-terminal-to-cloud) or an [`--environment` dispatch](https://code.claude.com/docs/en/self-hosted-environments-testing#run-the-test-loop), can run in self-hosted environments. [Claude Tag](https://claude.com/docs/claude-tag/overview) sessions can run in them too, but Claude can't use [Access bundles](https://claude.com/docs/claude-tag/concepts/glossary#access-bundle) in those sessions yet. [Claude Security](https://code.claude.com/docs/en/claude-security.md) and [Code Review](https://code.claude.com/docs/en/code-review.md) sessions don't route to them yet. Support for those two surfaces follows separately.
39* **Repositories**: sessions check out repositories from GitHub; see [GitHub authentication options](https://code.claude.com/docs/en/claude-code-on-the-web.md#github-authentication-options). For a GitHub Enterprise Server host, see its [network requirements](https://code.claude.com/docs/en/github-enterprise-server.md#network-requirements).
4040* **Billing**: sessions in a self-hosted environment consume your organization's Claude Code usage the same way sessions in Anthropic-hosted environments do.
4141 
4242## Why self-host
from line 64
6464| Runner | The long-lived process you deploy. A runner registers with the environment, receives a runner token, and polls for sessions. |
6565| Session | One Claude Code task, started from claude.ai, the mobile app, or another Anthropic surface such as a scheduled task or an agent. Each session runs as a child Claude Code process the runner spawns. |
6666 
67In API fields, token claims, and metric names, the environment appears as `pool`, and the environment ID is the `pool_id`. The [reference](/docs/en/self-hosted-environments-reference.md) maps the two spellings, including the deprecated `pool` flag names.
67In API fields, token claims, and metric names, the environment appears as `pool`, and the environment ID is the `pool_id`. The [reference](https://code.claude.com/docs/en/self-hosted-environments-reference.md) maps the two spellings, including the deprecated `pool` flag names.
6868 
6969A runner serves one owner at a time. The first session a runner picks up locks the runner to that session's owner, and the runner then runs sessions only for that owner, up to a configured capacity. Who the owner is depends on how the session started:
7070 
from line 86
8686 
8787### Runner lifecycle
8888 
89The first session a runner picks up locks the runner to that session's owner, and the runner runs up to `--capacity` concurrent sessions for that owner. While the runner has active sessions and hasn't received a shutdown signal or reached its retire time, the runner keeps claiming the locked owner's queued work. What happens once they finish depends on [`--drain-grace-sec`](/docs/en/self-hosted-environments-reference#runner-cli-flags):
89The first session a runner picks up locks the runner to that session's owner, and the runner runs up to `--capacity` concurrent sessions for that owner. While the runner has active sessions and hasn't received a shutdown signal or reached its retire time, the runner keeps claiming the locked owner's queued work. What happens once they finish depends on [`--drain-grace-sec`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags):
9090 
9191* **At the default of `0`**: the runner exits as soon as its active sessions finish, without polling for more, so the orchestrator you deploy it under, such as Kubernetes, can restart it with a fresh disk, ready to serve any owner.
9292* **At a positive value**: the runner keeps polling the locked owner's queue for that many seconds before exiting.
from line 93
9393 
9494This lifecycle isolates each owner's checked-out code without requiring the runner to delete disk state between owners.
9595 
96How your infrastructure stops a runner decides whether you need `--retire-at`. A kill that delivers `SIGTERM` needs no flag: the runner drains as [Shutdown timing](/docs/en/self-hosted-environments-deploy.md#shutdown-timing) describes, or keeps serving the sessions it already holds when you set [`--defer-shutdown-max-min`](/docs/en/self-hosted-environments-deploy#defer-the-drain-past-the-first-signal). If your infrastructure instead destroys hosts at a known wall-clock time without a signal, or with a grace period too short to drain, such as a sandbox lifetime cap or spot-instance reclamation, pass `--retire-at <epoch-seconds>` set to a few minutes before that time. At the retire time:
96How your infrastructure stops a runner decides whether you need `--retire-at`. A kill that delivers `SIGTERM` needs no flag: the runner drains as [Shutdown timing](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#shutdown-timing) describes, or keeps serving the sessions it already holds when you set [`--defer-shutdown-max-min`](https://code.claude.com/docs/en/self-hosted-environments-deploy#defer-the-drain-past-the-first-signal). If your infrastructure instead destroys hosts at a known wall-clock time without a signal, or with a grace period too short to drain, such as a sandbox lifetime cap or spot-instance reclamation, pass `--retire-at <epoch-seconds>` set to a few minutes before that time. At the retire time:
9797 
98981. The runner stops taking new work.
992. The runner releases each active session through the same release path the [`--release-idle-session-min`](/docs/en/self-hosted-environments-reference#runner-cli-flags) flag uses, so the session resumes on a fresh runner when the user sends their next message. When the runner releases each session depends on its state:
100 * The runner releases a session that's mid-turn after that turn finishes. It first waits for the session's process to report the turn's end to Anthropic, for no longer than [`SELF_HOSTED_RUNNER_POST_TURN_SETTLE_MS`](/docs/en/self-hosted-environments-reference#environment-variable-only-settings). Before v2.1.280, the runner released the session as soon as the turn finished.
101 * When a turn finishes and leaves background tasks running, the runner waits up to 60 seconds for them, then releases the session even if they're still running. If the tasks have finished but the follow-up turn that reads their results hasn't run yet, the runner keeps the session until that turn finishes, and waits no longer than [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](/docs/en/self-hosted-environments-reference#environment-variable-only-settings) for that turn to start.
992. The runner releases each active session through the same release path the [`--release-idle-session-min`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) flag uses, so the session resumes on a fresh runner when the user sends their next message. When the runner releases each session depends on its state:
100 * The runner releases a session that's mid-turn after that turn finishes. It first waits for the session's process to report the turn's end to Anthropic, for no longer than [`SELF_HOSTED_RUNNER_POST_TURN_SETTLE_MS`](https://code.claude.com/docs/en/self-hosted-environments-reference#environment-variable-only-settings). Before v2.1.280, the runner released the session as soon as the turn finished.
101 * When a turn finishes and leaves background tasks running, the runner waits up to 60 seconds for them, then releases the session even if they're still running. If the tasks have finished but the follow-up turn that reads their results hasn't run yet, the runner keeps the session until that turn finishes, and waits no longer than [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](https://code.claude.com/docs/en/self-hosted-environments-reference#environment-variable-only-settings) for that turn to start.
1021023. The runner exits 0 once all its sessions are released.
103103 
104A turn that outlives the kill is still lost; [Shutdown timing](/docs/en/self-hosted-environments-deploy.md#shutdown-timing) covers sizing the margin. Without `--retire-at`, a signal-less host kill is indistinguishable from a crash: the control plane records a lost worker rather than a clean release, and the session requeues to another runner.
104A turn that outlives the kill is still lost; [Shutdown timing](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#shutdown-timing) covers sizing the margin. Without `--retire-at`, a signal-less host kill is indistinguishable from a crash: the control plane records a lost worker rather than a clean release, and the session requeues to another runner.
105105 
106106### Network paths
107107 
from line 108
108108The runner and its sessions make several kinds of outbound connection, and no inbound connectivity from Anthropic is required:
109109 
110110* **Control plane**: the runner polls `api.anthropic.com` for work and posts setup-progress and failure events, all outbound HTTPS. Polling doubles as the runner's heartbeat.
111* **Git**: the runner clones from and pushes to your git host over HTTPS or SSH, authenticated with credentials your deployment provides. See [Configure git](/docs/en/self-hosted-environments-deploy.md#configure-git) for the options, including per-session minted credentials. With the [Anthropic git proxy](/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy), git traffic for repositories on github.com goes through `api.anthropic.com` instead.
112* **Session child**: the child Claude Code process holds the session's event stream to `api.anthropic.com`, and makes its own outbound calls for model inference and for git commands run during the session. In a session that uses [Anthropic-managed git](/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy), the child sends its `git` and `gh` traffic for github.com over a WebSocket connection it opens to `api.anthropic.com`.
113* **SCM connector**: the optional orchestrator [SCM connector](/docs/en/self-hosted-environments-reference.md#scm-connector-flags) isn't available, so its tunnel doesn't open. The tunnel is a WebSocket connection to `api.anthropic.com`.
111* **Git**: the runner clones from and pushes to your git host over HTTPS or SSH, authenticated with credentials your deployment provides. See [Configure git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#configure-git) for the options, including per-session minted credentials. With the [Anthropic git proxy](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy), git traffic for repositories on github.com goes through `api.anthropic.com` instead.
112* **Session child**: the child Claude Code process holds the session's event stream to `api.anthropic.com`, and makes its own outbound calls for model inference and for git commands run during the session. In a session that uses [Anthropic-managed git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy), the child sends its `git` and `gh` traffic for github.com over a WebSocket connection it opens to `api.anthropic.com`.
113* **SCM connector**: the optional orchestrator [SCM connector](https://code.claude.com/docs/en/self-hosted-environments-reference.md#scm-connector-flags) isn't available, so its tunnel doesn't open. The tunnel is a WebSocket connection to `api.anthropic.com`.
114114 
115See [Network requirements](/docs/en/self-hosted-environments-deploy.md#network-requirements) for the full egress list. The [diagram above](#how-self-hosted-environments-work) shows these paths, apart from the optional SCM connector and the Anthropic-managed git connection.
115See [Network requirements](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#network-requirements) for the full egress list. The [diagram above](#how-self-hosted-environments-work) shows these paths, apart from the optional SCM connector and the Anthropic-managed git connection.
116116 
117By default, model inference uses the Anthropic API. The control plane delivers the API endpoint to each session, and the session authenticates with an Anthropic-issued, session-scoped OAuth token. To send model requests to your own cloud account instead, see [Send model requests to Bedrock or Agent Platform](/docs/en/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform).
117By default, model inference uses the Anthropic API. The control plane delivers the API endpoint to each session, and the session authenticates with an Anthropic-issued, session-scoped OAuth token. To send model requests to your own cloud account instead, see [Send model requests to Bedrock or Agent Platform](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform).
118118 
119Corporate egress proxies are supported. The runner and the optional [autoscaling orchestrator](/docs/en/self-hosted-environments-configuration.md#on-demand-runners) honor the proxy and mTLS environment variables described in [Network configuration](/docs/en/network-config.md), such as `HTTPS_PROXY` and `NO_PROXY`; set them in each process's environment. The variables cover control-plane calls, the orchestrator's [SCM connector](/docs/en/self-hosted-environments-reference.md#scm-connector-flags) WebSocket, and the built-in clone for HTTPS remotes, and sessions inherit them from the runner. Session streaming uses server-sent events over HTTPS, so a proxy in the path must not buffer responses.
119Corporate egress proxies are supported. The runner and the optional [autoscaling orchestrator](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners) honor the proxy and mTLS environment variables described in [Network configuration](https://code.claude.com/docs/en/network-config.md), such as `HTTPS_PROXY` and `NO_PROXY`; set them in each process's environment. The variables cover control-plane calls, the orchestrator's [SCM connector](https://code.claude.com/docs/en/self-hosted-environments-reference.md#scm-connector-flags) WebSocket, and the built-in clone for HTTPS remotes, and sessions inherit them from the runner. Session streaming uses server-sent events over HTTPS, so a proxy in the path must not buffer responses.
120120 
121If your proxy also requires a `Proxy-Authorization` header, the runner can add it to each connection it opens to the proxy; see [Authenticate to an egress proxy](/docs/en/self-hosted-environments-deploy.md#authenticate-to-an-egress-proxy).
121If your proxy also requires a `Proxy-Authorization` header, the runner can add it to each connection it opens to the proxy; see [Authenticate to an egress proxy](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#authenticate-to-an-egress-proxy).
122122 
123123## What stays on your infrastructure
124124 
125Repository checkouts, build artifacts, secrets, and any files a session creates or modifies stay on the machines you provision. The conversation itself, including prompts, responses, and tool results, goes to `api.anthropic.com`, and Anthropic stores the session transcript so you can resume the session from another [supported surface](#availability-and-limitations). When a runner [sends model requests to Amazon Bedrock or Google Cloud's Agent Platform](/docs/en/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform), the conversation still goes to `api.anthropic.com` in the session's event stream.
125Repository checkouts, build artifacts, secrets, and any files a session creates or modifies stay on the machines you provision. The conversation itself, including prompts, responses, and tool results, goes to `api.anthropic.com`, and Anthropic stores the session transcript so you can resume the session from another [supported surface](#availability-and-limitations). When a runner [sends model requests to Amazon Bedrock or Google Cloud's Agent Platform](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform), the conversation still goes to `api.anthropic.com` in the session's event stream.
126126 
127127A self-hosted environment moves session execution into your network. The control plane remains Anthropic-hosted: session orchestration, queueing, and the claude.ai interface continue to run on Anthropic's infrastructure.
128128 
from line 130
130130 
131131The self-hosted environments pages are organized by what you're doing:
132132 
133* [Quickstart](/docs/en/self-hosted-environments-quickstart.md): install Claude Code, create an environment, start a runner, and route your first session
134* [Deploy to production](/docs/en/self-hosted-environments-deploy.md): security hardening, network egress, git credentials, Kubernetes and Compose recipes, known issues, and troubleshooting
135* [Customize sessions](/docs/en/self-hosted-environments-configuration.md): wrapper scripts for per-session credentials, lifecycle hooks, on-demand runners, MCP servers, and permissions
136* [Test end to end](/docs/en/self-hosted-environments-testing.md): a CI smoke test that verifies a runner image before you promote it
137* [Reference](/docs/en/self-hosted-environments-reference.md): every CLI flag, environment variable, metric, and the health endpoint
138* [Verify session identity](/docs/en/self-hosted-environments-identity.md): validate the session token from your own services before granting access
133* [Quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md): install Claude Code, create an environment, start a runner, and route your first session
134* [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md): security hardening, network egress, git credentials, Kubernetes and Compose recipes, known issues, and troubleshooting
135* [Customize sessions](https://code.claude.com/docs/en/self-hosted-environments-configuration.md): wrapper scripts for per-session credentials, lifecycle hooks, on-demand runners, MCP servers, and permissions
136* [Test end to end](https://code.claude.com/docs/en/self-hosted-environments-testing.md): a CI smoke test that verifies a runner image before you promote it
137* [Reference](https://code.claude.com/docs/en/self-hosted-environments-reference.md): every CLI flag, environment variable, metric, and the health endpoint
138* [Verify session identity](https://code.claude.com/docs/en/self-hosted-environments-identity.md): validate the session token from your own services before granting access
139139 

self-hosted-environments-configuration Changed · +108 / -108 lines

from line 4
44 
55> [!NOTE]
66>
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page assumes a working runner; see the [quickstart](/docs/en/self-hosted-environments-quickstart.md) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy.md) for the fleet recipes.
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page assumes a working runner; see the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md) for setup and [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md) for the fleet recipes.
88 
9A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web.md) on your own infrastructure, executed by a runner process you deploy. With no configuration, that runner clones the session's repository, spawns Claude Code, and cleans up. This page is for the platform engineer operating the runners: it covers the extension points for when those defaults don't fit, from per-session credential provisioning to replacing checkout entirely. Wrappers and hooks run as executable files on the runner host, which is Linux or macOS, and the examples on this page assume a POSIX shell.
9A [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md) on your own infrastructure, executed by a runner process you deploy. With no configuration, that runner clones the session's repository, spawns Claude Code, and cleans up. This page is for the platform engineer operating the runners: it covers the extension points for when those defaults don't fit, from per-session credential provisioning to replacing checkout entirely. Wrappers and hooks run as executable files on the runner host, which is Linux or macOS, and the examples on this page assume a POSIX shell.
1010 
1111A few hook environment variables on this page still use `pool`, such as `CLAUDE_RUNNER_POOL_ID`; the CLI flag and env var names use `environment`, such as `--environment-secret-file`.
1212 
from line 24
2424 
2525| Variable | Description |
2626| :- | :- |
27| `CLAUDE_CODE_SESSION_ACCESS_TOKEN` | The session JWT, prefixed `sk-ant-cc-`. Its `act` claim identifies the session creator, with the creator's email when the creating surface recorded it. The value is the token at spawn time; refreshes arrive over the child's stdin, so a wrapper sees only the initial value. See [Verify session identity](/docs/en/self-hosted-environments-identity.md). |
27| `CLAUDE_CODE_SESSION_ACCESS_TOKEN` | The session JWT, prefixed `sk-ant-cc-`. Its `act` claim identifies the session creator, with the creator's email when the creating surface recorded it. The value is the token at spawn time; refreshes arrive over the child's stdin, so a wrapper sees only the initial value. See [Verify session identity](https://code.claude.com/docs/en/self-hosted-environments-identity.md). |
2828| `CCR_SESSION_ACCOUNT_EMAIL` | The session creator's email, pre-extracted by the runner from the token's `act.email` claim without signature verification. Suitable for labelling, such as commit trailers. When the email gates credential issuance, verify the token and read the claim from it instead. See [Provision credentials scoped to the session creator](#provision-credentials-scoped-to-the-session-creator). Unset when the token carries no creator email, for example in sessions your organization's service identity creates. Treat as personally identifiable information. |
2929| `CLAUDE_RUNNER_CLIENT_PLATFORM` | The client surface that created the session, such as `web_claude_ai`, `desktop_app`, `ios`, `claude_code_cli`, or `scheduled_trigger`. Anthropic records the value once at session creation, so the wrapper and every lifecycle hook see the same value. Use it for adoption analytics and labelling only, not as an authorization signal. Unset when the session has no recorded or recognized surface. Requires Claude Code v2.1.229 or later. |
3030| `CLAUDE_RUNNER_CLAUDE_BIN` | Absolute path to the runner's own Claude Code binary. End your wrapper with `exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"` to pass control to the pinned binary without hardcoding an install path. |
from line 33
3333| `CLAUDE_CODE_REMOTE_SLACK_THREAD_URL` | For a [Claude Tag](https://claude.com/docs/claude-tag/overview) session that belongs to one Slack thread, the link to that thread. Unset for other sessions, and can be unset for a thread session too. |
3434| `CLAUDE_CODE_REMOTE_SLACK_THREAD_TS` | For a Claude Tag session that belongs to one Slack thread, that thread's Slack timestamp, such as `1700000000.000100`. Can be unset, and can be set when `CLAUDE_CODE_REMOTE_SLACK_THREAD_URL` isn't, so check each variable on its own. |
3535| `CLAUDE_SESSION_INGRESS_TOKEN_FILE` | Absolute path to a per-session file holding the current session JWT, kept fresh across token refreshes. Shell subprocesses read it for their `Authorization` header when downloading attachments the user added to the session. `exec` preserves the variable automatically; a wrapper that rebuilds the child's environment must carry the variable over, or attachment downloads silently stop working. |
36| `CLAUDE_CONFIG_DIR` | Per-session Claude config directory, written at session start from the snapshot of the runner host's config that the runner captures at startup; see [Permissions and tool approval](#permissions-and-tool-approval). Writes here are isolated to this session. The directory stays under `<base-dir>/_sessions/` after the session ends unless you start the runner with [`--remove-session-state`](/docs/en/self-hosted-environments-reference#runner-cli-flags); see [Reuse a pre-warmed checkout](/docs/en/self-hosted-environments-deploy.md#reuse-a-pre-warmed-checkout). |
37| `CLAUDE_CODE_REMOTE_MCP_CONFIG` | Path to a file in `CLAUDE_CONFIG_DIR` holding the MCP server definitions that Anthropic's control plane delivered for this session. If your wrapper rebuilds the child's environment, carry this variable over. Without it, when the control plane updates the delivered servers, the child shuts down any stdio, HTTP, or SSE server your wrapper adds with [`--mcp-config`](/docs/en/cli-reference#cli-flags) that isn't in the update. Requires Claude Code v2.1.299 or later. |
36| `CLAUDE_CONFIG_DIR` | Per-session Claude config directory, written at session start from the snapshot of the runner host's config that the runner captures at startup; see [Permissions and tool approval](#permissions-and-tool-approval). Writes here are isolated to this session. The directory stays under `<base-dir>/_sessions/` after the session ends unless you start the runner with [`--remove-session-state`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags); see [Reuse a pre-warmed checkout](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#reuse-a-pre-warmed-checkout). |
37| `CLAUDE_CODE_REMOTE_MCP_CONFIG` | Path to a file in `CLAUDE_CONFIG_DIR` holding the MCP server definitions that Anthropic's control plane delivered for this session. If your wrapper rebuilds the child's environment, carry this variable over. Without it, when the control plane updates the delivered servers, the child shuts down any stdio, HTTP, or SSE server your wrapper adds with [`--mcp-config`](https://code.claude.com/docs/en/cli-reference#cli-flags) that isn't in the update. Requires Claude Code v2.1.299 or later. |
3838| `ANTHROPIC_BASE_URL` | The API base URL the child will use, delivered by the control plane per session and normally `https://api.anthropic.com`. Don't override it: the session's inference credential is an Anthropic-issued OAuth token that other providers don't accept. |
3939| `CLAUDE_CODE_OAUTH_TOKEN` | The short-lived OAuth access token the child uses for model inference, with a lifetime of about 30 minutes. The runner re-mints it before expiry and delivers the rotation over the child's stdin, so a wrapper that doesn't [keep stdin attached](#keep-stdin-and-file-descriptor-3-attached) sees only the initial value. Don't rely on your organization's IP allowlist to bound this token's use: treat it as a bearer credential that stays usable for roughly 30 minutes if it leaks, and don't log it, write it to disk, or forward it outside the session container. |
4040 
from line 48
4848 
4949### Quote the arguments and variables your script expands
5050 
51Quote every argument and variable that the runner passes to your wrapper or [`command` hook](#command). Some values contain characters a shell acts on, because they hold JSON, whole sentences, or a [permission rule](/docs/en/permissions.md#permission-rule-syntax) such as `Bash(npm run test *)`.
51Quote every argument and variable that the runner passes to your wrapper or [`command` hook](#command). Some values contain characters a shell acts on, because they hold JSON, whole sentences, or a [permission rule](https://code.claude.com/docs/en/permissions.md#permission-rule-syntax) such as `Bash(npm run test *)`.
5252 
5353* **Arguments**: pass them on as `"$@"`
5454* **Variables**: quote each one, as in `"${CLAUDE_CODE_REMOTE_SLACK_THREAD_URL:-}"`. The Slack thread link, for example, can contain `?` and `&`.
from line 76
7676 
7777### Pass the system prompt flags through
7878 
79The system prompt and appended system prompt that Anthropic's control plane sends for a session reach your wrapper as file paths, not as inline text. The runner writes each prompt to a file in the session's config directory, `CLAUDE_CONFIG_DIR`, and passes its path in the arguments your wrapper receives, as [`--system-prompt-file <path>` or `--append-system-prompt-file <path>`](/docs/en/cli-reference#system-prompt-flags).
79The system prompt and appended system prompt that Anthropic's control plane sends for a session reach your wrapper as file paths, not as inline text. The runner writes each prompt to a file in the session's config directory, `CLAUDE_CONFIG_DIR`, and passes its path in the arguments your wrapper receives, as [`--system-prompt-file <path>` or `--append-system-prompt-file <path>`](https://code.claude.com/docs/en/cli-reference#system-prompt-flags).
8080 
8181Runners on Claude Code v2.1.281 or later deliver the prompts as files. Before v2.1.281, the runner passed them as `--system-prompt <text>` and `--append-system-prompt <text>`.
8282 
from line 87
8787 
8888### Provision credentials scoped to the session creator
8989 
90Use the `decode-token` subcommand to read claims from the session JWT. It reads the token from an argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from stdin, in that order; see [Verify the token inside the session](/docs/en/self-hosted-environments-identity.md#verify-the-token-inside-the-session) for what it checks. The example below decodes the creator identity, exchanges it for short-lived AWS credentials, and execs into Claude Code:
90Use the `decode-token` subcommand to read claims from the session JWT. It reads the token from an argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from stdin, in that order; see [Verify the token inside the session](https://code.claude.com/docs/en/self-hosted-environments-identity.md#verify-the-token-inside-the-session) for what it checks. The example below decodes the creator identity, exchanges it for short-lived AWS credentials, and execs into Claude Code:
9191 
9292```bash theme={null}
9393#!/bin/bash
from line 103
103103exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@"
104104```
105105 
106Use `jq -re` rather than `jq -r` when the extracted claim gates an auth decision, so an absent claim exits non-zero instead of passing the literal string `null` downstream. Sessions created by an organization service identity, such as bot and agent sessions, carry an `agent:` subject rather than `user:`, so this example refuses them; if your environment serves those sessions, decide explicitly whether the wrapper falls back to a default credential for them instead of exiting. When your credential exchange needs the email instead, read `.act.email` and handle its absence: the token carries it only when the creating surface recorded it, and a [CLI-dispatched session](/docs/en/self-hosted-environments-testing.md#run-the-test-loop) can lack it. For the full claim reference and verification from services outside the runner, see [Verify session identity](/docs/en/self-hosted-environments-identity.md).
106Use `jq -re` rather than `jq -r` when the extracted claim gates an auth decision, so an absent claim exits non-zero instead of passing the literal string `null` downstream. Sessions created by an organization service identity, such as bot and agent sessions, carry an `agent:` subject rather than `user:`, so this example refuses them; if your environment serves those sessions, decide explicitly whether the wrapper falls back to a default credential for them instead of exiting. When your credential exchange needs the email instead, read `.act.email` and handle its absence: the token carries it only when the creating surface recorded it, and a [CLI-dispatched session](https://code.claude.com/docs/en/self-hosted-environments-testing.md#run-the-test-loop) can lack it. For the full claim reference and verification from services outside the runner, see [Verify session identity](https://code.claude.com/docs/en/self-hosted-environments-identity.md).
107107 
108108## Lifecycle hooks
109109 
110Lifecycle hooks replace stages of the runner's per-session pipeline with your own scripts. Point the runner at a directory of hooks with `--hooks-dir <path>`, or `SELF_HOSTED_RUNNER_HOOKS_DIR`. The runner looks for executable files with well-known names; any hook that isn't present falls through to the built-in behavior, so you only write the ones you need. Hooks run with the runner's own privileges, and session children share that UID, so mount the hooks directory read-only, or bake it into the image, so session code can't modify it; see the [hardening section](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment).
110Lifecycle hooks replace stages of the runner's per-session pipeline with your own scripts. Point the runner at a directory of hooks with `--hooks-dir <path>`, or `SELF_HOSTED_RUNNER_HOOKS_DIR`. The runner looks for executable files with well-known names; any hook that isn't present falls through to the built-in behavior, so you only write the ones you need. Hooks run with the runner's own privileges, and session children share that UID, so mount the hooks directory read-only, or bake it into the image, so session code can't modify it; see the [hardening section](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment).
111111 
112These hooks are distinct from [Claude Code hooks](/docs/en/hooks.md), which run inside the session; lifecycle hooks run on the runner, around the session.
112These hooks are distinct from [Claude Code hooks](https://code.claude.com/docs/en/hooks.md), which run inside the session; lifecycle hooks run on the runner, around the session.
113113 
114114### checkout
115115 
from line 135
135135 
136136The runner doesn't pass a git credential to the hook. The `decode-token` subcommand isn't available here either, because `CLAUDE_RUNNER_CLAUDE_BIN` isn't set in the checkout-hook environment. Mint a per-session clone credential from the session's identity instead, or fall back to the host's own git authentication:
137137 
138* **Per-session clone credential**: verify `CLAUDE_CODE_SESSION_ACCESS_TOKEN` with a standard JWT library against the JWKS endpoint under `CLAUDE_RUNNER_API_BASE_URL`, as described in [Verify the token from your service](/docs/en/self-hosted-environments-identity.md#verify-the-token-from-your-service). Then have your credential service issue a short-lived clone credential for the identity in the token's `act` claim. Key that credential on `act.sub`, and don't require `act.email`.
138* **Per-session clone credential**: verify `CLAUDE_CODE_SESSION_ACCESS_TOKEN` with a standard JWT library against the JWKS endpoint under `CLAUDE_RUNNER_API_BASE_URL`, as described in [Verify the token from your service](https://code.claude.com/docs/en/self-hosted-environments-identity.md#verify-the-token-from-your-service). Then have your credential service issue a short-lived clone credential for the identity in the token's `act` claim. Key that credential on `act.sub`, and don't require `act.email`.
139139* **Host git authentication**: use whatever git authentication the host already has, such as an SSH agent, credential helper, or `.netrc`.
140140 
141141#### When the hook fails
from line 144
144144 
145145* **A repository the session pushes results to**: the runner fails the session, and on a non-zero exit surfaces the tail of the script's stderr to the user. Write the reason to stderr as one short line, write it last, and then exit non-zero. Don't print secrets to stderr, and remove `set -x` before you deploy the hook.
146146 
147 If git's `couldn't find remote ref` appears in roughly the last 4 KB of the hook's stderr, the runner reports a deleted branch instead. The session's error message then says the branch no longer exists on the remote and asks the user to restore it. If that branch exists, see [A session fails with `no longer exists on the remote` or `couldn't find remote ref`](/docs/en/self-hosted-environments-deploy#no-longer-exists-on-the-remote).
147 If git's `couldn't find remote ref` appears in roughly the last 4 KB of the hook's stderr, the runner reports a deleted branch instead. The session's error message then says the branch no longer exists on the remote and asks the user to restore it. If that branch exists, see [A session fails with `no longer exists on the remote` or `couldn't find remote ref`](https://code.claude.com/docs/en/self-hosted-environments-deploy#no-longer-exists-on-the-remote).
148148* **A repository the session only reads from**, such as a repository added to a running session: the runner skips it and continues with the remaining repositories. The runner posts a `Skipped` step to the session and removes whatever the hook left at the checkout path.
149149 
150 When the `Skipped` step mentions the checkout hook, find the failure detail on the `[runner:warn] checkout hook failed` line in the runner's log. When the step says a source ref `no longer exists on remote`, see [A session fails with `no longer exists on the remote` or `couldn't find remote ref`](/docs/en/self-hosted-environments-deploy#no-longer-exists-on-the-remote). If skipping leaves the session with no repository at all, the runner fails the session anyway.
150 When the `Skipped` step mentions the checkout hook, find the failure detail on the `[runner:warn] checkout hook failed` line in the runner's log. When the step says a source ref `no longer exists on remote`, see [A session fails with `no longer exists on the remote` or `couldn't find remote ref`](https://code.claude.com/docs/en/self-hosted-environments-deploy#no-longer-exists-on-the-remote). If skipping leaves the session with no repository at all, the runner fails the session anyway.
151151 
152152When the hook succeeds, the runner removes the checkout path after the session ends.
153153 
154154### post-session
155155 
156Runs once per session, after the Claude Code child has exited and before the runner tears the workspace down. This hook is your only chance to save uncommitted work: at `--capacity` above one, the runner deletes per-session worktrees right after the hook returns, and at `--capacity 1` the reused [canonical clone](/docs/en/self-hosted-environments-deploy.md#reuse-a-pre-warmed-checkout) is hard-reset when the next session starts, so uncommitted tracked changes don't survive on either path. Typical uses are pushing a snapshot branch of uncommitted changes, archiving logs, or emitting a session-ended event to your own systems.
156Runs once per session, after the Claude Code child has exited and before the runner tears the workspace down. This hook is your only chance to save uncommitted work: at `--capacity` above one, the runner deletes per-session worktrees right after the hook returns, and at `--capacity 1` the reused [canonical clone](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#reuse-a-pre-warmed-checkout) is hard-reset when the next session starts, so uncommitted tracked changes don't survive on either path. Typical uses are pushing a snapshot branch of uncommitted changes, archiving logs, or emitting a session-ended event to your own systems.
157157 
158158The hook fires on every session end where a child process was spawned, whatever the cause; the `CLAUDE_RUNNER_EXIT_REASON` values below enumerate the cases. It can't fire when the runner terminates abruptly, such as a VM preemption or a power loss; if you need guarantees against abrupt termination, snapshot periodically from inside the session with a Claude Code `PostToolUse` hook instead. The runner sets:
159159 
from line 179
179179 * The server moved the session off this runner.
180180 * The runner's poll noticed an archive or delete before the process exited.
181181 * The runner was draining.
182 * The session outlasted its [`--kill-session-after-min`](/docs/en/self-hosted-environments-reference#runner-cli-flags) limit.
182 * The session outlasted its [`--kill-session-after-min`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) limit.
183183* `abandoned`: reserved for a session another runner claimed. The hook doesn't currently fire in that case.
184184 
185If you compare hook receipts with the [session lifecycle counters](/docs/en/self-hosted-environments-reference.md#session-lifecycle-counter-semantics), expect some `interrupted` receipts to count as `completed` there. The counters count a release, a startup timeout, a server move, and an archive or delete that the runner's poll noticed first as `completed`, because the runner handed the slot back cleanly.
185If you compare hook receipts with the [session lifecycle counters](https://code.claude.com/docs/en/self-hosted-environments-reference.md#session-lifecycle-counter-semantics), expect some `interrupted` receipts to count as `completed` there. The counters count a release, a startup timeout, a server move, and an archive or delete that the runner's poll noticed first as `completed`, because the runner handed the slot back cleanly.
186186 
187187The hook's exit status never affects the session outcome; a failure is logged and ignored. The runner waits up to `--post-session-hook-timeout-sec`, 60 seconds by default, on every session end including runner shutdown. This example saves uncommitted work to a rescue branch:
188188 
from line 211
211211 
212212The `GIT_ALLOW_PROTOCOL` line in the script limits git to HTTPS, HTTP, and SSH remotes. If the runner's environment already sets a non-empty `GIT_ALLOW_PROTOCOL` list of its own, the script keeps that list.
213213 
214The hook pushes with whatever git credentials are available in its own environment on the runner host. Under the [no-credentials-in-the-image posture](/docs/en/self-hosted-environments-deploy.md#configure-git), including when the built-in clone goes through the Anthropic git proxy, there are none, so mint a short-lived push credential inside the hook before pushing: exchange the session token the hook receives in `CLAUDE_CODE_SESSION_ACCESS_TOKEN` with your own token service, verifying it as [Verify session identity](/docs/en/self-hosted-environments-identity.md) describes.
214The hook pushes with whatever git credentials are available in its own environment on the runner host. Under the [no-credentials-in-the-image posture](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#configure-git), including when the built-in clone goes through the Anthropic git proxy, there are none, so mint a short-lived push credential inside the hook before pushing: exchange the session token the hook receives in `CLAUDE_CODE_SESSION_ACCESS_TOKEN` with your own token service, verifying it as [Verify session identity](https://code.claude.com/docs/en/self-hosted-environments-identity.md) describes.
215215 
216216Treat any credential your hook gives git as one a session can obtain, and mint it so that it can do no more than this push. Git in your hook reads configuration files that a session can write, and a credential helper or filter driver named in one of them runs with your hook's privileges. Settings in those files can also change where a push goes, whatever remote you name. For the git settings the runner fixes in your hook and the ones it leaves to those files, see [Git configuration inside lifecycle hooks](#git-configuration-inside-lifecycle-hooks).
217217 
from line 222
222222* **Idle after a turn, or timed out at startup**: the runner stops the child and runs this hook to completion. Only then does it release the session. A user message sent while the hook runs can't resume the session on another runner before the hook finishes.
223223* **Waiting for the user to answer a prompt, such as a permission prompt**: the runner releases the session first, then runs this hook. A user message sent while the hook runs can resume the session on another runner before the hook finishes.
224224 
225This applies whenever the runner releases a session: at the idle timeout, at the [`--retire-at`](/docs/en/self-hosted-environments-reference#runner-cli-flags) time, and, on a runner on v2.1.260 or later, at a session's [`--kill-session-after-min`](/docs/en/self-hosted-environments-reference#runner-cli-flags) limit. A session whose turn has ended and that holds only background tasks counts as idle here. Before v2.1.236, the runner released the session first and then ran this hook in both cases.
225This applies whenever the runner releases a session: at the idle timeout, at the [`--retire-at`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) time, and, on a runner on v2.1.260 or later, at a session's [`--kill-session-after-min`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) limit. A session whose turn has ended and that holds only background tasks counts as idle here. Before v2.1.236, the runner released the session first and then ran this hook in both cases.
226226 
227During a `SIGTERM` drain, the runner holds the session lease until the hook finishes; see [Shutdown timing](/docs/en/self-hosted-environments-deploy.md#shutdown-timing).
227During a `SIGTERM` drain, the runner holds the session lease until the hook finishes; see [Shutdown timing](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#shutdown-timing).
228228 
229229### Git configuration inside lifecycle hooks
230230 
from line 235
235235* **Remote protocols**: `GIT_ALLOW_PROTOCOL` is `https:http:ssh`. A clone, fetch, or push that uses a local path, a `file://` URL, or a `git://` URL fails with `fatal: transport 'file' not allowed` or `fatal: transport 'git' not allowed`.
236236* **SSH command and credential prompt**: git in your hook ignores `core.sshCommand` and `core.askPass` from configuration files. To use your own SSH command, set `GIT_SSH_COMMAND` in the runner's environment. To use a credential prompt program, set `GIT_ASKPASS` there. Sessions inherit the runner's environment, so both variables also reach the session's own git. Don't put a credential in either.
237237* **gpg programs**: `gpg.program`, `gpg.openpgp.program`, `gpg.x509.program`, and `gpg.ssh.program` are paths the runner sets, never values from a configuration file.
238* **Commit signing**: with [`--configure-git`](/docs/en/self-hosted-environments-deploy#let-the-runner-configure-git), commits you make from a hook are signed as the session. Without the flag, `commit.gpgsign` and `tag.gpgsign` are `false`.
238* **Commit signing**: with [`--configure-git`](https://code.claude.com/docs/en/self-hosted-environments-deploy#let-the-runner-configure-git), commits you make from a hook are signed as the session. Without the flag, `commit.gpgsign` and `tag.gpgsign` are `false`.
239239 
240240To change one of these settings, use the runner's environment or a `git -c` option inside the hook:
241241 
from line 251
251251 
252252Runs once per session after checkout, in place of the built-in child spawn. The hook receives the same environment as a [wrapper script](#wrapper-scripts) and should `exec` into `"$CLAUDE_RUNNER_CLAUDE_BIN"` the same way. Use the `command` hook to keep all customization in one hooks directory; use `--exec-path` when the wrapper lives elsewhere. If `--exec-path` is also set, the flag takes precedence and the `command` hook is ignored.
253253 
254Always `exec` the runner's own binary rather than a PATH-resolved `claude`; otherwise you defeat [version pinning](/docs/en/self-hosted-environments-deploy.md#pin-the-version).
254Always `exec` the runner's own binary rather than a PATH-resolved `claude`; otherwise you defeat [version pinning](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#pin-the-version).
255255 
256256## On-demand runners
257257 
from line 277
277277| :- | :- |
278278| `CLAUDE_RUNNER_WORK_ORDER_FILE` | Path to a temp file containing the signed work-order JWT the new runner registers with. Deleted after the hook exits. Don't log the file's contents. |
279279| `CLAUDE_RUNNER_ORDER_ID` | Opaque idempotency key, unique per spawn request and safe for Kubernetes resource names. Use only the order ID as your provisioner's dedup key. |
280| `CLAUDE_RUNNER_SESSION_ID` | The session this request is for, as the `session_...` ID in the session's URL. It repeats on every re-request for the session, so use it for logging and routing, not as a dedup key. Empty for pre-warming requests, which boot a standby runner ahead of any specific session when [`--min-idle`](/docs/en/self-hosted-environments-reference#orchestrator-cli-flags) is set, so don't assume the variable is set. |
280| `CLAUDE_RUNNER_SESSION_ID` | The session this request is for, as the `session_...` ID in the session's URL. It repeats on every re-request for the session, so use it for logging and routing, not as a dedup key. Empty for pre-warming requests, which boot a standby runner ahead of any specific session when [`--min-idle`](https://code.claude.com/docs/en/self-hosted-environments-reference#orchestrator-cli-flags) is set, so don't assume the variable is set. |
281281| `CLAUDE_RUNNER_SESSION_UUID` | The same session ID in canonical UUID form. Empty for pre-warming requests. |
282282| `CLAUDE_RUNNER_ATTEMPT` | A per-session counter to use for logging. It isn't a retry count or a request count. `0` for pre-warming requests, though a request for a session can carry `0` too. |
283283| `CLAUDE_RUNNER_ORDER_SERVER_TIME` | Server time from the poll response's HTTP `Date` header. When the hook verifies the work-order JWT's `exp`, compare against this value instead of the local clock to tolerate skew. Empty when the gateway omitted the header. |
from line 292
292292 
293293The spawned runner registers with the work order in place of the environment secret:
294294 
295* **Start it with the work order**: point [`--environment-secret-file`](/docs/en/self-hosted-environments-reference#runner-cli-flags) at a file containing the work-order JWT, or set `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` to the JWT value.
295* **Start it with the work order**: point [`--environment-secret-file`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) at a file containing the work-order JWT, or set `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` to the JWT value.
296296* **Copy the JWT before the hook exits**: the orchestrator deletes the work-order file after the hook exits, so copy the JWT into the workload you submit, such as a Kubernetes Secret on the spawned Job, rather than passing the file path through.
297297* **Use `--capacity 1` on spawned runners**: a session-bound work order registers exactly one runner bound to that session, so a higher capacity adds slots that never receive work, and the runner logs a warning at startup.
298298* **Pre-warming work orders register unbound**: the standby runner isn't bound to a session and claims queued work like a fixed-fleet runner.
from line 305
305305 
306306 * **Exit 0**: submitted.
307307 * **Exit 1, or still running when `--hook-timeout` ends**: retryable failure. The session backs off and is re-offered.
308 * **Exit 2 or higher**: non-retryable failure. The session is blocked from spawning again until a user sends it a new message or an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) selects **Retry** on it in the environment's **Activity** tab.
308 * **Exit 2 or higher**: non-retryable failure. The session is blocked from spawning again until a user sends it a new message or an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) selects **Retry** on it in the environment's **Activity** tab.
309309 
310310 While a session backs off, it shows **Queued** in the **Activity** tab with no spawn error, so read the hook's output in the orchestrator's log. Once blocked, the session shows **Failed**. Expand it to read the spawn error, taken from the last 512 characters of the hook's stderr. Write an actionable error to stderr, and no secrets. In a shell hook, [keep transient failures retryable](#keep-transient-failures-retryable-in-a-shell-hook).
311311 
from line 318
318318 
319319#### Make on-demand runners exit when idle
320320 
321By default, a runner your hook starts doesn't exit when its session's turn finishes, so the workload you submitted keeps running. To make runners exit, have your hook pass these [runner flags](/docs/en/self-hosted-environments-reference.md#runner-cli-flags):
321By default, a runner your hook starts doesn't exit when its session's turn finishes, so the workload you submitted keeps running. To make runners exit, have your hook pass these [runner flags](https://code.claude.com/docs/en/self-hosted-environments-reference.md#runner-cli-flags):
322322 
323323| Flag, with an example value | Pass it to | What it does |
324324| :- | :- | :- |
325| `--release-idle-session-min 30` | Every runner your hook starts | The runner releases its session after that many idle minutes and then exits 0. The session resumes when its user sends their next message, but [work it hadn't pushed is gone](/docs/en/self-hosted-environments-deploy.md#additional-limitations). |
326| `--kill-session-after-min 480` | Every runner your hook starts | Limits each session to that many minutes, as a backstop for [sessions that don't count as idle](/docs/en/self-hosted-environments-deploy.md#some-sessions-don’t-count-as-idle), so set it above your longest expected session |
327| `--exit-if-unused-min 60` | Standby runners, which your hook starts for pre-warming requests | A standby that never takes a session exits after that many minutes. If you set [`--min-idle`](/docs/en/self-hosted-environments-reference#orchestrator-cli-flags), you get a replacement unless the standby was surplus, so use a value well above your boot time. |
325| `--release-idle-session-min 30` | Every runner your hook starts | The runner releases its session after that many idle minutes and then exits 0. The session resumes when its user sends their next message, but [work it hadn't pushed is gone](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#additional-limitations). |
326| `--kill-session-after-min 480` | Every runner your hook starts | Limits each session to that many minutes, as a backstop for [sessions that don't count as idle](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#some-sessions-don’t-count-as-idle), so set it above your longest expected session |
327| `--exit-if-unused-min 60` | Standby runners, which your hook starts for pre-warming requests | A standby that never takes a session exits after that many minutes. If you set [`--min-idle`](https://code.claude.com/docs/en/self-hosted-environments-reference#orchestrator-cli-flags), you get a replacement unless the standby was surplus, so use a value well above your boot time. |
328328 
329329Remove the workload when the runner exits, for example by setting `ttlSecondsAfterFinished` on a Kubernetes Job or by having the VM's start script delete the VM afterwards.
330330 
from line 341
341341* About a second after the hook exits, the orchestrator logs `exited, but a process it started still has the hook's stdout or stderr open`. From that line on, it logs up to 64 KB more of that output under `[runner:hook:spawn-runner:after-exit jti=<order id>]` and then drops the rest.
342342* Once the orchestrator exits, the process's writes there fail.
343343 
344A runner your hook starts itself runs sessions on the orchestrator host, which holds the [environment secret](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment).
344A runner your hook starts itself runs sessions on the orchestrator host, which holds the [environment secret](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment).
345345 
346Before v2.1.305, a hook that had exited still reached [`--hook-timeout`](/docs/en/self-hosted-environments-reference#orchestrator-cli-flags) while a process it started had the hook's stdout or stderr open, shown as:
346Before v2.1.305, a hook that had exited still reached [`--hook-timeout`](https://code.claude.com/docs/en/self-hosted-environments-reference#orchestrator-cli-flags) while a process it started had the hook's stdout or stderr open, shown as:
347347 
348348* **Activity tab**, in a failed session's spawn error: `spawn-runner hook timed out after 60000ms`, or the last lines on the hook's stderr.
349349* **Orchestrator log**: `timed out after 60000ms, sending SIGTERM to process tree`.
from line 350
350350 
351351#### Exit the hook without waiting for the runner
352352 
353If every on-demand runner stops about 60 seconds after its hook starts, and your hook runs again for the same session, check whether the hook waits for the runner. The 60 seconds are the default [`--hook-timeout`](/docs/en/self-hosted-environments-reference#orchestrator-cli-flags), so if you set your own, runners stop that long after the hook starts.
353If every on-demand runner stops about 60 seconds after its hook starts, and your hook runs again for the same session, check whether the hook waits for the runner. The 60 seconds are the default [`--hook-timeout`](https://code.claude.com/docs/en/self-hosted-environments-reference#orchestrator-cli-flags), so if you set your own, runners stop that long after the hook starts.
354354 
355355A hook that starts `claude self-hosted-runner` on the orchestrator's host and waits for it, in the foreground or with `wait`, is still running when `--hook-timeout` ends. The orchestrator then stops the hook and the processes running under it, including that runner and the session's Claude process, so a turn that's still running is cut off.
356356 
from line 361
361361 
362362In a shell hook that uses `set -e`, a failure that a retry could have cleared can block the session. The hook stops at the failing command and exits with that command's own status, and the orchestrator applies the exit-code contract to that status. Many failures return a status of 2 or higher, such as `127` when a command isn't installed and `22` from `curl --fail` on an HTTP error, so they block the session at its first failure.
363363 
364A session the hook has already blocked stays blocked until a user sends it a new message or an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) selects **Retry** on it in the environment's **Activity** tab.
364A session the hook has already blocked stays blocked until a user sends it a new message or an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) selects **Retry** on it in the environment's **Activity** tab.
365365 
366366To turn such a failure into exit 1 instead, put these lines directly below the hook's `#!` line, above anything that can fail:
367367 
from line 400
400400 
401401## Send model requests to Bedrock or Agent Platform
402402 
403If your organization needs model requests to go through its own AWS or Google Cloud account, configure the runner for [Amazon Bedrock](/docs/en/amazon-bedrock.md) or [Google Cloud's Agent Platform, formerly Vertex AI](/docs/en/google-vertex-ai.md). Every session that runner starts then calls the model in your cloud account, with your cloud credentials. Without this configuration, sessions send model requests to the Anthropic API.
403If your organization needs model requests to go through its own AWS or Google Cloud account, configure the runner for [Amazon Bedrock](https://code.claude.com/docs/en/amazon-bedrock.md) or [Google Cloud's Agent Platform, formerly Vertex AI](https://code.claude.com/docs/en/google-vertex-ai.md). Every session that runner starts then calls the model in your cloud account, with your cloud credentials. Without this configuration, sessions send model requests to the Anthropic API.
404404 
405The runner still polls Anthropic for sessions, and each session still sends its event stream to `api.anthropic.com`. The event stream carries prompts, responses, and tool results. The plan requirement and the Zero Data Retention exclusion in [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) still apply.
405The runner still polls Anthropic for sessions, and each session still sends its event stream to `api.anthropic.com`. The event stream carries prompts, responses, and tool results. The plan requirement and the Zero Data Retention exclusion in [Availability and limitations](https://code.claude.com/docs/en/self-hosted-environments#availability-and-limitations) still apply.
406406 
407407Sessions are routed to an environment, not to a runner, and a requeued or resumed session can run on a different runner. Configure every runner in the environment the same way. Before you start, read [what differs on these providers](#what-differs-from-sessions-on-the-anthropic-api).
408408 
from line 410
410410 
411411Set up model access, a narrowly scoped policy or role, and network access:
412412 
413* **Amazon Bedrock**: [submit use case details](/docs/en/amazon-bedrock.md#1-submit-use-case-details), then create the policy in [IAM configuration](/docs/en/amazon-bedrock.md#iam-configuration), limiting `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream` to the inference profiles your sessions use and the foundation models behind them
414* **Agent Platform**: [enable the API](/docs/en/google-vertex-ai.md#1-enable-agent-platform-api) and [request model access](/docs/en/google-vertex-ai.md#2-request-model-access), then create the custom role that [IAM configuration](/docs/en/google-vertex-ai.md#iam-configuration) describes, with only `aiplatform.endpoints.predict`
415* **Egress**: allow your provider's endpoints through your egress rules. See [Network requirements](/docs/en/self-hosted-environments-deploy.md#network-requirements). If sessions can't reach them, Claude Code can keep retrying for hours before the session shows an error.
413* **Amazon Bedrock**: [submit use case details](https://code.claude.com/docs/en/amazon-bedrock.md#1-submit-use-case-details), then create the policy in [IAM configuration](https://code.claude.com/docs/en/amazon-bedrock.md#iam-configuration), limiting `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream` to the inference profiles your sessions use and the foundation models behind them
414* **Agent Platform**: [enable the API](https://code.claude.com/docs/en/google-vertex-ai.md#1-enable-agent-platform-api) and [request model access](https://code.claude.com/docs/en/google-vertex-ai.md#2-request-model-access), then create the custom role that [IAM configuration](https://code.claude.com/docs/en/google-vertex-ai.md#iam-configuration) describes, with only `aiplatform.endpoints.predict`
415* **Egress**: allow your provider's endpoints through your egress rules. See [Network requirements](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#network-requirements). If sessions can't reach them, Claude Code can keep retrying for hours before the session shows an error.
416416 
417417**2. Give sessions narrowly scoped credentials**
418418 
419Attach the policy or role from step 1 to an identity that can do nothing else. For the methods Claude Code accepts, see [Configure AWS credentials](/docs/en/amazon-bedrock.md#2-configure-aws-credentials) and [Configure GCP credentials](/docs/en/google-vertex-ai.md#3-configure-gcp-credentials).
419Attach the policy or role from step 1 to an identity that can do nothing else. For the methods Claude Code accepts, see [Configure AWS credentials](https://code.claude.com/docs/en/amazon-bedrock.md#2-configure-aws-credentials) and [Configure GCP credentials](https://code.claude.com/docs/en/google-vertex-ai.md#3-configure-gcp-credentials).
420420 
421421> [!WARNING]
422422>
423423> Anyone who can get code to run in a session, including through prompt injection, can use these credentials at your cost for as long as the credentials are valid. Claude Code runs inside the session, so the credential it calls the model with has to be readable there.
424424>
425> The shell commands Claude runs, your [Claude Code hooks](/docs/en/hooks.md), and stdio MCP servers inherit the session's environment and run as the same user as Claude Code. As a result, they can read credential variables and credential files.
425> The shell commands Claude runs, your [Claude Code hooks](https://code.claude.com/docs/en/hooks.md), and stdio MCP servers inherit the session's environment and run as the same user as Claude Code. As a result, they can read credential variables and credential files.
426426>
427> When you [harden your deployment](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment), you keep host credentials out of sessions, but you can't keep this credential out. Give the identity behind it nothing beyond the policy or role from step 1.
427> When you [harden your deployment](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment), you keep host credentials out of sessions, but you can't keep this credential out. Give the identity behind it nothing beyond the policy or role from step 1.
428428 
429429Check the method you choose against these runner behaviors:
430430 
431* **Metadata endpoint**: if you deny sessions the [cloud metadata endpoint](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment) outright, credentials served from it, such as an instance profile, don't reach Claude Code either. A file-based web identity, such as IAM Roles for Service Accounts (IRSA) on Amazon EKS or a Workload Identity Federation credential file, doesn't depend on it.
431* **Metadata endpoint**: if you deny sessions the [cloud metadata endpoint](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment) outright, credentials served from it, such as an instance profile, don't reach Claude Code either. A file-based web identity, such as IAM Roles for Service Accounts (IRSA) on Amazon EKS or a Workload Identity Federation credential file, doesn't depend on it.
432432* **Renewal**: a session can outlive a credential, so use a method that renews itself, such as a file-based web identity
433433* **Wrapper script**: the runner starts your [wrapper script](#provision-credentials-scoped-to-the-session-creator) once per session, so credentials it exports aren't renewed. Claude Code reads AWS credentials from its environment, so if your wrapper already exports AWS credentials for other work, Claude Code can sign model requests with them.
434434 
from line 436
436436 
437437Set exactly one provider's variables where you set the runner's other environment variables, such as the container spec or service unit, then restart the runner. The examples show them as shell exports. With [on-demand runners](#on-demand-runners), set them on the workload your `spawn-runner` hook starts.
438438 
439Start these runners with [`--confine-repo-settings enforce`](/docs/en/self-hosted-environments-deploy#harden-your-deployment). It refuses sessions on repositories whose committed settings it flags, so run in the default `warn` mode first and clear what it logs.
439Start these runners with [`--confine-repo-settings enforce`](https://code.claude.com/docs/en/self-hosted-environments-deploy#harden-your-deployment). It refuses sessions on repositories whose committed settings it flags, so run in the default `warn` mode first and clear what it logs.
440440 
441441**Amazon Bedrock**
442442 
from line 447
447447export AWS_REGION=us-east-1
448448```
449449 
450For how Claude Code resolves the region, see [Configure Claude Code](/docs/en/amazon-bedrock.md#3-configure-claude-code). For which inference profile prefix Claude Code uses for your region, see [Cross-region inference profile prefixes](/docs/en/amazon-bedrock.md#cross-region-inference-profile-prefixes).
450For how Claude Code resolves the region, see [Configure Claude Code](https://code.claude.com/docs/en/amazon-bedrock.md#3-configure-claude-code). For which inference profile prefix Claude Code uses for your region, see [Cross-region inference profile prefixes](https://code.claude.com/docs/en/amazon-bedrock.md#cross-region-inference-profile-prefixes).
451451 
452452**Google Cloud's Agent Platform**
453453 
from line 459
459459export ANTHROPIC_VERTEX_PROJECT_ID=YOUR-PROJECT-ID
460460```
461461 
462To choose a region, see [Region configuration](/docs/en/google-vertex-ai.md#region-configuration).
462To choose a region, see [Region configuration](https://code.claude.com/docs/en/google-vertex-ai.md#region-configuration).
463463 
464464**4. Check that the variables reached a session**
465465 
from line 471
471471 
472472A line that sets `CLAUDE_CODE_USE_BEDROCK` or `CLAUDE_CODE_USE_VERTEX` to `1` means the variable reached the session. If both appear, Claude Code uses Amazon Bedrock. No output means neither reached it.
473473 
474The command shows configuration, not traffic. To confirm the requests themselves, look for them in your cloud account's own metrics or request logs. If the first message fails instead, see troubleshooting for [Amazon Bedrock](/docs/en/amazon-bedrock.md#troubleshooting) or [Agent Platform](/docs/en/google-vertex-ai.md#troubleshooting).
474The command shows configuration, not traffic. To confirm the requests themselves, look for them in your cloud account's own metrics or request logs. If the first message fails instead, see troubleshooting for [Amazon Bedrock](https://code.claude.com/docs/en/amazon-bedrock.md#troubleshooting) or [Agent Platform](https://code.claude.com/docs/en/google-vertex-ai.md#troubleshooting).
475475 
476476### What differs from sessions on the Anthropic API
477477 
478478A session that sends model requests to Amazon Bedrock or Google Cloud's Agent Platform differs from a session on the Anthropic API in these ways:
479479 
480* **Policy from claude.ai**: [server-managed settings](/docs/en/server-managed-settings.md) don't reach these sessions. Neither do the organization policies an Owner sets in Claude Code admin settings, so Claude Code doesn't enforce them inside the session. Put the rules you rely on in the runner image's [managed settings file](/docs/en/managed-settings.md#delivery-mechanisms).
480* **Policy from claude.ai**: [server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md) don't reach these sessions. Neither do the organization policies an Owner sets in Claude Code admin settings, so Claude Code doesn't enforce them inside the session. Put the rules you rely on in the runner image's [managed settings file](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms).
481481* **Account skills**: these sessions don't download the skills enabled for a person's claude.ai account. See [How each session's config is assembled](#how-each-session’s-config-is-assembled).
482* **Files**: files that people attach to a session in claude.ai or the mobile or desktop app don't reach it, and Claude can't send files back with the [`SendUserFile` tool](/docs/en/tools-reference). Put input files in the repository or on the runner instead.
482* **Files**: files that people attach to a session in claude.ai or the mobile or desktop app don't reach it, and Claude can't send files back with the [`SendUserFile` tool](https://code.claude.com/docs/en/tools-reference). Put input files in the repository or on the runner instead.
483483* **Model selection**: Anthropic's control plane sends each session's model, and when a session starts without one, Claude Code uses its default for the provider. You can't choose the model with `ANTHROPIC_MODEL` or `ANTHROPIC_DEFAULT_MODEL` in the runner's environment, but you can pin what an alias resolves to:
484484 * **`ANTHROPIC_MODEL` and `ANTHROPIC_DEFAULT_MODEL`**: the runner removes them from the environment it passes to sessions, even though the provider pages' examples set `ANTHROPIC_MODEL`.
485 * **Per-family pinning variables**: the variables in Pin model versions for [Amazon Bedrock](/docs/en/amazon-bedrock.md#4-pin-model-versions) and [Agent Platform](/docs/en/google-vertex-ai.md#5-pin-model-versions) do reach sessions. They decide what an alias such as `opus` resolves to, not what a full model ID resolves to.
486* **Models your account doesn't serve**: a session can fail on a message with an error that names the model. Enable the models your developers can choose, the background model described in Pin model versions, and the classifier model that [auto mode](/docs/en/permission-modes.md#enable-auto-mode-on-bedrock-agent-platform-or-foundry) uses. On Amazon Bedrock, allow each of them in your policy.
487* **Web search and fast mode**: [web search](/docs/en/tools-reference.md#websearch-tool-behavior) isn't available on Amazon Bedrock, and [fast mode](/docs/en/fast-mode.md) isn't available on either provider. For other capabilities that differ by provider, see [CLI capabilities that vary by provider](/docs/en/feature-availability.md#cli-capabilities-that-vary-by-provider).
485 * **Per-family pinning variables**: the variables in Pin model versions for [Amazon Bedrock](https://code.claude.com/docs/en/amazon-bedrock.md#4-pin-model-versions) and [Agent Platform](https://code.claude.com/docs/en/google-vertex-ai.md#5-pin-model-versions) do reach sessions. They decide what an alias such as `opus` resolves to, not what a full model ID resolves to.
486* **Models your account doesn't serve**: a session can fail on a message with an error that names the model. Enable the models your developers can choose, the background model described in Pin model versions, and the classifier model that [auto mode](https://code.claude.com/docs/en/permission-modes.md#enable-auto-mode-on-bedrock-agent-platform-or-foundry) uses. On Amazon Bedrock, allow each of them in your policy.
487* **Web search and fast mode**: [web search](https://code.claude.com/docs/en/tools-reference.md#websearch-tool-behavior) isn't available on Amazon Bedrock, and [fast mode](https://code.claude.com/docs/en/fast-mode.md) isn't available on either provider. For other capabilities that differ by provider, see [CLI capabilities that vary by provider](https://code.claude.com/docs/en/feature-availability.md#cli-capabilities-that-vary-by-provider).
488488 
489489## MCP servers
490490 
491To make [MCP servers](/docs/en/mcp.md) available in every session, add them at image build time with the same `claude mcp add` command used on a desktop install. If your runner is a bare process rather than a container, run the same command as the runner's user on the host, then restart the runner: it reads host config once at startup. The `--scope user` flag is required; the default local scope writes under a per-directory key that the runner doesn't seed into sessions. For example, in your Dockerfile:
491To make [MCP servers](https://code.claude.com/docs/en/mcp.md) available in every session, add them at image build time with the same `claude mcp add` command used on a desktop install. If your runner is a bare process rather than a container, run the same command as the runner's user on the host, then restart the runner: it reads host config once at startup. The `--scope user` flag is required; the default local scope writes under a per-directory key that the runner doesn't seed into sessions. For example, in your Dockerfile:
492492 
493493```dockerfile theme={null}
494494RUN claude mcp add --scope user sidecar -- /usr/local/bin/mcp-sidecar
from line 499
499499 
500500Claude Code also loads MCP servers from other sources:
501501 
502* The enterprise-scope [managed MCP file](/docs/en/managed-mcp.md) at its standard system path: `/etc/claude-code/managed-mcp.json` on Linux runner hosts, `/Library/Application Support/ClaudeCode/managed-mcp.json` on macOS hosts. Use it for locked-down fleets where only administrator-listed servers may load. See [exclusive control with managed-mcp.json](/docs/en/managed-mcp.md#exclusive-control-with-managed-mcp-json) for the precedence rules. When this file is on the runner host, Claude Code skips the MCP servers Anthropic's control plane delivers to a session, including claude.ai connectors, and names them in a warning on the session child's stderr, which the runner records at the `debug` log level. Before v2.1.229, those sessions exited at startup with `You cannot dynamically configure MCP servers when an enterprise MCP config is present`.
503* The [`managedMcpServers`](/docs/en/settings-reference#managedmcpservers) key in [managed settings](/docs/en/managed-settings.md) on the runner host: provides HTTP and SSE servers without taking exclusive control, so servers from the other sources still load. Requires Claude Code v2.1.259 or later.
504* `<repo>/.mcp.json`: project scope. Outside a [project](/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories) thread, commit the file to the repository, and its servers are auto-approved in cloud sessions. In a session with several repositories, [at most one repository's file loads](#repository-settings-in-sessions-with-several-repositories).
505* A [`--mcp-config`](/docs/en/cli-reference#cli-flags) file from your wrapper script: append the flag after `"$@"`, as in `exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --mcp-config /etc/claude/mcp.json`. To keep that file's servers running when the session's delivered servers are updated mid-session, have your wrapper pass the [full environment](#wrapper-scripts) to the child, as `exec` does. Before v2.1.299, the stdio, HTTP, and SSE servers in that file shut down at the first such update that didn't name them.
502* The enterprise-scope [managed MCP file](https://code.claude.com/docs/en/managed-mcp.md) at its standard system path: `/etc/claude-code/managed-mcp.json` on Linux runner hosts, `/Library/Application Support/ClaudeCode/managed-mcp.json` on macOS hosts. Use it for locked-down fleets where only administrator-listed servers may load. See [exclusive control with managed-mcp.json](https://code.claude.com/docs/en/managed-mcp.md#exclusive-control-with-managed-mcp-json) for the precedence rules. When this file is on the runner host, Claude Code skips the MCP servers Anthropic's control plane delivers to a session, including claude.ai connectors, and names them in a warning on the session child's stderr, which the runner records at the `debug` log level. Before v2.1.229, those sessions exited at startup with `You cannot dynamically configure MCP servers when an enterprise MCP config is present`.
503* The [`managedMcpServers`](https://code.claude.com/docs/en/settings-reference#managedmcpservers) key in [managed settings](https://code.claude.com/docs/en/managed-settings.md) on the runner host: provides HTTP and SSE servers without taking exclusive control, so servers from the other sources still load. Requires Claude Code v2.1.259 or later.
504* `<repo>/.mcp.json`: project scope. Outside a [project](https://code.claude.com/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories) thread, commit the file to the repository, and its servers are auto-approved in cloud sessions. In a session with several repositories, [at most one repository's file loads](#repository-settings-in-sessions-with-several-repositories).
505* A [`--mcp-config`](https://code.claude.com/docs/en/cli-reference#cli-flags) file from your wrapper script: append the flag after `"$@"`, as in `exec "$CLAUDE_RUNNER_CLAUDE_BIN" "$@" --mcp-config /etc/claude/mcp.json`. To keep that file's servers running when the session's delivered servers are updated mid-session, have your wrapper pass the [full environment](#wrapper-scripts) to the child, as `exec` does. Before v2.1.299, the stdio, HTTP, and SSE servers in that file shut down at the first such update that didn't name them.
506506 
507When connector delivery is enabled for your organization, Anthropic's control plane delivers the connectors you've configured on claude.ai to interactively-created sessions through server-provided MCP configuration, routed through `api.anthropic.com`. Sessions created programmatically, such as [CLI dispatches](/docs/en/self-hosted-environments-testing.md#run-the-test-loop), don't receive connector delivery; give them MCP servers through any of the other sources this section lists instead. The child's OAuth token doesn't carry a scope for fetching connectors directly, so the child doesn't attempt that fetch itself; delivery is server-driven.
507When connector delivery is enabled for your organization, Anthropic's control plane delivers the connectors you've configured on claude.ai to interactively-created sessions through server-provided MCP configuration, routed through `api.anthropic.com`. Sessions created programmatically, such as [CLI dispatches](https://code.claude.com/docs/en/self-hosted-environments-testing.md#run-the-test-loop), don't receive connector delivery; give them MCP servers through any of the other sources this section lists instead. The child's OAuth token doesn't carry a scope for fetching connectors directly, so the child doesn't attempt that fetch itself; delivery is server-driven.
508508 
509509> [!NOTE]
510510>
511511> In a session that received a connector, a server of the same name from the host's `.claude.json` or a repository's `.mcp.json` isn't connected. To use both, give your server a different name.
512512 
513`settings.json` doesn't carry MCP server definitions, and there is no top-level `mcpServers` field in the settings schema. In managed settings, provide servers with the [`managedMcpServers`](/docs/en/settings-reference#managedmcpservers) key instead.
513`settings.json` doesn't carry MCP server definitions, and there is no top-level `mcpServers` field in the settings schema. In managed settings, provide servers with the [`managedMcpServers`](https://code.claude.com/docs/en/settings-reference#managedmcpservers) key instead.
514514 
515Sessions inherit the runner's environment, so set [`ENABLE_TOOL_SEARCH`](/docs/en/mcp#scale-with-mcp-tool-search) there to control MCP tool search for every session a runner spawns; the MCP page covers the values.
515Sessions inherit the runner's environment, so set [`ENABLE_TOOL_SEARCH`](https://code.claude.com/docs/en/mcp#scale-with-mcp-tool-search) there to control MCP tool search for every session a runner spawns; the MCP page covers the values.
516516 
517517### Wait for MCP servers before the first turn
518518 
519519A self-hosted session waits briefly for MCP servers that are still connecting, at two separate points. A server that misses a wait has its tools missing when the first turn starts, and they become available later with no action on your part. The two waits are:
520520 
521* **Session startup**: before the tool list is first taken, the session waits up to 5 seconds by default for an HTTP or SSE server whose entry sets [`alwaysLoad: true`](/docs/en/mcp#exempt-a-server-from-deferral), or for all servers when you set [`MCP_CONNECTION_NONBLOCKING=0`](/docs/en/env-vars) in the runner's environment. HTTP and SSE servers otherwise connect in the background. While the session waits here, it's slower to initialize. [`MCP_CONNECT_TIMEOUT_MS`](/docs/en/env-vars) changes the 5-second default.
522* **First turn**: after the message arrives, the first turn waits up to 2 seconds for stdio servers that are still connecting. While the session waits here, the first reply is slower. To change how long this wait lasts, set [`CLAUDE_CODE_MCP_STARTUP_WAIT_MS`](/docs/en/env-vars) in the runner's environment. It doesn't change which servers the wait covers. Requires Claude Code v2.1.274 or later.
521* **Session startup**: before the tool list is first taken, the session waits up to 5 seconds by default for an HTTP or SSE server whose entry sets [`alwaysLoad: true`](https://code.claude.com/docs/en/mcp#exempt-a-server-from-deferral), or for all servers when you set [`MCP_CONNECTION_NONBLOCKING=0`](https://code.claude.com/docs/en/env-vars) in the runner's environment. HTTP and SSE servers otherwise connect in the background. While the session waits here, it's slower to initialize. [`MCP_CONNECT_TIMEOUT_MS`](https://code.claude.com/docs/en/env-vars) changes the 5-second default.
522* **First turn**: after the message arrives, the first turn waits up to 2 seconds for stdio servers that are still connecting. While the session waits here, the first reply is slower. To change how long this wait lasts, set [`CLAUDE_CODE_MCP_STARTUP_WAIT_MS`](https://code.claude.com/docs/en/env-vars) in the runner's environment. It doesn't change which servers the wait covers. Requires Claude Code v2.1.274 or later.
523523 
524524`claude mcp add` has no `alwaysLoad` flag. To set the key, add the server with `claude mcp add-json` instead, which takes it in the server's JSON and writes it to `.claude.json`. In your Dockerfile:
525525 
from line 531
531531 
532532### Turn off built-in session tools
533533 
534Anthropic's control plane attaches its own MCP server, named Claude Code Remote, to cloud sessions. Claude uses the server's tools to create [cloud scheduled tasks](/docs/en/routines.md), start and steer other cloud sessions, attach more repositories, and follow pull request activity.
534Anthropic's control plane attaches its own MCP server, named Claude Code Remote, to cloud sessions. Claude uses the server's tools to create [cloud scheduled tasks](https://code.claude.com/docs/en/routines.md), start and steer other cloud sessions, attach more repositories, and follow pull request activity.
535535 
536To turn off the whole server, add a [server-level deny rule](/docs/en/permissions.md#mcp) to your settings. The control plane registers the server under one of three names, depending on how the session was created. Claude Code matches the name in a rule exactly, including case, so write the rule once per name as shown:
536To turn off the whole server, add a [server-level deny rule](https://code.claude.com/docs/en/permissions.md#mcp) to your settings. The control plane registers the server under one of three names, depending on how the session was created. Claude Code matches the name in a rule exactly, including case, so write the rule once per name as shown:
537537 
538538```json theme={null}
539539{
from line 547
547547}
548548```
549549 
550A rule that names the whole server covers tools the server gains later too. To turn off one tool and keep the rest, append two more underscores and the tool name to each rule, as in `mcp__Claude_Code_Remote__add_repo`. To block the server from connecting at all rather than removing its tools, add the three names without the `mcp__` prefix as `serverName` entries under [`deniedMcpServers`](/docs/en/managed-mcp#policy-based-control-with-allowlists-and-denylists) instead.
550A rule that names the whole server covers tools the server gains later too. To turn off one tool and keep the rest, append two more underscores and the tool name to each rule, as in `mcp__Claude_Code_Remote__add_repo`. To block the server from connecting at all rather than removing its tools, add the three names without the `mcp__` prefix as `serverName` entries under [`deniedMcpServers`](https://code.claude.com/docs/en/managed-mcp#policy-based-control-with-allowlists-and-denylists) instead.
551551 
552Put the rules in [server-managed settings](/docs/en/server-managed-settings.md) to reach sessions with no change to the runner, or in `~/.claude/settings.json` on the runner. On a runner that [sends model requests to Bedrock or Agent Platform](#send-model-requests-to-bedrock-or-agent-platform), use that file, because server-managed settings don't reach those sessions. [Permissions and tool approval](#permissions-and-tool-approval) explains how settings on the runner reach sessions.
552Put the rules in [server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md) to reach sessions with no change to the runner, or in `~/.claude/settings.json` on the runner. On a runner that [sends model requests to Bedrock or Agent Platform](#send-model-requests-to-bedrock-or-agent-platform), use that file, because server-managed settings don't reach those sessions. [Permissions and tool approval](#permissions-and-tool-approval) explains how settings on the runner reach sessions.
553553 
554554To confirm the rules took effect, start a session on the environment and ask Claude to list its MCP tools. Claude Code removes a denied tool from Claude's context, so the denied tools are absent from its answer.
555555 
556556## Prompt sessions to push their work
557557 
558Anthropic-hosted sessions run a [`Stop` hook](/docs/en/hooks#stop), the Claude Code hook that runs when Claude finishes responding, that prompts Claude to commit and push its work. The runner doesn't install one. Without it, a session that ends with uncommitted changes leaves that work only on the runner's disk, and the **Create PR** button in claude.ai/code stays inactive until the branch exists on the remote.
558Anthropic-hosted sessions run a [`Stop` hook](https://code.claude.com/docs/en/hooks#stop), the Claude Code hook that runs when Claude finishes responding, that prompts Claude to commit and push its work. The runner doesn't install one. Without it, a session that ends with uncommitted changes leaves that work only on the runner's disk, and the **Create PR** button in claude.ai/code stays inactive until the branch exists on the remote.
559559 
560560The reference implementation below has two parts. Merge the settings block into `~/.claude/settings.json` on the runner host, which the runner seeds into every session, and save the script as `~/.claude/hooks/stop-hook-nudge.sh` on the runner host and make it executable:
561561 
from line 667
667667 
668668The Stop hook only prompts Claude. For the commit and push to work, set up:
669669 
670* **A git identity**: set `user.name` and `user.email` in your runner image, or start the runner with [`--configure-git`](/docs/en/self-hosted-environments-deploy#let-the-runner-configure-git)
671* **Push credentials**: use [Anthropic-managed git](/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy), or supply credentials as [Ship git config in your image](/docs/en/self-hosted-environments-deploy.md#ship-git-config-in-your-image) describes
672* **For resumed sessions**: set [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) on every runner in the environment. A resumed session then starts from the commits that Claude pushed to the session's branch. For the flag's limits, see [Resumed sessions lose unpushed work](/docs/en/self-hosted-environments-deploy.md#additional-limitations).
670* **A git identity**: set `user.name` and `user.email` in your runner image, or start the runner with [`--configure-git`](https://code.claude.com/docs/en/self-hosted-environments-deploy#let-the-runner-configure-git)
671* **Push credentials**: use [Anthropic-managed git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy), or supply credentials as [Ship git config in your image](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#ship-git-config-in-your-image) describes
672* **For resumed sessions**: set [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) on every runner in the environment. A resumed session then starts from the commits that Claude pushed to the session's branch. For the flag's limits, see [Resumed sessions lose unpushed work](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#additional-limitations).
673673 
674674The Stop hook prompts at most once per turn. If the commit or push fails, the turn still ends. Look for git's error in the session and fix what it names.
675675 
from line 687
687687 
688688Search your copy of `stop-hook-nudge.sh` for `GIT_OPTIONAL_LOCKS`. If it's missing, you have an earlier copy. Replace it with the [current script](#prompt-sessions-to-push-their-work) wherever you installed it, then restart the runners. Keep your Stop hook settings, except to raise `timeout` on a large checkout.
689689 
690* **When**: if your sessions run with the [Bash sandbox](/docs/en/sandboxing.md) on, replace it now. Otherwise, replace it at your next image build or runner restart.
690* **When**: if your sessions run with the [Bash sandbox](https://code.claude.com/docs/en/sandboxing.md) on, replace it now. Otherwise, replace it at your next image build or runner restart.
691691* **What changes**: uncommitted files in a submodule no longer prompt Claude. A new commit in a submodule that `.gitmodules` marks `ignore = all` now does.
692692 
693693> [!WARNING]
694694>
695> Hooks run outside the Bash sandbox, so the current script's git calls skip some git features that can start a program named in the checkout's git settings. They don't skip them all. Don't count on the sandbox alone, and isolate sessions as [Harden your deployment](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment) describes.
695> Hooks run outside the Bash sandbox, so the current script's git calls skip some git features that can start a program named in the checkout's git settings. They don't skip them all. Don't count on the sandbox alone, and isolate sessions as [Harden your deployment](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment) describes.
696696 
697697## Permissions and tool approval
698698 
699A self-hosted session has no terminal attached, so an unanswered permission prompt stalls the turn until the user responds in the UI. Anthropic's control plane sends each session's tool list and permission rules with the work payload; the default configuration pre-approves routine tool calls, including `Bash`, and cloud sessions [pre-approve file edits regardless of mode](/docs/en/permission-modes.md#switch-permission-modes). A call that nothing pre-approves prompts through the session UI.
699A self-hosted session has no terminal attached, so an unanswered permission prompt stalls the turn until the user responds in the UI. Anthropic's control plane sends each session's tool list and permission rules with the work payload; the default configuration pre-approves routine tool calls, including `Bash`, and cloud sessions [pre-approve file edits regardless of mode](https://code.claude.com/docs/en/permission-modes.md#switch-permission-modes). A call that nothing pre-approves prompts through the session UI.
700700 
701701> [!NOTE]
702702>
703> Only pin auto mode on an environment whose session containers run with [default-deny network egress](/docs/en/self-hosted-environments-deploy.md#default-deny-egress) and the rest of the [hardening section](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment) in place. Routine tool calls, including `Bash` network requests, run without a human in the loop on both the default pre-approved tool set and in auto mode, so the network boundary is what limits where those calls can reach.
703> Only pin auto mode on an environment whose session containers run with [default-deny network egress](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#default-deny-egress) and the rest of the [hardening section](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment) in place. Routine tool calls, including `Bash` network requests, run without a human in the loop on both the default pre-approved tool set and in auto mode, so the network boundary is what limits where those calls can reach.
704704 
705To keep prompts to a minimum regardless of what the control plane sends, pin [auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) from your wrapper script or [`command` hook](#command). Auto mode lets sessions run without routine permission prompts: a separate classifier model reviews actions before they run and blocks the ones it rejects, and explicit ask rules still force a prompt; the permission modes page covers what the classifier checks. The runner appends server-computed flags before invoking the wrapper, and for single-value flags such as `--permission-mode` the parser honors the last occurrence, so a flag you append after `"$@"` overrides the server-sent value:
705To keep prompts to a minimum regardless of what the control plane sends, pin [auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) from your wrapper script or [`command` hook](#command). Auto mode lets sessions run without routine permission prompts: a separate classifier model reviews actions before they run and blocks the ones it rejects, and explicit ask rules still force a prompt; the permission modes page covers what the classifier checks. The runner appends server-computed flags before invoking the wrapper, and for single-value flags such as `--permission-mode` the parser honors the last occurrence, so a flag you append after `"$@"` overrides the server-sent value:
706706 
707707```bash theme={null}
708708#!/bin/bash
from line 719
719719 
720720Sessions also read these settings files:
721721 
722* **Project settings**: outside a [project](/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories) thread, a repository-committed `.claude/settings.json` layers on top of the user-level baseline. In a session with several repositories, [at most one repository's file takes effect](#repository-settings-in-sessions-with-several-repositories).
723* **Managed settings**: sessions read [`managed-settings.json`](/docs/en/settings#where-settings-live) from the standard system path in your runner image. For whether its keys apply alongside [server-managed settings](/docs/en/server-managed-settings.md), see [how Claude Code combines managed sources](/docs/en/managed-settings.md#how-claude-code-combines-managed-sources).
722* **Project settings**: outside a [project](https://code.claude.com/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories) thread, a repository-committed `.claude/settings.json` layers on top of the user-level baseline. In a session with several repositories, [at most one repository's file takes effect](#repository-settings-in-sessions-with-several-repositories).
723* **Managed settings**: sessions read [`managed-settings.json`](https://code.claude.com/docs/en/settings#where-settings-live) from the standard system path in your runner image. For whether its keys apply alongside [server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md), see [how Claude Code combines managed sources](https://code.claude.com/docs/en/managed-settings.md#how-claude-code-combines-managed-sources).
724724 
725For the order these sources apply in, see [settings precedence](/docs/en/settings.md#settings-precedence).
725For the order these sources apply in, see [settings precedence](https://code.claude.com/docs/en/settings.md#settings-precedence).
726726 
727When Anthropic's control plane supplies a session with [Claude Code hooks](/docs/en/hooks.md), the runner installs them alongside, not over, your own configuration. Requires Claude Code v2.1.229 or later.
727When Anthropic's control plane supplies a session with [Claude Code hooks](https://code.claude.com/docs/en/hooks.md), the runner installs them alongside, not over, your own configuration. Requires Claude Code v2.1.229 or later.
728728 
729729* **Where they land**: the runner writes each supplied hook script to a reserved `hooks/.ccr-launcher/` subdirectory of the session's config directory and registers the scripts in a separate settings file it passes to the session with `--settings`, leaving the seeded `settings.json` and your own scripts at `hooks/<name>` untouched. The runner recreates the reserved subdirectory for each session and doesn't seed host content at `~/.claude/hooks/.ccr-launcher/` into sessions.
730730* **Who authors them**: the control plane populates the scripts from fixed constants in its own deployment, never from per-session or third-party input.
731* **What still governs them**: hooks delivered through `--settings` enter the ordinary merged hook configuration, not the managed tier, so your managed settings still apply. `disableAllHooks` disables them, and they are not among the categories [`allowManagedHooksOnly`](/docs/en/settings-reference#allowmanagedhooksonly) keeps loaded.
731* **What still governs them**: hooks delivered through `--settings` enter the ordinary merged hook configuration, not the managed tier, so your managed settings still apply. `disableAllHooks` disables them, and they are not among the categories [`allowManagedHooksOnly`](https://code.claude.com/docs/en/settings-reference#allowmanagedhooksonly) keeps loaded.
732732 
733When a person starts their own session, Claude Code downloads the [skills enabled for their claude.ai account](/docs/en/skills.md#skills-in-cowork-and-cloud-sessions) into that session's config directory. A [cloud scheduled task](/docs/en/routines.md) run doesn't get its owner's skills, and a session that [sends model requests to Bedrock or Agent Platform](#send-model-requests-to-bedrock-or-agent-platform) doesn't download any. For a skill those sessions need, add it to your runner image or, outside a [project](/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories) thread, commit it to the repository's `.claude/skills/`.
733When a person starts their own session, Claude Code downloads the [skills enabled for their claude.ai account](https://code.claude.com/docs/en/skills.md#skills-in-cowork-and-cloud-sessions) into that session's config directory. A [cloud scheduled task](https://code.claude.com/docs/en/routines.md) run doesn't get its owner's skills, and a session that [sends model requests to Bedrock or Agent Platform](#send-model-requests-to-bedrock-or-agent-platform) doesn't download any. For a skill those sessions need, add it to your runner image or, outside a [project](https://code.claude.com/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories) thread, commit it to the repository's `.claude/skills/`.
734734 
735Outside [Claude Tag](https://claude.com/docs/claude-tag/overview) sessions, a session in a self-hosted environment runs with [auto memory](/docs/en/memory.md#auto-memory) off by default. For instructions that should carry across sessions, use the `CLAUDE.md` in your runner image or in the repository.
735Outside [Claude Tag](https://claude.com/docs/claude-tag/overview) sessions, a session in a self-hosted environment runs with [auto memory](https://code.claude.com/docs/en/memory.md#auto-memory) off by default. For instructions that should carry across sessions, use the `CLAUDE.md` in your runner image or in the repository.
736736 
737737The runner's snapshot of the host's `~/.claude/` leaves out the `projects/` directory. Auto memory's default storage location is under that directory. If you put memory files there, the runner doesn't seed them into sessions, and they don't turn auto memory on.
738738 
739739### Repository settings in sessions with several repositories
740740 
741In a thread in a [project](/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories), no repository's `.claude/settings.json` takes effect as project settings and no repository's `.mcp.json` or skills load, wherever the thread starts. To give those threads a skill, add it to your runner image.
741In a thread in a [project](https://code.claude.com/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories), no repository's `.claude/settings.json` takes effect as project settings and no repository's `.mcp.json` or skills load, wherever the thread starts. To give those threads a skill, add it to your runner image.
742742 
743743In any other session with several repositories, Claude Code reads project settings from the directory the session starts in, so at most one repository's `.claude/settings.json` takes effect as project settings. A hook defined in another repository's file doesn't run, a deny rule in it doesn't apply, and its `env` isn't set.
744744 
745745* **`--capacity 1`, the default, with the built-in checkout**: the session starts in the first repository in its list of repositories. That repository's `.claude/settings.json` takes effect as project settings and its `.mcp.json` loads, and the other repositories' don't.
746* **A `--capacity` above one, or a [`checkout` hook](#checkout)**: the session starts in a per-session directory that contains the checkouts. No repository's `.claude/settings.json` takes effect as project settings, no repository's `.mcp.json` loads, and [`$CLAUDE_PROJECT_DIR`](/docs/en/hooks#reference-scripts-by-path) in a hook command is that directory, not a checkout.
746* **A `--capacity` above one, or a [`checkout` hook](#checkout)**: the session starts in a per-session directory that contains the checkouts. No repository's `.claude/settings.json` takes effect as project settings, no repository's `.mcp.json` loads, and [`$CLAUDE_PROJECT_DIR`](https://code.claude.com/docs/en/hooks#reference-scripts-by-path) in a hook command is that directory, not a checkout.
747747 
748Each repository's `CLAUDE.md` loads wherever the session starts. Outside a project thread, its skills load too. The runner passes every repository to Claude Code as an [additional directory](/docs/en/permissions.md#additional-directories-grant-file-access-not-configuration), so Claude Code also reads the `enabledPlugins` and `extraKnownMarketplaces` keys from each repository's `.claude/settings.json`.
748Each repository's `CLAUDE.md` loads wherever the session starts. Outside a project thread, its skills load too. The runner passes every repository to Claude Code as an [additional directory](https://code.claude.com/docs/en/permissions.md#additional-directories-grant-file-access-not-configuration), so Claude Code also reads the `enabledPlugins` and `extraKnownMarketplaces` keys from each repository's `.claude/settings.json`.
749749 
750To run a hook or apply a permission rule in every session, put it in `~/.claude/settings.json` on the runner host. The runner [seeds the host file into every session](#how-each-session’s-config-is-assembled), wherever the session starts. Write a path in a `Read` or `Edit` rule as a `//` absolute or `~/` home-relative [pattern](/docs/en/permissions.md#read-and-edit), because other patterns anchor at the settings source or the current directory.
750To run a hook or apply a permission rule in every session, put it in `~/.claude/settings.json` on the runner host. The runner [seeds the host file into every session](#how-each-session’s-config-is-assembled), wherever the session starts. Write a path in a `Read` or `Edit` rule as a `//` absolute or `~/` home-relative [pattern](https://code.claude.com/docs/en/permissions.md#read-and-edit), because other patterns anchor at the settings source or the current directory.
751751 
752752### Repository-committed permission rules
753753 
754Don't put a bare `"Edit"`, `"Write"`, or `"NotebookEdit"` entry in a repository-committed `permissions.allow`. A bare file-tool rule matches the tool regardless of path, granting writes anywhere on the host rather than only the workspace, so the runner's write-scope confine guard flags the session; with [`--confine-repo-settings enforce`](/docs/en/self-hosted-environments-reference#runner-cli-flags) it refuses to spawn the session instead of logging and continuing. See the [hardening section](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment).
754Don't put a bare `"Edit"`, `"Write"`, or `"NotebookEdit"` entry in a repository-committed `permissions.allow`. A bare file-tool rule matches the tool regardless of path, granting writes anywhere on the host rather than only the workspace, so the runner's write-scope confine guard flags the session; with [`--confine-repo-settings enforce`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) it refuses to spawn the session instead of logging and continuing. See the [hardening section](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment).
755755 
756A repository needs no file-tool rule at all: cloud sessions [pre-approve file edits regardless of mode](/docs/en/permission-modes.md#switch-permission-modes). If you do commit a rule, scope it to the workspace, such as `"Edit(/**)"`; a single leading slash is relative to the project root, which is the session's workspace. Bare file-tool rules are fine in the operator's host-level `settings.json`, since that file isn't repository-committed.
756A repository needs no file-tool rule at all: cloud sessions [pre-approve file edits regardless of mode](https://code.claude.com/docs/en/permission-modes.md#switch-permission-modes). If you do commit a rule, scope it to the workspace, such as `"Edit(/**)"`; a single leading slash is relative to the project root, which is the session's workspace. Bare file-tool rules are fine in the operator's host-level `settings.json`, since that file isn't repository-committed.
757757 
758A `defaultMode` of `auto` is only honored from the image-wide or user-level settings file, so a checked-out repository can't grant itself auto mode. For which modes cloud sessions accept and the full rule syntax, see [permission modes](/docs/en/permission-modes.md).
758A `defaultMode` of `auto` is only honored from the image-wide or user-level settings file, so a checked-out repository can't grant itself auto mode. For which modes cloud sessions accept and the full rule syntax, see [permission modes](https://code.claude.com/docs/en/permission-modes.md).
759759 
760760## What's next
761761 
762* [Reference](/docs/en/self-hosted-environments-reference.md): every CLI flag, environment variable, and metric
763* [Verify session identity](/docs/en/self-hosted-environments-identity.md): validate the session token from services outside the runner
762* [Reference](https://code.claude.com/docs/en/self-hosted-environments-reference.md): every CLI flag, environment variable, and metric
763* [Verify session identity](https://code.claude.com/docs/en/self-hosted-environments-identity.md): validate the session token from services outside the runner
764764 

self-hosted-environments-deploy Changed · +103 / -103 lines

from line 4
44 
55> [!NOTE]
66>
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page covers running the fleet in production; see the [quickstart](/docs/en/self-hosted-environments-quickstart.md) for your first runner and session.
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](https://code.claude.com/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page covers running the fleet in production; see the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md) for your first runner and session.
88 
9A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web.md) on runners you deploy inside your network, and in production those sessions execute model-directed code on behalf of everyone who can dispatch a session to the environment. This page is for the operator taking a working environment to production. It works through the deployment in order: what to lock down before connecting real systems, the egress the fleet needs, how sessions authenticate to your git host, the deployment recipes themselves, and what to check when sessions misbehave.
9A [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md) on runners you deploy inside your network, and in production those sessions execute model-directed code on behalf of everyone who can dispatch a session to the environment. This page is for the operator taking a working environment to production. It works through the deployment in order: what to lock down before connecting real systems, the egress the fleet needs, how sessions authenticate to your git host, the deployment recipes themselves, and what to check when sessions misbehave.
1010 
1111## Harden your deployment
1212 
1313A self-hosted runner executes arbitrary, model-directed code on your infrastructure on behalf of everyone who can dispatch a session to its environment. That's any member of your Anthropic organization, and anyone who can start a [Claude Tag](https://claude.com/docs/claude-tag/overview) channel session in a scope an Owner routed to the environment. Work through each item before you connect an environment to production systems:
1414 
15* **Ephemeral, per-session containers**: run each runner process in a fresh container or VM that's destroyed when the process exits, with `--capacity 1` and the default `--drain-grace-sec 0` so each container serves exactly one session. At a higher capacity, or with a positive drain grace, one container serves multiple sessions from the same [locked owner](/docs/en/self-hosted-environments#key-concepts); see [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle). Don't reuse a filesystem between runner restarts, except in the deliberate [pre-warmed checkout](#reuse-a-pre-warmed-checkout) setup, and never across owners.
15* **Ephemeral, per-session containers**: run each runner process in a fresh container or VM that's destroyed when the process exits, with `--capacity 1` and the default `--drain-grace-sec 0` so each container serves exactly one session. At a higher capacity, or with a positive drain grace, one container serves multiple sessions from the same [locked owner](https://code.claude.com/docs/en/self-hosted-environments#key-concepts); see [Runner lifecycle](https://code.claude.com/docs/en/self-hosted-environments#runner-lifecycle). Don't reuse a filesystem between runner restarts, except in the deliberate [pre-warmed checkout](#reuse-a-pre-warmed-checkout) setup, and never across owners.
1616 * When the runner stops a session, it sends no signal to a process still running after its shell command exited, such as a service that daemonized. Destroying the container or VM ends that process.
17* **No broad credentials in the image**: don't include long-lived SSH keys, cloud-provider credentials, or personal access tokens that grant more than a session needs. Mint credentials used during a session, such as push or API tokens, per session from your [wrapper script](/docs/en/self-hosted-environments-configuration.md#wrapper-scripts). The initial clone happens before the wrapper runs, so handle it with a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout), or with [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) when all of a session's repositories are on github.com. For both, see [Configure git](#configure-git).
17* **No broad credentials in the image**: don't include long-lived SSH keys, cloud-provider credentials, or personal access tokens that grant more than a session needs. Mint credentials used during a session, such as push or API tokens, per session from your [wrapper script](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts). The initial clone happens before the wrapper runs, so handle it with a [`checkout` lifecycle hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout), or with [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) when all of a session's repositories are on github.com. For both, see [Configure git](#configure-git).
1818* **Keep the host's GitHub credentials away from sessions**: Claude can use any GitHub credential a session can read, with whatever access that credential grants. Keep the runner host's own broadly scoped GitHub credentials out of anything a session can read. Such a credential can be a personal access token, the token `gh auth login` saves for your account, or a `GH_TOKEN` in the runner's environment.
1919 * **With [Anthropic-managed git](#use-the-anthropic-git-proxy)**: with such a credential, Claude reaches GitHub directly instead of through Anthropic-managed git.
2020 * **Without Anthropic-managed git**: a clone credential can stay in the image if you scope it as tightly as [Ship git config in your image](#ship-git-config-in-your-image) describes.
21* **Keep the environment secret off session-running hosts**: the environment secret can register runners and pick up any session queued on the environment. On a fixed fleet it lives on every runner host, where any session's code can read the secret file. Prefer [on-demand runners](/docs/en/self-hosted-environments-configuration.md#on-demand-runners), where the secret stays on the orchestrator host, which never runs user code, and each runner receives a single-use work order that registers exactly one runner. On a fixed fleet, treat the environment-secret file as readable by every session and rotate the secret after any suspected session compromise.
21* **Keep the environment secret off session-running hosts**: the environment secret can register runners and pick up any session queued on the environment. On a fixed fleet it lives on every runner host, where any session's code can read the secret file. Prefer [on-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), where the secret stays on the orchestrator host, which never runs user code, and each runner receives a single-use work order that registers exactly one runner. On a fixed fleet, treat the environment-secret file as readable by every session and rotate the secret after any suspected session compromise.
2222* **Default-deny network egress**: restrict runner and session container outbound traffic at your own network boundary on every environment; [Default-deny egress](#default-deny-egress) covers what to allow and why.
2323* **Least-privilege host IAM**: the compute identity attached to the runner host, such as an instance profile or node service account, should grant only what the runner itself needs. Sessions should obtain their own credentials through your wrapper script rather than inheriting the host's.
2424* **Block the cloud metadata endpoint from sessions**: keeping sessions off the host identity requires blocking their access to the cloud metadata endpoint, and subnet-level egress policies don't intercept link-local metadata traffic, so block it in the container itself:
from line 27
2727 * GKE Workload Identity with metadata concealment
2828 * An explicit deny for `169.254.169.254` in the session container's network namespace
2929 
30 The block applies to your wrapper script and lifecycle hooks too, since they share the container. Authenticate any token exchange with the [session JWT](/docs/en/self-hosted-environments-identity.md) against your own token service over allowlisted egress, or use a file-based web identity such as IAM Roles for Service Accounts (IRSA) on Amazon EKS.
30 The block applies to your wrapper script and lifecycle hooks too, since they share the container. Authenticate any token exchange with the [session JWT](https://code.claude.com/docs/en/self-hosted-environments-identity.md) against your own token service over allowlisted egress, or use a file-based web identity such as IAM Roles for Service Accounts (IRSA) on Amazon EKS.
3131* **Per-runner filesystem isolation**: each runner process gets its own working directory that no other process on the host can read or write. Make `--hooks-dir`, the wrapper script, and the host's `~/.claude/` read-only to the session, either built into the image or mounted read-only.
32* **Dispatch has no per-environment access control**: any member of your Anthropic organization can dispatch a session to any of its environments. If an Owner [routes Claude Tag channels to the environment](/docs/en/cloud-environments.md#set-the-environment-a-claude-tag-channel-uses), anyone the [Claude Tag access setting](https://claude.com/docs/claude-tag/admins/restrict-access#restrict-who-can-use-claude) admits can start channel sessions that run there. By default that's anyone in the connected Slack workspace, with or without a Claude account. Treat every runner host as reachable for code execution by everyone who can dispatch to it, and place on a runner host only data and credentials that all of those people are allowed to read. [`--lock-to-account`](/docs/en/self-hosted-environments-reference#runner-cli-flags) bounds which account's sessions a given host executes, but it doesn't narrow who can dispatch into the environment. To make self-hosted environments the only picker option, an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) can hide Anthropic-hosted environments for the whole organization from the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments).
33* **Enforce the repo-settings guard**: choose the guard mode with [`--confine-repo-settings`](/docs/en/self-hosted-environments-reference#runner-cli-flags). The default `warn` logs a violation and still spawns the session, `enforce` refuses the session, and `off` disables the scan. The runner scans each repository's committed settings for:
32* **Dispatch has no per-environment access control**: any member of your Anthropic organization can dispatch a session to any of its environments. If an Owner [routes Claude Tag channels to the environment](https://code.claude.com/docs/en/cloud-environments.md#set-the-environment-a-claude-tag-channel-uses), anyone the [Claude Tag access setting](https://claude.com/docs/claude-tag/admins/restrict-access#restrict-who-can-use-claude) admits can start channel sessions that run there. By default that's anyone in the connected Slack workspace, with or without a Claude account. Treat every runner host as reachable for code execution by everyone who can dispatch to it, and place on a runner host only data and credentials that all of those people are allowed to read. [`--lock-to-account`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) bounds which account's sessions a given host executes, but it doesn't narrow who can dispatch into the environment. To make self-hosted environments the only picker option, an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) can hide Anthropic-hosted environments for the whole organization from the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments).
33* **Enforce the repo-settings guard**: choose the guard mode with [`--confine-repo-settings`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags). The default `warn` logs a violation and still spawns the session, `enforce` refuses the session, and `off` disables the scan. The runner scans each repository's committed settings for:
3434 
3535 * A grant that resolves outside that session's own workspace: an `additionalDirectories` entry, an `Edit`, `Write`, or `NotebookEdit` rule in `permissions.allow`, or a `sandbox.filesystem.allowWrite` or `allowRead` entry
3636 * A non-empty `env` block
3737 * An operator-posture override such as `sandbox.enabled: false`
3838 
39 The guard runs regardless of [`--trust-workspace`](/docs/en/self-hosted-environments-reference#runner-cli-flags), and doesn't cover repository hooks, `.mcp.json`, or Bash rules; see [Permissions and tool approval](/docs/en/self-hosted-environments-configuration.md#permissions-and-tool-approval) for where those grants belong.
39 The guard runs regardless of [`--trust-workspace`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags), and doesn't cover repository hooks, `.mcp.json`, or Bash rules; see [Permissions and tool approval](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#permissions-and-tool-approval) for where those grants belong.
4040 
4141> [!NOTE]
4242>
43> If your organization has [IP allowlisting](https://support.claude.com/en/articles/13200993-restrict-access-to-claude-with-ip-allowlisting) enabled, add the public egress addresses of your runners and session containers to the allowlist before you start them. If you run [on-demand runners](/docs/en/self-hosted-environments-configuration.md#on-demand-runners), add the orchestrator host's address too. Don't rely on the allowlist as a network control for runner or session traffic. Apply default-deny egress at your own network boundary instead.
43> If your organization has [IP allowlisting](https://support.claude.com/en/articles/13200993-restrict-access-to-claude-with-ip-allowlisting) enabled, add the public egress addresses of your runners and session containers to the allowlist before you start them. If you run [on-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), add the orchestrator host's address too. Don't rely on the allowlist as a network control for runner or session traffic. Apply default-deny egress at your own network boundary instead.
4444 
4545## Network requirements
4646 
from line 50
5050 
5151| Host | Port | Used for |
5252| :- | :- | :- |
53| `api.anthropic.com` | 443, HTTPS; WSS for [Anthropic-managed git](#use-the-anthropic-git-proxy) | Runner control plane and session streaming, model inference, feature flags, product analytics, [JWKS](/docs/en/self-hosted-environments-identity.md) key fetches, commit signing, and Anthropic-managed git when `--use-anthropic-git-proxy` is set |
53| `api.anthropic.com` | 443, HTTPS; WSS for [Anthropic-managed git](#use-the-anthropic-git-proxy) | Runner control plane and session streaming, model inference, feature flags, product analytics, [JWKS](https://code.claude.com/docs/en/self-hosted-environments-identity.md) key fetches, commit signing, and Anthropic-managed git when `--use-anthropic-git-proxy` is set |
5454| Your git host, such as `github.com` or your GitHub Enterprise host | 443 or 22 | Cloning and pushing repositories on each git host the runner's sessions use. On a runner that uses [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), see [when the `github.com` path is still needed](#github-com-egress-with-the-anthropic-git-proxy). |
5555 
5656A runner that uses [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) routes its `github.com` git traffic through `api.anthropic.com`, so it doesn't need the git host path for `github.com`. It still needs that path if you set `--push-outcome-on-release` or push from a `post-session` hook.
from line 62
6262| `downloads.claude.ai` | 443 | At install time, when you install or update Claude Code on the host with the native installer; the `install.sh` script itself is served from `claude.ai`. At session runtime, only when sessions install plugins from the official Anthropic marketplace. |
6363| `storage.googleapis.com` | 443 | At session runtime, for the plugin install counts and metadata shown in `/plugin`. |
6464| `code.claude.com` and `claude.com` | 443 | Documentation lookups by the built-in claude-code-guide agent and pre-approved WebFetch requests during sessions. Blocking these hosts only affects documentation lookups. |
65| `*.frame.claudeusercontent.com` | 443 | Only when the [Artifact tool](/docs/en/artifacts.md#availability) is available for sessions in your organization; defaults vary by plan, per the availability table there. Set `CLAUDE_CODE_DISABLE_ARTIFACT=1` on the runner to keep the tool disabled regardless of the organization setting. |
65| `*.frame.claudeusercontent.com` | 443 | Only when the [Artifact tool](https://code.claude.com/docs/en/artifacts.md#availability) is available for sessions in your organization; defaults vary by plan, per the availability table there. Set `CLAUDE_CODE_DISABLE_ARTIFACT=1` on the runner to keep the tool disabled regardless of the organization setting. |
6666| `registry.npmjs.org` | 443 | When a session installs a plugin, both for fetching npm-source plugin packages and for installing a plugin's Node.js dependencies, or when an `npx`-launched MCP server runs |
6767| `http-intake.logs.us5.datadoghq.com` | 443 | Anthropic operational metrics. Only when `CLAUDE_CODE_BYOC_ENABLE_DATADOG=1` is set; off by default in self-hosted environments. |
68| `browser-intake-us5-datadoghq.com` | 443 | Anthropic error-report uploads, sent only when [error reporting](/docs/en/data-usage.md#telemetry-services) is enabled for the session's account. Suppressed by `DISABLE_ERROR_REPORTING=1` or `DISABLE_TELEMETRY=1`. |
69| Your cloud provider's endpoints for model requests, model lookups, and renewing credentials, such as `bedrock-runtime.us-east-1.amazonaws.com` or `aiplatform.googleapis.com` | 443 | Only when the runner [sends model requests to Amazon Bedrock or Google Cloud's Agent Platform](/docs/en/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform) |
68| `browser-intake-us5-datadoghq.com` | 443 | Anthropic error-report uploads, sent only when [error reporting](https://code.claude.com/docs/en/data-usage.md#telemetry-services) is enabled for the session's account. Suppressed by `DISABLE_ERROR_REPORTING=1` or `DISABLE_TELEMETRY=1`. |
69| Your cloud provider's endpoints for model requests, model lookups, and renewing credentials, such as `bedrock-runtime.us-east-1.amazonaws.com` or `aiplatform.googleapis.com` | 443 | Only when the runner [sends model requests to Amazon Bedrock or Google Cloud's Agent Platform](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#send-model-requests-to-bedrock-or-agent-platform) |
7070 
7171You don't need to allowlist these hosts for runner or session traffic:
7272 
7373* **`statsig.anthropic.com`, `*.sentry.io`, `claude.ai`, and `platform.claude.com`**: these hosts appear in some older enterprise network checklists, but the runner doesn't reach them. Feature-flag fetches go to `api.anthropic.com`, and the runner authenticates with the environment secret rather than interactive OAuth.
74* **`mcp-proxy.anthropic.com`**: self-hosted sessions don't use it. When connector delivery is enabled for your organization, your organization's claude.ai connectors reach sessions through `api.anthropic.com`. See [MCP servers](/docs/en/self-hosted-environments-configuration.md#mcp-servers).
74* **`mcp-proxy.anthropic.com`**: self-hosted sessions don't use it. When connector delivery is enabled for your organization, your organization's claude.ai connectors reach sessions through `api.anthropic.com`. See [MCP servers](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#mcp-servers).
7575 
7676These host-side flows do reach `claude.ai`, so run them from a host whose egress allows it rather than widening session-container egress:
7777 
7878* **The one-line installer**: fetches `install.sh` from `claude.ai` at install time.
79* **Interactive `claude auth login`**: signs in through `claude.ai`, `claude.com`, and `platform.claude.com`. The [guided setup](/docs/en/self-hosted-environments-quickstart.md#run-the-guided-setup), `doctor`'s signed-in mode, and [CI dispatch](/docs/en/self-hosted-environments-testing.md#authenticate-from-ci) use it. The browser you sign in with also loads the claude.ai sign-in page's browser checks from `hcaptcha.com`, `*.hcaptcha.com`, and `challenges.cloudflare.com`. If your organization uses single sign-on, that browser can also be redirected through `sso.claude.com`.
79* **Interactive `claude auth login`**: signs in through `claude.ai`, `claude.com`, and `platform.claude.com`. The [guided setup](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md#run-the-guided-setup), `doctor`'s signed-in mode, and [CI dispatch](https://code.claude.com/docs/en/self-hosted-environments-testing.md#authenticate-from-ci) use it. The browser you sign in with also loads the claude.ai sign-in page's browser checks from `hcaptcha.com`, `*.hcaptcha.com`, and `challenges.cloudflare.com`. If your organization uses single sign-on, that browser can also be redirected through `sso.claude.com`.
8080 
8181### Default-deny egress
8282 
83Deploy runner and session containers in a network segment or namespace whose outbound traffic is limited to the hosts in the [network requirements table](#network-requirements), your git host, and the specific internal services sessions need to reach. The product can't verify or enforce this, so apply it at your own network boundary on every environment. Session code is model-directed and can attempt connections to arbitrary hosts; default-deny egress at the network layer bounds where those attempts can land. This applies regardless of permission mode: the default pre-approved tool set already includes `Bash`, so shell egress runs without a prompt even without [auto mode](/docs/en/self-hosted-environments-configuration.md#permissions-and-tool-approval).
83Deploy runner and session containers in a network segment or namespace whose outbound traffic is limited to the hosts in the [network requirements table](#network-requirements), your git host, and the specific internal services sessions need to reach. The product can't verify or enforce this, so apply it at your own network boundary on every environment. Session code is model-directed and can attempt connections to arbitrary hosts; default-deny egress at the network layer bounds where those attempts can land. This applies regardless of permission mode: the default pre-approved tool set already includes `Bash`, so shell egress runs without a prompt even without [auto mode](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#permissions-and-tool-approval).
8484 
85For details on which telemetry each session emits and how to turn it off, see [Telemetry](/docs/en/self-hosted-environments-reference.md#telemetry).
85For details on which telemetry each session emits and how to turn it off, see [Telemetry](https://code.claude.com/docs/en/self-hosted-environments-reference.md#telemetry).
8686 
8787### Authenticate to an egress proxy
8888 
from line 92
9292 
9393Pick the flag that matches how you produce the `Proxy-Authorization` token:
9494 
95* **[`--proxy-authorization-command <command>`](/docs/en/self-hosted-environments-reference#runner-cli-flags)**: choose this for a token you generate on demand. The runner runs the shell command and uses its trimmed stdout as the header value, for example `Bearer <token>`.
96* **[`--proxy-authorization-file <path>`](/docs/en/self-hosted-environments-reference#runner-cli-flags)**: choose this for a token another process rotates in place. The runner reads the file and uses its trimmed contents as the header value.
95* **[`--proxy-authorization-command <command>`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags)**: choose this for a token you generate on demand. The runner runs the shell command and uses its trimmed stdout as the header value, for example `Bearer <token>`.
96* **[`--proxy-authorization-file <path>`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags)**: choose this for a token another process rotates in place. The runner reads the file and uses its trimmed contents as the header value.
9797 
9898#### Configurations the runner refuses to start with
9999 
100Each flag also has an environment variable form, listed beside it in the [runner CLI flags reference](/docs/en/self-hosted-environments-reference.md#runner-cli-flags). Before the runner contacts your proxy or the control plane, it checks the flags and their variables, and refuses to start in three cases:
100Each flag also has an environment variable form, listed beside it in the [runner CLI flags reference](https://code.claude.com/docs/en/self-hosted-environments-reference.md#runner-cli-flags). Before the runner contacts your proxy or the control plane, it checks the flags and their variables, and refuses to start in three cases:
101101 
102102* **Both flags set**: one flag plus the other flag's environment variable counts as setting both.
103103* **No proxy URL**: neither `HTTPS_PROXY` nor `HTTP_PROXY` holds an `http://` or `https://` URL. The runner reads both variables in upper or lower case, and doesn't consult `ALL_PROXY`.
from line 122
122122 
123123For repositories on github.com, you can also start the runner with [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), or set `CLAUDE_RUNNER_USE_GIT_PROXY=1`, to ask Anthropic to serve git for the runner's sessions.
124124 
125Git version floors on the runner host: [`--configure-git`](#let-the-runner-configure-git) SSH commit signing requires Git 2.34 or later, [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) requires 2.32 or later, and resuming sessions from branches pushed by [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) requires 2.29 or later. Git 2.24 is sufficient if you omit all three and manage git identity yourself.
125Git version floors on the runner host: [`--configure-git`](#let-the-runner-configure-git) SSH commit signing requires Git 2.34 or later, [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy) requires 2.32 or later, and resuming sessions from branches pushed by [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) requires 2.29 or later. Git 2.24 is sufficient if you omit all three and manage git identity yourself.
126126 
127127### Let the runner configure git
128128 
from line 131
131131* `user.name = Claude` and `user.email = [email protected]`, matching Anthropic-hosted sessions
132132* SSH-format commit and tag signing, routed through a runner-managed shim that signs each commit via Anthropic's signing service using the session's own credentials. Signatures are verifiable on GitHub against Anthropic's published SSH signing key.
133133* `push.negotiate = true`, so git asks your git host which commits it already has before packing a push. Requires Claude Code v2.1.257 or later.
134* `core.hooksPath` pointing at a runner-managed hooks directory. Its `commit-msg` and `prepare-commit-msg` hooks add a `Co-authored-by:` trailer for the session's creator to each commit. The trailer is built from the email in [`CCR_SESSION_ACCOUNT_EMAIL`](/docs/en/self-hosted-environments-configuration#wrapper-scripts) and omitted when that variable is unset. If your image already sets `core.hooksPath` and the runner doesn't use [Anthropic-managed git](#use-the-anthropic-git-proxy), the runner keeps your setting, skips installing these hooks, and prints a `[runner:git]` warning.
134* `core.hooksPath` pointing at a runner-managed hooks directory. Its `commit-msg` and `prepare-commit-msg` hooks add a `Co-authored-by:` trailer for the session's creator to each commit. The trailer is built from the email in [`CCR_SESSION_ACCOUNT_EMAIL`](https://code.claude.com/docs/en/self-hosted-environments-configuration#wrapper-scripts) and omitted when that variable is unset. If your image already sets `core.hooksPath` and the runner doesn't use [Anthropic-managed git](#use-the-anthropic-git-proxy), the runner keeps your setting, skips installing these hooks, and prints a `[runner:git]` warning.
135135 
136136Commit signing requires git 2.34 or later; the runner checks at startup and exits with an error if your git is older. This flag doesn't configure push credentials, which you still provide in the image.
137137 
138On a runner on v2.1.280 or later, commits you make from a `checkout` or `post-session` lifecycle hook are signed as the session too, without the `Co-authored-by:` trailer. [Git configuration inside lifecycle hooks](/docs/en/self-hosted-environments-configuration.md#git-configuration-inside-lifecycle-hooks) describes the git settings the runner fixes inside those hooks.
138On a runner on v2.1.280 or later, commits you make from a `checkout` or `post-session` lifecycle hook are signed as the session too, without the `Co-authored-by:` trailer. [Git configuration inside lifecycle hooks](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#git-configuration-inside-lifecycle-hooks) describes the git settings the runner fixes inside those hooks.
139139 
140With or without `--configure-git`, Claude Code instructs Claude to end its commit messages with a `Claude-Session: <url>` trailer and its pull request descriptions with the session's URL. To omit both, set [`attribution.sessionUrl`](/docs/en/settings-reference#attribution-sessionurl) to `false` in the runner host's [`~/.claude/settings.json`](/docs/en/self-hosted-environments-configuration#how-each-session’s-config-is-assembled), then restart the runner.
140With or without `--configure-git`, Claude Code instructs Claude to end its commit messages with a `Claude-Session: <url>` trailer and its pull request descriptions with the session's URL. To omit both, set [`attribution.sessionUrl`](https://code.claude.com/docs/en/settings-reference#attribution-sessionurl) to `false` in the runner host's [`~/.claude/settings.json`](https://code.claude.com/docs/en/self-hosted-environments-configuration#how-each-session’s-config-is-assembled), then restart the runner.
141141 
142142### Ship git config in your image
143143 
from line 150
150150 
151151Without an identity, `git commit` fails with `Please tell me who you are` and sessions can't make progress. You can use your own bot identity instead; the runner doesn't override these values.
152152 
153Don't bake long-lived or broadly scoped push credentials into a shared runner image: a credential in the image is available to every session the image runs, whoever started it. Instead, mint a short-lived, least-scoped token per session from your [wrapper script](/docs/en/self-hosted-environments-configuration.md#wrapper-scripts), using the session creator's identity decoded from the session JWT. Pair it with an ephemeral per-session container, which requires `--capacity 1`, so no credential outlives the session that minted it; see the [hardening section](#harden-your-deployment).
153Don't bake long-lived or broadly scoped push credentials into a shared runner image: a credential in the image is available to every session the image runs, whoever started it. Instead, mint a short-lived, least-scoped token per session from your [wrapper script](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts), using the session creator's identity decoded from the session JWT. Pair it with an ephemeral per-session container, which requires `--capacity 1`, so no credential outlives the session that minted it; see the [hardening section](#harden-your-deployment).
154154 
155155If you must configure push credentials at the image level, for example for a read-only deploy key, scope them as tightly as your git host allows:
156156 
from line 184
184184In exchange, the git proxy limits what the runner supports and changes what it needs:
185185 
186186* **github.com only**: Anthropic serves a session only when all of its repositories are on github.com, and the git proxy doesn't support GitHub Enterprise Server yet. On a runner with the git proxy, a session with a repository on another git host [fails to start](#when-anthropic-doesnt-serve-a-session).
187* **Credentials for the session's repositories only**: Anthropic supplies git credentials for the repositories that are part of the session, not for other repositories on the same git host. A private submodule, a dependency that your package manager fetches with git, or a plugin marketplace in another repository gets no credential from Anthropic. Ask the people who create sessions to [add every repository](/docs/en/web-quickstart.md#start-a-task) a session needs when they create it.
187* **Credentials for the session's repositories only**: Anthropic supplies git credentials for the repositories that are part of the session, not for other repositories on the same git host. A private submodule, a dependency that your package manager fetches with git, or a plugin marketplace in another repository gets no credential from Anthropic. Ask the people who create sessions to [add every repository](https://code.claude.com/docs/en/web-quickstart.md#start-a-task) a session needs when they create it.
188188 
189 Each run of a [cloud scheduled task](/docs/en/routines.md) is a session with the task's repositories, so ask each task's owner to [add every repository its runs need](/docs/en/routines.md#edit-and-control-routines).
190* **Branch pushes only**: a push that deletes a branch fails, and so does a push to any other kind of ref, such as a tag. For which branches a push can update, see [GitHub proxy](/docs/en/cloud-environments.md#github-proxy).
191* **Connected GitHub accounts**: ask each person who creates user sessions with a repository to [connect GitHub](/docs/en/web-quickstart.md#connect-github). Without their own connection, those sessions [don't start](#creator-has-no-github-connection).
189 Each run of a [cloud scheduled task](https://code.claude.com/docs/en/routines.md) is a session with the task's repositories, so ask each task's owner to [add every repository its runs need](https://code.claude.com/docs/en/routines.md#edit-and-control-routines).
190* **Branch pushes only**: a push that deletes a branch fails, and so does a push to any other kind of ref, such as a tag. For which branches a push can update, see [GitHub proxy](https://code.claude.com/docs/en/cloud-environments.md#github-proxy).
191* **Connected GitHub accounts**: ask each person who creates user sessions with a repository to [connect GitHub](https://code.claude.com/docs/en/web-quickstart.md#connect-github). Without their own connection, those sessions [don't start](#creator-has-no-github-connection).
192192* **`--capacity 1`**: the git proxy requires one session per runner process, so run more replicas for parallelism. [Turn the Anthropic git proxy on](#turn-the-anthropic-git-proxy-on) lists the requirements.
193193* **Replaced global git config**: the runner [deletes and replaces the global git config](#git-proxy-replaces-global-git-config) of the user it runs as. Run it as a dedicated user or in a container.
194* **Host credentials for host pushes**: the runner's [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) push and any push your [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session) makes still use the runner host's own git credentials and its [network path to `github.com`](#github-com-egress-with-the-anthropic-git-proxy). Keep those credentials out of the [global git config](#git-proxy-replaces-global-git-config), and see [Ship git config in your image](#ship-git-config-in-your-image) for ways to supply them.
194* **Host credentials for host pushes**: the runner's [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) push and any push your [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session) makes still use the runner host's own git credentials and its [network path to `github.com`](#github-com-egress-with-the-anthropic-git-proxy). Keep those credentials out of the [global git config](#git-proxy-replaces-global-git-config), and see [Ship git config in your image](#ship-git-config-in-your-image) for ways to supply them.
195195* **Per-session decision**: Anthropic decides for each session on the runner whether to serve its git, and a session it doesn't serve fails to start. [When sessions fail to start on a runner with the git proxy](#when-anthropic-doesnt-serve-a-session) covers the causes.
196196 
197197> [!WARNING]
from line 212
212212>
213213> The [Kubernetes](#kubernetes) and [Docker Compose](#docker-compose) recipes on this page use `--capacity 4`. If you add `--use-anthropic-git-proxy` or `CLAUDE_RUNNER_USE_GIT_PROXY=1` to one of them without changing the capacity to `1`, the runner exits at startup every time your orchestrator restarts it. Set `--capacity 1` and run more replicas for parallelism. [When the runner exits](#when-the-runner-exits) shows the line the runner prints.
214214 
215To turn the git proxy on, add `--use-anthropic-git-proxy` to the runner's command, or set `CLAUDE_RUNNER_USE_GIT_PROXY=1` in the runner's environment. This command, run in a shell on the runner host, starts the [quickstart](/docs/en/self-hosted-environments-quickstart.md#set-up-manually)'s runner with the git proxy on:
215To turn the git proxy on, add `--use-anthropic-git-proxy` to the runner's command, or set `CLAUDE_RUNNER_USE_GIT_PROXY=1` in the runner's environment. This command, run in a shell on the runner host, starts the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md#set-up-manually)'s runner with the git proxy on:
216216 
217217```bash theme={null}
218218claude self-hosted-runner --environment-secret-file '/etc/claude/environment-secret' --base-dir '<writable-dir>' --use-anthropic-git-proxy
from line 257
257257 
258258**2. Give the runner git credentials**
259259 
260Provide credentials that work without a prompt for every git host the runners' sessions use, github.com included. Any credential that was in the runner user's global git config is gone, because the runner deleted that config while `--use-anthropic-git-proxy` was set. [Ship credentials in your image](#ship-git-config-in-your-image) or use a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout).
260Provide credentials that work without a prompt for every git host the runners' sessions use, github.com included. Any credential that was in the runner user's global git config is gone, because the runner deleted that config while `--use-anthropic-git-proxy` was set. [Ship credentials in your image](#ship-git-config-in-your-image) or use a [`checkout` lifecycle hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout).
261261 
262262**3. Open the network path**
263263 
from line 280
280280* **Credentials**: the built-in `gh` sends its REST requests through Anthropic-managed git, which supplies the GitHub credential on Anthropic's side, so the image needs no GitHub token for it
281281* **Which sessions get it**: Anthropic decides per session whether Anthropic-managed git serves the session's `gh`. When it does, the `[runner:session] governed git ACTIVE` line that the runner logs for the session shows `gh_path_shim=true`. When it doesn't, the session has no `gh`
282282* **`jq`**: install `jq` in the image if you want `--jq` to work
283* **[`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars)**: if the session environment sets it, Claude Code doesn't provide the built-in `gh`, and the session has no `gh`
283* **[`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](https://code.claude.com/docs/en/env-vars)**: if the session environment sets it, Claude Code doesn't provide the built-in `gh`, and the session has no `gh`
284284 
285285When the image includes the GitHub CLI, sessions use it.
286286 
from line 294
294294* **`GIT_SSL_CAINFO`**: set it to a PEM file of your CAs, for example `GIT_SSL_CAINFO=/etc/ssl/corp-ca.pem`.
295295* **`GIT_SSL_NO_VERIFY`**: doesn't help behind a re-signing proxy. The runner's own clone through Anthropic-managed git checks certificates even when the variable is set, so that clone fails until git trusts your CA through one of the other two approaches.
296296 
297For git connections that carry a session's token to Anthropic-managed git, the runner applies the two variables as follows. A [`command` hook](/docs/en/self-hosted-environments-configuration#command) starts with the session's environment, so it gets what git inside the session gets:
297For git connections that carry a session's token to Anthropic-managed git, the runner applies the two variables as follows. A [`command` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#command) starts with the session's environment, so it gets what git inside the session gets:
298298 
299299* **`GIT_SSL_CAINFO`**: what git checks Anthropic-managed git against depends on where git runs:
300300 * **Runner's own clone and fetches**: run without the variable and check Anthropic-managed git against a per-session certificate file the runner writes. The runner builds that file from the runner host's system CA bundle and the certificates in your file.
from line 311
311311 
312312### Rewrite git URLs for private networks
313313 
314Repository URLs arrive from the control plane as HTTPS, with the hostname of your git host; for GitHub Enterprise, that's the hostname you configured for the [GitHub Enterprise integration](/docs/en/github-enterprise-server.md) on claude.ai. Two repeatable flags rewrite those URLs before clone:
314Repository URLs arrive from the control plane as HTTPS, with the hostname of your git host; for GitHub Enterprise, that's the hostname you configured for the [GitHub Enterprise integration](https://code.claude.com/docs/en/github-enterprise-server.md) on claude.ai. Two repeatable flags rewrite those URLs before clone:
315315 
316316* `--git-host-rewrite <from>=<to>`: for split-horizon DNS, where Anthropic reaches your git host via an external hostname but runners must use an internal one
317317* `--git-ssh-rewrite <host>`: for git hosts that only accept SSH, or a runner host that has only an SSH key. For example, to clone github.com repositories over SSH, add `--git-ssh-rewrite github.com` to the runner command. The runner then rewrites `https://github.com/owner/repo` to `[email protected]:owner/repo`. SSH must [connect without a prompt](#git-must-work-without-a-prompt).
318318 
319Host rewriting runs first, so list the internal hostname in `--git-ssh-rewrite` if you need both. For full control over checkout, use a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout).
319Host rewriting runs first, so list the internal hostname in `--git-ssh-rewrite` if you need both. For full control over checkout, use a [`checkout` lifecycle hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout).
320320 
321321## Build the runner image
322322 
323Anthropic doesn't publish a pre-built runner image. Build your own around the `claude` binary, layering in whatever toolchain your repositories need: language runtimes, compilers, package managers, and [MCP](/docs/en/mcp.md) sidecars.
323Anthropic doesn't publish a pre-built runner image. Build your own around the `claude` binary, layering in whatever toolchain your repositories need: language runtimes, compilers, package managers, and [MCP](https://code.claude.com/docs/en/mcp.md) sidecars.
324324 
325The recipes below use `--capacity 4`, so one container serves up to four concurrent sessions from the same locked owner. That doesn't provide the per-session container isolation in the [hardening section](#harden-your-deployment): before connecting an environment to production systems, either run the recipes at `--capacity 1` with one container per session, or use [on-demand runners](/docs/en/self-hosted-environments-configuration.md#on-demand-runners), which also keep the environment secret off session-running hosts. If you add the [Anthropic git proxy](#use-the-anthropic-git-proxy) to one of these recipes, also change `--capacity` to `1`.
325The recipes below use `--capacity 4`, so one container serves up to four concurrent sessions from the same locked owner. That doesn't provide the per-session container isolation in the [hardening section](#harden-your-deployment): before connecting an environment to production systems, either run the recipes at `--capacity 1` with one container per session, or use [on-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), which also keep the environment secret off session-running hosts. If you add the [Anthropic git proxy](#use-the-anthropic-git-proxy) to one of these recipes, also change `--capacity` to `1`.
326326 
327327This Dockerfile is a minimal starting point:
328328 
from line 339
339339ENTRYPOINT ["claude"]
340340```
341341 
342Swap `linux-x64` for `linux-arm64` if your nodes are ARM, or for `linux-x64-musl` or `linux-arm64-musl` on a musl-based image such as Alpine; see [Alpine Linux setup](/docs/en/setup.md#alpine-linux-and-musl-based-distributions) for the extra packages musl images need. The URL is the standard Claude Code release location, so you can verify the downloaded binary against the release's signed manifest as described in [Binary integrity and code signing](/docs/en/setup.md#binary-integrity-and-code-signing). The runner requires Claude Code version 2.1.224 or later. Build the image, then push it to your registry and reference it in the recipes below:
342Swap `linux-x64` for `linux-arm64` if your nodes are ARM, or for `linux-x64-musl` or `linux-arm64-musl` on a musl-based image such as Alpine; see [Alpine Linux setup](https://code.claude.com/docs/en/setup.md#alpine-linux-and-musl-based-distributions) for the extra packages musl images need. The URL is the standard Claude Code release location, so you can verify the downloaded binary against the release's signed manifest as described in [Binary integrity and code signing](https://code.claude.com/docs/en/setup.md#binary-integrity-and-code-signing). The runner requires Claude Code version 2.1.224 or later. Build the image, then push it to your registry and reference it in the recipes below:
343343 
344344```bash theme={null}
345345docker build \
from line 347
347347 -t <your-registry>/claude-runner:latest .
348348```
349349 
350The command substitution looks up the current `stable` release number and passes it as the build argument, so running the same command after a new stable release rebuilds the download layer with the newer binary. To pin a specific release for reproducible builds, pass the version number directly as `CLAUDE_CODE_VERSION`. Replace `stable` with `latest` in the lookup URL when you need a release newer than the stable channel, such as one a [newly launched model requires](/docs/en/model-config.md).
350The command substitution looks up the current `stable` release number and passes it as the build argument, so running the same command after a new stable release rebuilds the download layer with the newer binary. To pin a specific release for reproducible builds, pass the version number directly as `CLAUDE_CODE_VERSION`. Replace `stable` with `latest` in the lookup URL when you need a release newer than the stable channel, such as one a [newly launched model requires](https://code.claude.com/docs/en/model-config.md).
351351 
352352## Size CPU and memory for sessions
353353 
354Size a runner's container or host for the sessions it runs rather than for the runner process. The runner itself polls for work, prepares each session's checkout, runs your [lifecycle hooks](/docs/en/self-hosted-environments-configuration.md#lifecycle-hooks), and starts and supervises the session processes. The load comes from the sessions: each one is a Claude Code process plus whatever it starts, such as builds, test suites, package installs, and [MCP servers](/docs/en/mcp.md).
354Size a runner's container or host for the sessions it runs rather than for the runner process. The runner itself polls for work, prepares each session's checkout, runs your [lifecycle hooks](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#lifecycle-hooks), and starts and supervises the session processes. The load comes from the sessions: each one is a Claude Code process plus whatever it starts, such as builds, test suites, package installs, and [MCP servers](https://code.claude.com/docs/en/mcp.md).
355355 
356356For one session, start with the following values, stated as Kubernetes requests and limits or your platform's equivalent, and treat them as a starting point rather than a requirement:
357357 
358* **Memory**: a request and a limit of 4 GiB each, which meets the 4 GB minimum in Claude Code's [system requirements](/docs/en/setup.md#system-requirements). Keep the two equal so the scheduler accounts for the container's full memory. When the container reaches its memory limit, the kernel kills processes inside it, which can end a session mid-task.
358* **Memory**: a request and a limit of 4 GiB each, which meets the 4 GB minimum in Claude Code's [system requirements](https://code.claude.com/docs/en/setup.md#system-requirements). Keep the two equal so the scheduler accounts for the container's full memory. When the container reaches its memory limit, the kernel kills processes inside it, which can end a session mid-task.
359359* **CPU**: a request of 2 CPUs and a limit of 4 CPUs, so a session can burst above the request during builds. The kernel throttles a container at its CPU limit rather than killing processes in it, so sessions at the limit run slower but keep running.
360360 
361361In a Kubernetes container spec, set those starting values with the following `resources` block:
from line 372
372372 
373373Builds and tests are usually the largest and most variable part of a session's load, so run a representative build of your repository, measure its peak CPU and memory, and raise any starting value that leaves no room for the Claude Code process on top of that peak.
374374 
375The runner uses `--capacity` to cap how many sessions it runs at once. It doesn't divide CPU or memory between them, so the sessions on a runner share the container's CPU and memory. To cap one session's share, apply limits from your [wrapper script](/docs/en/self-hosted-environments-configuration.md#wrapper-scripts). What to give one container therefore depends on how many sessions it serves at once:
375The runner uses `--capacity` to cap how many sessions it runs at once. It doesn't divide CPU or memory between them, so the sessions on a runner share the container's CPU and memory. To cap one session's share, apply limits from your [wrapper script](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts). What to give one container therefore depends on how many sessions it serves at once:
376376 
377* **One session per runner**: give each container one session's values. Use this sizing at `--capacity 1`, which the [hardening section](#harden-your-deployment) recommends, and for [on-demand runners](/docs/en/self-hosted-environments-configuration.md#on-demand-runners), where you set the values on the workload your [`spawn-runner` hook](/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook) submits, such as a Kubernetes Job's pod template.
377* **One session per runner**: give each container one session's values. Use this sizing at `--capacity 1`, which the [hardening section](#harden-your-deployment) recommends, and for [on-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), where you set the values on the workload your [`spawn-runner` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook) submits, such as a Kubernetes Job's pod template.
378378* **Several sessions per runner**: at a `--capacity` above one, multiply one session's values by the capacity, because up to that many sessions can run in the container at the same time. The [Kubernetes](#kubernetes) and [Docker Compose](#docker-compose) recipes run `--capacity 4` with no CPU or memory limits, so add limits sized for the capacity you run.
379379 
380380## Kubernetes
381381 
382The runner serves `GET /healthz` on port 8080 by default, configurable with `--health-port`, so Kubernetes probes work with no extra setup. The endpoint returns `200` whenever the process is alive, so the probes below detect a dead process, not a stuck one; to catch a runner that stopped polling, alert on the `last_poll_age_seconds` series from [`/metrics`](/docs/en/self-hosted-environments-reference#prometheus-metrics). The Deployment below mounts the environment secret from a Kubernetes Secret, points the liveness and readiness probes at `/healthz`, and sets a 90-second termination grace period. See [Shutdown timing](#shutdown-timing) for why the grace period matters.
382The runner serves `GET /healthz` on port 8080 by default, configurable with `--health-port`, so Kubernetes probes work with no extra setup. The endpoint returns `200` whenever the process is alive, so the probes below detect a dead process, not a stuck one; to catch a runner that stopped polling, alert on the `last_poll_age_seconds` series from [`/metrics`](https://code.claude.com/docs/en/self-hosted-environments-reference#prometheus-metrics). The Deployment below mounts the environment secret from a Kubernetes Secret, points the liveness and readiness probes at `/healthz`, and sets a 90-second termination grace period. See [Shutdown timing](#shutdown-timing) for why the grace period matters.
383383 
384384The manifest sets no CPU or memory `resources` on the runner container. Add a block sized for the capacity you run, as [Size CPU and memory for sessions](#size-cpu-and-memory-for-sessions) describes.
385385 
from line 443
443443kubectl create namespace claude-runners
444444```
445445 
446Create the backing Secret from a local file holding the value you copied in the admin UI's [**Copy environment key** step](/docs/en/self-hosted-environments-quickstart.md#set-up-manually), so the secret never appears in your shell history. Run `(umask 077 && cat > ./environment-secret)`, paste the secret, press Enter, then Ctrl-D. Then create the Secret and delete the file:
446Create the backing Secret from a local file holding the value you copied in the admin UI's [**Copy environment key** step](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md#set-up-manually), so the secret never appears in your shell history. Run `(umask 077 && cat > ./environment-secret)`, paste the secret, press Enter, then Ctrl-D. Then create the Secret and delete the file:
447447 
448448```bash theme={null}
449449kubectl create secret generic claude-runner-environment-secret -n claude-runners --from-file=environment-secret=./environment-secret
from line 483
483483 
484484On `SIGTERM`, the runner stops accepting new sessions. It then begins a graceful shutdown, called a drain, unless you set [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal) to delay it. The drain has three steps:
485485 
4861. The runner waits up to [`--drain-wait-sec`](/docs/en/self-hosted-environments-reference#runner-cli-flags) seconds, `0` by default, for turns still running to finish.
4861. The runner waits up to [`--drain-wait-sec`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) seconds, `0` by default, for turns still running to finish.
4874872. It terminates each session's process tree, including any command Claude was still running, but not [a process still running after its shell command exited](#processes-a-stopped-session-leaves).
4883. It runs the [`post-session` lifecycle hook](/docs/en/self-hosted-environments-configuration#post-session).
4883. It runs the [`post-session` lifecycle hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session).
489489 
490490The runner keeps polling Anthropic throughout the drain. That keeps its sessions assigned to it, so another runner doesn't pick one up while your `post-session` hook is still saving uncommitted work.
491491 
from line 492
492492The logged time is the sum of these values:
493493 
494494* `--drain-wait-sec` for step 1, 0 seconds by default
495* [`--session-stop-grace-sec`](/docs/en/self-hosted-environments-reference#runner-cli-flags) for step 2, 5 seconds by default
496* [`--post-session-hook-timeout-sec`](/docs/en/self-hosted-environments-reference#runner-cli-flags) for step 3, 60 seconds by default
495* [`--session-stop-grace-sec`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) for step 2, 5 seconds by default
496* [`--post-session-hook-timeout-sec`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) for step 3, 60 seconds by default
497497* A fixed 15 seconds of headroom
498* 30 more seconds when [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) is set
498* 30 more seconds when [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) is set
499499 
500500At default settings that comes to 0 + 5 + 60 + 15 = 80 seconds. A higher `--capacity` doesn't add to it, because the runner drains all of its sessions at the same time.
501501 
502502Allow more time if you set either of these flags:
503503 
504* **With [`--retire-at`](/docs/en/self-hosted-environments-reference#runner-cli-flags)**: leave enough time between the retire time and the time the host stops for typical turns to finish, plus the wait for background tasks that [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle) describes, plus the logged time. Compute the retire time at each launch, for example `date +%s` plus the runner's intended lifetime.
504* **With [`--retire-at`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags)**: leave enough time between the retire time and the time the host stops for typical turns to finish, plus the wait for background tasks that [Runner lifecycle](https://code.claude.com/docs/en/self-hosted-environments#runner-lifecycle) describes, plus the logged time. Compute the retire time at each launch, for example `date +%s` plus the runner's intended lifetime.
505505* **With [`--defer-shutdown-max-min`](#defer-the-drain-past-the-first-signal)**: the stop timeout must also cover the minutes you configure and a further wait before the drain starts, 75 seconds at default settings. [Defer the drain past the first signal](#defer-the-drain-past-the-first-signal) explains both. The runner logs this longer time at startup too.
506506 
507507### Let running turns finish before a restart
from line 518
518518 
519519### Post a status message before a restart
520520 
521Before a restart that will interrupt sessions, have an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) post a status message on the environment from the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). At [claude.ai/code](https://claude.ai/code), members see the message in their sessions in that environment. Have the Owner set an expiry with the post, or remove the message once the runners are back.
521Before a restart that will interrupt sessions, have an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) post a status message on the environment from the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). At [claude.ai/code](https://claude.ai/code), members see the message in their sessions in that environment. Have the Owner set an expiry with the post, or remove the message once the runners are back.
522522 
523523### Defer the drain past the first signal
524524 
525Set [`--defer-shutdown-max-min <n>`](/docs/en/self-hosted-environments-reference#runner-cli-flags) if you want a runner you're restarting to go on serving the sessions it holds for up to `n` minutes, instead of draining them on the first signal. On the first `SIGTERM` or `SIGINT`, the runner stops taking new work and goes on serving the sessions it holds. It keeps polling so that the control plane doesn't requeue those sessions. Requires Claude Code v2.1.238 or later.
525Set [`--defer-shutdown-max-min <n>`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) if you want a runner you're restarting to go on serving the sessions it holds for up to `n` minutes, instead of draining them on the first signal. On the first `SIGTERM` or `SIGINT`, the runner stops taking new work and goes on serving the sessions it holds. It keeps polling so that the control plane doesn't requeue those sessions. Requires Claude Code v2.1.238 or later.
526526 
527527#### What happens to the sessions the runner holds after the first signal
528528 
529529In the first two stages that follow the signal, the runner releases sessions, and a released session resumes on a fresh runner when its user sends their next message. Counting from the first signal, the runner moves through three stages:
530530 
531* **For the first `n` minutes**: the runner serves its sessions normally and keeps enforcing `--startup-timeout-min` and `--kill-session-after-min`. If you also set [`--release-idle-session-min`](/docs/en/self-hosted-environments-reference#runner-cli-flags), the runner releases any session whose user has been idle that long; without it, idle sessions stay on the runner.
531* **For the first `n` minutes**: the runner serves its sessions normally and keeps enforcing `--startup-timeout-min` and `--kill-session-after-min`. If you also set [`--release-idle-session-min`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags), the runner releases any session whose user has been idle that long; without it, idle sessions stay on the runner.
532532* **When the `n` minutes run out**: the runner releases every session it still holds, idle or not. The runner waits for a mid-turn session's turn to end, and up to 60 seconds more for a turn's background tasks, before releasing that session.
533533* **When the post-release grace runs out**: the runner drains whatever sessions it still holds, and the control plane requeues each drained session to another runner right away. The post-release grace starts when the `n` minutes run out and is 75 seconds at defaults. If you set `--drain-wait-sec` above 60 seconds, the post-release grace is `--drain-wait-sec` plus 15 seconds instead.
534534 
from line 557
557557 
558558Use the same `--base-dir` and `--capacity` on every runner in an environment, and don't use a per-host value such as an instance ID or hostname. Pass both flags explicitly, as the [Kubernetes](#kubernetes) and [Docker Compose](#docker-compose) recipes do, so that neither value depends on a default.
559559 
560The base directory defaults to `/workspace`, with the exception the [`--base-dir` reference row](/docs/en/self-hosted-environments-reference#runner-cli-flags) records. The runner needs write access to it. At startup, before registering, the runner creates the directory and confirms it can write to it, and exits with `cannot create or write to base directory` when it can't. A runner started as root creates the default `/workspace` itself. For a non-root runner, create the directory and give the runner's user ownership before starting the runner, or point `--base-dir` at a directory that user already owns.
560The base directory defaults to `/workspace`, with the exception the [`--base-dir` reference row](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) records. The runner needs write access to it. At startup, before registering, the runner creates the directory and confirms it can write to it, and exits with `cannot create or write to base directory` when it can't. A runner started as root creates the default `/workspace` itself. For a non-root runner, create the directory and give the runner's user ownership before starting the runner, or point `--base-dir` at a directory that user already owns.
561561 
562562## Reuse a pre-warmed checkout
563563 
564For large repositories, the clone can dominate session startup. To skip the cold clone, supply a clone yourself at the path where the runner keeps its own. With no [`checkout` hook](/docs/en/self-hosted-environments-configuration#checkout), the runner keeps one canonical clone per repository at `<base-dir>/<repo-owner>/<repo>` and reuses it across sessions:
564For large repositories, the clone can dominate session startup. To skip the cold clone, supply a clone yourself at the path where the runner keeps its own. With no [`checkout` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout), the runner keeps one canonical clone per repository at `<base-dir>/<repo-owner>/<repo>` and reuses it across sessions:
565565 
566566* **At `--capacity 1`**: the runner fetches the requested ref, detaches `HEAD`, and resets hard to it, which is near-instant when little has changed.
567567* **At a `--capacity` above one**: the runner fetches into that clone, then checks out a separate worktree from it for each session. A pre-warmed clone saves the download but not the checkout.
from line 569
569569Supply the clone in the image or on a persistent volume:
570570 
571571* **Clone in the image**: build the clone into your runner image at that path. Every fresh container then starts with the warm clone without reusing a disk.
572* **Clone on a persistent volume**: on runners you pre-lock to one user's account with [`--lock-to-account`](/docs/en/self-hosted-environments-reference#runner-cli-flags), point `--base-dir` at a persistent volume, so the disk only ever serves that account. A pre-locked runner never picks up Claude Tag channel sessions, so this option doesn't apply to runners that serve them.
572* **Clone on a persistent volume**: on runners you pre-lock to one user's account with [`--lock-to-account`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags), point `--base-dir` at a persistent volume, so the disk only ever serves that account. A pre-locked runner never picks up Claude Tag channel sessions, so this option doesn't apply to runners that serve them.
573573 
574574What the reuse path does and doesn't guarantee:
575575 
from line 578
578578* **Per-session directories persist too**: alongside the checkout, the runner creates per-session entries under `<base-dir>/_sessions/` for every session it runs. The session's Claude config directory holds a local copy of the conversation transcript. Next to it sit the session's uploaded files, when the session has any. The session directory sits there too: it holds any per-session worktrees and `checkout` hook checkouts while the session runs, and it keeps whatever else Claude wrote in it.
579579 
580580 By default the runner leaves these in place when the session ends, so on a disk that outlives the runner process they accumulate. Every session runs as the runner's own user, so any later session that disk serves can read them. If you keep a persistent `--base-dir`, size the volume for that growth. The same applies to any setup that restarts the runner on the same filesystem, including the [Docker Compose recipe](#docker-compose).
581* **With `--remove-session-state`, per-session directories don't persist**: start the runner with [`--remove-session-state`](/docs/en/self-hosted-environments-reference#runner-cli-flags) to have it delete each session's per-session directories as the session ends. The deletion is best-effort: the directories stay when the runner is killed before its cleanup runs. The canonical clone and files a session wrote elsewhere on the host, such as the temporary directory, stay regardless.
581* **With `--remove-session-state`, per-session directories don't persist**: start the runner with [`--remove-session-state`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) to have it delete each session's per-session directories as the session ends. The deletion is best-effort: the directories stay when the runner is killed before its cleanup runs. The canonical clone and files a session wrote elsewhere on the host, such as the temporary directory, stay regardless.
582582* **With the git proxy, the reset becomes a checkout**: with [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), the runner sanitizes the clone's `.git/` before each session. It keeps the object store and shallow state, and deletes the index. Without the index, each session starts with a full working-tree checkout instead of a near-instant reset. The runner still fetches into the existing clone rather than cloning the repository again. Submodule pre-warms aren't supported under the proxy.
583583* **With the git proxy, a `reftable` pre-warm loses its refs**: when the runner sanitizes `.git/` before each session, it keeps `.git/refs/` and removes the `.git/reftable/` directory. A clone pre-warmed in git's `reftable` ref format loses its refs that way, so pre-warm the clone in git's `files` ref format instead.
584584* **Long clones need no workaround**: the runner bounds each git operation with a 120-second no-progress watchdog and a 30-minute hard cap, not a flat timeout, so a slow cold clone that keeps reporting progress completes.
from line 589
589589 
590590Choose which version your sessions run and when it changes:
591591 
592* **Before you pin a version**: check [the Claude Code versions that models require](/docs/en/model-config.md#available-models) for every model your sessions use. If a model requires a newer version than the one your sessions run, the server rejects requests for that model with [Claude Code does not support this model](/docs/en/errors.md#claude-code-does-not-support-this-model).
593* **To hold a fleet on one version**: build the image with a pinned version, or on a bare host install a specific version and [disable auto-updates](/docs/en/setup.md#disable-auto-updates)
594* **To upgrade a fixed fleet**: read the [changelog](/docs/en/changelog.md) entries between your version and the one you're installing, then install the newer version or rebuild the image and restart the runners
595* **To upgrade on-demand runners**: read the [changelog](/docs/en/changelog.md) entries between your version and the one you're installing, then change the image your [`spawn-runner` hook](/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook) starts. Each new runner gets the new version. A runner that's already up, including a standby runner that [`--min-idle`](/docs/en/self-hosted-environments-reference#orchestrator-cli-flags) started, keeps its version until it exits. Don't restart it, because its work order is single-use.
596* **If sessions start a different binary**: if you use [`--exec-path`](/docs/en/self-hosted-environments-reference#runner-cli-flags) or a [`command` hook](/docs/en/self-hosted-environments-configuration#command) to start a Claude Code binary other than the runner's own, sessions run that binary's version. Update that binary when you update the runner.
592* **Before you pin a version**: check [the Claude Code versions that models require](https://code.claude.com/docs/en/model-config.md#available-models) for every model your sessions use. If a model requires a newer version than the one your sessions run, the server rejects requests for that model with [Claude Code does not support this model](https://code.claude.com/docs/en/errors.md#claude-code-does-not-support-this-model).
593* **To hold a fleet on one version**: build the image with a pinned version, or on a bare host install a specific version and [disable auto-updates](https://code.claude.com/docs/en/setup.md#disable-auto-updates)
594* **To upgrade a fixed fleet**: read the [changelog](https://code.claude.com/docs/en/changelog.md) entries between your version and the one you're installing, then install the newer version or rebuild the image and restart the runners
595* **To upgrade on-demand runners**: read the [changelog](https://code.claude.com/docs/en/changelog.md) entries between your version and the one you're installing, then change the image your [`spawn-runner` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook) starts. Each new runner gets the new version. A runner that's already up, including a standby runner that [`--min-idle`](https://code.claude.com/docs/en/self-hosted-environments-reference#orchestrator-cli-flags) started, keeps its version until it exits. Don't restart it, because its work order is single-use.
596* **If sessions start a different binary**: if you use [`--exec-path`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) or a [`command` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#command) to start a Claude Code binary other than the runner's own, sessions run that binary's version. Update that binary when you update the runner.
597597* **Plugins**: plugin marketplaces don't auto-update either; set `FORCE_AUTOUPDATE_PLUGINS=1` in the runner's environment to let plugins auto-update while the binary stays pinned
598598 
599599## Scale the fleet
600600 
601Your orchestrator decides when to add or remove runners. Because of the [one-owner-per-runner lock](/docs/en/self-hosted-environments#runner-lifecycle), the minimum replica count is the number of users and Claude Tag agents you expect to be active concurrently; `--capacity` controls parallelism within one owner's sessions, not across owners.
601Your orchestrator decides when to add or remove runners. Because of the [one-owner-per-runner lock](https://code.claude.com/docs/en/self-hosted-environments#runner-lifecycle), the minimum replica count is the number of users and Claude Tag agents you expect to be active concurrently; `--capacity` controls parallelism within one owner's sessions, not across owners.
602602 
603603Two scaling approaches are available:
604604 
605* **Fixed fleet**: run a static set of runner replicas and scale on the [Prometheus metrics](/docs/en/self-hosted-environments-reference.md#prometheus-metrics) each runner serves
606* **On-demand runners**: run the `claude self-hosted-runner orchestrator` subcommand, which polls Anthropic for sessions that are queued with no runner available and invokes your `spawn-runner` hook to boot one per session. See [On-demand runners](/docs/en/self-hosted-environments-configuration.md#on-demand-runners).
605* **Fixed fleet**: run a static set of runner replicas and scale on the [Prometheus metrics](https://code.claude.com/docs/en/self-hosted-environments-reference.md#prometheus-metrics) each runner serves
606* **On-demand runners**: run the `claude self-hosted-runner orchestrator` subcommand, which polls Anthropic for sessions that are queued with no runner available and invokes your `spawn-runner` hook to boot one per session. See [On-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners).
607607 
608608## Known issues and limitations
609609 
from line 613
613613 
614614Anthropic calls connector tools from its own infrastructure rather than from your runner. Connector tools are the claude.ai connectors, such as GitHub, Slack, and Linear. When Claude uses a connector in a self-hosted session, that traffic goes through `api.anthropic.com` rather than originating inside your network boundary.
615615 
616To keep a connector out of self-hosted sessions, filter it with the [`allowedMcpServers` and `deniedMcpServers` policy settings](/docs/en/managed-mcp#policy-based-control-with-allowlists-and-denylists). Claude Code applies these settings to the connectors Anthropic delivers as well as to the servers you seed from the runner host and the servers users add, so if you deploy an allowlist for other servers, Claude Code blocks delivered connectors too. To keep connectors available alongside a URL-based allowlist, add entries that match the Anthropic proxy paths for delivered connectors:
616To keep a connector out of self-hosted sessions, filter it with the [`allowedMcpServers` and `deniedMcpServers` policy settings](https://code.claude.com/docs/en/managed-mcp#policy-based-control-with-allowlists-and-denylists). Claude Code applies these settings to the connectors Anthropic delivers as well as to the servers you seed from the runner host and the servers users add, so if you deploy an allowlist for other servers, Claude Code blocks delivered connectors too. To keep connectors available alongside a URL-based allowlist, add entries that match the Anthropic proxy paths for delivered connectors:
617617 
618618* `https://api.anthropic.com/v2/ccr-sessions/*`
619619* `https://api.anthropic.com/v1/code/sessions/*`
620620* `https://api.anthropic.com/v1/code/mcp/*`
621621 
622If tool traffic must stay inside your network, run the equivalent tools as local MCP servers on the runner image instead. See [MCP servers](/docs/en/self-hosted-environments-configuration.md#mcp-servers).
622If tool traffic must stay inside your network, run the equivalent tools as local MCP servers on the runner image instead. See [MCP servers](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#mcp-servers).
623623 
624624### Some sessions don't count as idle
625625 
626626A session holding a background task that never finishes doesn't count as idle, so `--release-idle-session-min` won't release that session's slot. A session that's waiting on an approval requested from inside a running tool call also doesn't count as idle. Always set `--kill-session-after-min` alongside it as a hard backstop so no session can hold a slot indefinitely.
627627 
628`--kill-session-after-min` is a backstop for runaway sessions. On a runner on v2.1.260 or later, a session that reaches the limit isn't terminated outright. The runner gives it a grace window, 15 minutes by default, which you can change with [`SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS`](/docs/en/self-hosted-environments-reference#environment-variable-only-settings):
628`--kill-session-after-min` is a backstop for runaway sessions. On a runner on v2.1.260 or later, a session that reaches the limit isn't terminated outright. The runner gives it a grace window, 15 minutes by default, which you can change with [`SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS`](https://code.claude.com/docs/en/self-hosted-environments-reference#environment-variable-only-settings):
629629 
630630* If the session is waiting on its user, the runner releases it. If its turn has ended and it holds only background tasks, the runner waits up to 60 seconds for those tasks to finish and then releases it. The session resumes when its user sends their next message.
631631* If a turn is still running, the runner waits for the turn to finish, or for the session to next wait on its user, and then releases it.
from line 638
638638### Additional limitations
639639 
640640* **Resumed sessions lose unpushed work**: a fresh runner clones the repository again from its starting branch, so work the session hadn't pushed is gone.
641 * **To keep committed work**: set [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) on every runner in the environment, because a runner without the flag resumes the session from its starting branch. A runner with the flag makes a best-effort push of the session's outcome branches before it releases, and the resumed session starts from those commits. The push uses the runner host's own git credentials, including on a runner that uses [Anthropic-managed git](#use-the-anthropic-git-proxy). Uncommitted changes are still lost.
642 * **With a `checkout` hook**: repositories checked out via a [`checkout` lifecycle hook](/docs/en/self-hosted-environments-configuration#checkout) aren't pushed. Snapshot those from the [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session) instead.
641 * **To keep committed work**: set [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) on every runner in the environment, because a runner without the flag resumes the session from its starting branch. A runner with the flag makes a best-effort push of the session's outcome branches before it releases, and the resumed session starts from those commits. The push uses the runner host's own git credentials, including on a runner that uses [Anthropic-managed git](#use-the-anthropic-git-proxy). Uncommitted changes are still lost.
642 * **With a `checkout` hook**: repositories checked out via a [`checkout` lifecycle hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout) aren't pushed. Snapshot those from the [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session) instead.
643643 * **Before enabling the flag**: restrict who can push to `claude/*` refs on the source remote. On resume, the runner fetches the previously pushed branch without verifying who pushed it.
644644* **A repository added mid-session can fail to clone**: Claude clones it with `git clone` over HTTPS. On a runner without [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), the clone fails with a git authentication error if nothing on the host can read the repository. Where you can, select every repository the session needs when you create it.
645645* **Some connectors don't appear in self-hosted sessions**: a connector you haven't yet connected in claude.ai Settings isn't listed in a self-hosted session, and the session won't prompt you to connect it. Connect it in Settings first, then start a fresh session. Adding a connector to an already-running session also doesn't make its tools available to Claude; start a fresh session to pick up a newly added connector.
646* **When someone archives or deletes a session, or releases its runner from the session's menu**: don't count on the [`--push-outcome-on-release`](/docs/en/self-hosted-environments-reference#runner-cli-flags) push. To save commits in those cases, push `HEAD` to a rescue branch from a [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session). If you start from the example there, make it push even when nothing is uncommitted, because it pushes only after a snapshot commit.
646* **When someone archives or deletes a session, or releases its runner from the session's menu**: don't count on the [`--push-outcome-on-release`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) push. To save commits in those cases, push `HEAD` to a rescue branch from a [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session). If you start from the example there, make it push even when nothing is uncommitted, because it pushes only after a snapshot commit.
647647 
648648### Report an issue
649649 
from line 661
661661 
662662* **Runner doesn't appear in the environment**: confirm the host can reach `api.anthropic.com` over HTTPS, the environment secret is current, and the host clock is within five minutes of real time; larger skew causes authentication to fail. The runner logs `[runner:fatal]` with the rejection reason on auth failure.
663663* **Runner exits at startup with `cannot create or write to base directory`**: the runner can't create or write to `--base-dir`, which defaults to `/workspace`. Fix the directory's ownership or point `--base-dir` at a writable path, as described in [Keep the base directory and capacity identical across runners](#keep-the-base-directory-and-capacity-identical-across-runners). If the runner instead logs `[runner:fatal]` saying the base directory check timed out, the directory is on a hung NFS or CSI mount. Check mount health rather than permissions. The runner prints both of these startup failures to stderr before it opens `--log-file`, so look for them in the terminal or your platform's container logs rather than the log file. Before v2.1.225, the runner didn't check the base directory at startup, and this misconfiguration failed sessions after pickup instead.
664* **Runner exits at startup with `RegisterRunner failed:` and a TLS certificate error**: the error can be `self signed certificate in certificate chain`, for example. The runner doesn't trust the certificate authority (CA) behind the certificate it received, as happens behind a TLS-inspecting proxy. Set [`NODE_EXTRA_CA_CERTS`](/docs/en/network-config#custom-ca-certificates) in the runner's environment to a PEM file with the proxy's root CA.
664* **Runner exits at startup with `RegisterRunner failed:` and a TLS certificate error**: the error can be `self signed certificate in certificate chain`, for example. The runner doesn't trust the certificate authority (CA) behind the certificate it received, as happens behind a TLS-inspecting proxy. Set [`NODE_EXTRA_CA_CERTS`](https://code.claude.com/docs/en/network-config#custom-ca-certificates) in the runner's environment to a PEM file with the proxy's root CA.
665665 
666666 You don't set it again for sessions, which inherit it. Git doesn't read it: for clones through that proxy, set [`GIT_SSL_CAINFO`](#trust-a-private-certificate-authority-with-anthropic-managed-git) too.
667* **Your start script prints a write error containing `Resource temporarily unavailable` and exits**: your script shares its stdout or stderr with the `claude self-hosted-runner` or [orchestrator](/docs/en/self-hosted-environments-configuration.md#on-demand-runners) process it starts, which has switched that shared pipe or socket to non-blocking mode. When the pipe is full, a write from your script fails with this operating-system error instead of waiting, and under `set -e` the script exits.
667* **Your start script prints a write error containing `Resource temporarily unavailable` and exits**: your script shares its stdout or stderr with the `claude self-hosted-runner` or [orchestrator](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners) process it starts, which has switched that shared pipe or socket to non-blocking mode. When the pipe is full, a write from your script fails with this operating-system error instead of waiting, and under `set -e` the script exits.
668668 
669669 To stop the error, send the runner's or orchestrator's output to a file that your script's user can create. Append `>> "$HOME/claude-runner.log" 2>&1` to the command that starts it, ahead of any `&` that backgrounds it:
670670 
from line 672
672672 claude self-hosted-runner --environment-secret-file /etc/claude/environment-secret >> "$HOME/claude-runner.log" 2>&1
673673 ```
674674 
675 Your script's own output still goes where it went before, while what the runner writes to stdout and stderr, its startup errors included, goes to that file. Use this shell redirect rather than [`--log-file`](/docs/en/self-hosted-environments-reference#runner-cli-flags) alone, which mirrors the log to a file while the runner keeps writing to stdout and stderr. If you already pass `--log-file`, keep it as well, because the redirect doesn't change what that file receives.
676* **Orchestrator logs `poll failed:` with a TLS certificate error and keeps retrying**: the orchestrator doesn't trust the CA behind the certificate it received, as happens behind a TLS-inspecting proxy. Set [`NODE_EXTRA_CA_CERTS`](/docs/en/network-config#custom-ca-certificates) in the orchestrator's environment to a PEM file with the proxy's root CA. Set it for the runners your `spawn-runner` hook starts too.
675 Your script's own output still goes where it went before, while what the runner writes to stdout and stderr, its startup errors included, goes to that file. Use this shell redirect rather than [`--log-file`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) alone, which mirrors the log to a file while the runner keeps writing to stdout and stderr. If you already pass `--log-file`, keep it as well, because the redirect doesn't change what that file receives.
676* **Orchestrator logs `poll failed:` with a TLS certificate error and keeps retrying**: the orchestrator doesn't trust the CA behind the certificate it received, as happens behind a TLS-inspecting proxy. Set [`NODE_EXTRA_CA_CERTS`](https://code.claude.com/docs/en/network-config#custom-ca-certificates) in the orchestrator's environment to a PEM file with the proxy's root CA. Set it for the runners your `spawn-runner` hook starts too.
677677 
678 For example, a line can end `poll failed: self signed certificate in certificate chain — retrying in 1s`, with the TLS library's words between the colon and the dash. To confirm the fix, check that `connected` is `true` in the orchestrator's [`/healthz`](/docs/en/self-hosted-environments-reference#health-endpoint) body.
679* **Sessions stay queued**: every online runner may be locked to a different owner. Check each runner's `claude_code_self_hosted_runner_locked_account` [metric](/docs/en/self-hosted-environments-reference.md#prometheus-metrics) or the `locked_account` field of its `[runner:health]` log line to see who holds it. Both show the owner's email only after the runner has been issued a session token carrying an `act.email` claim, which a Claude Tag agent's sessions never do. Without the claim, the runner emits no `locked_account` series and logs `locked_account=yes`, which tells you the runner is locked but not to which owner. Add replicas, or wait for an existing runner to drain and restart. If the environment uses on-demand runners, check the orchestrator instead; see [On-demand runners](/docs/en/self-hosted-environments-configuration.md#on-demand-runners).
678 For example, a line can end `poll failed: self signed certificate in certificate chain — retrying in 1s`, with the TLS library's words between the colon and the dash. To confirm the fix, check that `connected` is `true` in the orchestrator's [`/healthz`](https://code.claude.com/docs/en/self-hosted-environments-reference#health-endpoint) body.
679* **Sessions stay queued**: every online runner may be locked to a different owner. Check each runner's `claude_code_self_hosted_runner_locked_account` [metric](https://code.claude.com/docs/en/self-hosted-environments-reference.md#prometheus-metrics) or the `locked_account` field of its `[runner:health]` log line to see who holds it. Both show the owner's email only after the runner has been issued a session token carrying an `act.email` claim, which a Claude Tag agent's sessions never do. Without the claim, the runner emits no `locked_account` series and logs `locked_account=yes`, which tells you the runner is locked but not to which owner. Add replicas, or wait for an existing runner to drain and restart. If the environment uses on-demand runners, check the orchestrator instead; see [On-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners).
680680* **Sessions fail immediately after pickup**: open the session in claude.ai/code to see the error. If the error shows that git couldn't authenticate, check the runner's [git credentials](#configure-git). On a runner started with `--use-anthropic-git-proxy`, see [When sessions fail to start on a runner with the git proxy](#when-anthropic-doesnt-serve-a-session).
681681 
682682 An unwritable base directory stops the runner at startup instead of failing sessions. See the **Runner exits at startup with `cannot create or write to base directory`** entry in this list.
from line 684
684684* **Sessions can't reach the network through an authenticating egress proxy**: when the source you set with [`--proxy-authorization-command` or `--proxy-authorization-file`](#authenticate-to-an-egress-proxy) fails, times out after 30 seconds, or yields an empty value, the runner answers that connection `502 Bad Gateway` and logs why. The runner redacts the command's stderr in that log and never logs the header value. With `--proxy-authorization-command`, run the command yourself on the host to confirm it prints the whole header value on stdout. If the runner instead exits at startup with `could not start the proxy-authorization listener`, it couldn't open its loopback listener.
685685* **Runner logs `Poll failed` lines containing `rejecting the malformed poll response`**: the runner received a work-poll response whose body isn't the queue's expected JSON. One cause is an intercepting proxy or a captive portal answering with its own page. If the runner connects through a proxy, configure the proxy to pass responses from `api.anthropic.com` through unaltered.
686686 
687 The runner rejects the malformed response, counts it under the `transport` kind of the `claude_code_self_hosted_runner_poll_errors_total` [metric](/docs/en/self-hosted-environments-reference.md#prometheus-metrics), and retries on the failed-poll schedule described in [Session lifecycle](/docs/en/self-hosted-environments#session-lifecycle). The runner keeps serving its live sessions. Before v2.1.246, the runner read such a response as an empty work queue, which could end its live sessions or make it exit.
687 The runner rejects the malformed response, counts it under the `transport` kind of the `claude_code_self_hosted_runner_poll_errors_total` [metric](https://code.claude.com/docs/en/self-hosted-environments-reference.md#prometheus-metrics), and retries on the failed-poll schedule described in [Session lifecycle](https://code.claude.com/docs/en/self-hosted-environments#session-lifecycle). The runner keeps serving its live sessions. Before v2.1.246, the runner read such a response as an empty work queue, which could end its live sessions or make it exit.
688688* **A session fails with `no longer exists on the remote` or `couldn't find remote ref`**: the runner reads git's `couldn't find remote ref` as a deleted branch. The branch can still exist. Look for it on your git host, under exactly the name in the message, then check each cause that fits:
689689 
690690 * **The branch is gone**: restore it, as the message asks.
691691 * **The branch exists and you don't administer the runner**: send the runner's administrator the full message and tell them the branch exists.
692 * **The branch exists and the runner has a `checkout` hook**: a git command in your hook can print `couldn't find remote ref` to stderr before the hook exits non-zero. Find that command, and correct the ref it asks for or the remote it fetches from. The runner passes your hook the revision the session asked for as [`CLAUDE_RUNNER_REPO_REF`](/docs/en/self-hosted-environments-configuration#checkout).
692 * **The branch exists and the runner has a `checkout` hook**: a git command in your hook can print `couldn't find remote ref` to stderr before the hook exits non-zero. Find that command, and correct the ref it asks for or the remote it fetches from. The runner passes your hook the revision the session asked for as [`CLAUDE_RUNNER_REPO_REF`](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout).
693693 * **The branch exists and the runner has no `checkout` hook**: the runner fetches the branch itself. If a [`--git-host-rewrite`](#rewrite-git-urls-for-private-networks) applies to the repository, confirm that the host it rewrites to has the branch under the same name, and push the branch there if it doesn't.
694694 * **The branch exists and the runner is older than v2.1.301**: upgrade the runner as described in [Pin the version](#pin-the-version), so that a different failure shows its own error. Before v2.1.301, other failures with `couldn't find remote ref` in their error output were also reported as a deleted branch.
695695 
from line 696
696696 Once the runner is upgraded or the cause is fixed, retry the session or start a new one. If none of these checks finds the cause, [report an issue](#report-an-issue).
697697 
698698 For the source the session pushes results to, a deleted branch fails the session. For a git source the session only reads from, the runner skips that source and continues on the remaining ones. The runner fails the session when skipping would leave it with no repository at all. Before v2.1.228, such a session started in an empty directory.
699* **A session starts without one of its repositories**: on a runner with no [`checkout` hook](/docs/en/self-hosted-environments-configuration#checkout), the git host can refuse the runner's access check for a repository the session only reads from. The runner then skips that repository, logs a `[runner:warn] could not access context source` line naming the refusal, and starts the session on the remaining ones.
699* **A session starts without one of its repositories**: on a runner with no [`checkout` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#checkout), the git host can refuse the runner's access check for a repository the session only reads from. The runner then skips that repository, logs a `[runner:warn] could not access context source` line naming the refusal, and starts the session on the remaining ones.
700700 
701701 The runner skips only a clear refusal: the host answers that the repository wasn't found, git finds no credentials for the host, or authentication fails. A network failure, a timeout, or an HTTP `403` still fails the session start, as does a refusal for a repository the session pushes results to. The runner still fails a session that skipping would leave with no repository at all. With [`--use-anthropic-git-proxy`](#use-the-anthropic-git-proxy), the runner skips only a repository the git proxy itself denies.
702702 
from line 704
704704* **A session fails to start with `This runner couldn't read`**: the runner asked the git host for a repository with its [git credentials](#configure-git) and got a not-found answer. The repository doesn't exist, or those credentials can't reach the repository.
705705 * **If you administer the runner**: give its git credentials read access to the repository, then start a new session.
706706 * **If you started the session**: if the repository doesn't exist, start a new session with the right repository. If the runner can't reach the repository, ask the runner's administrator to grant access, then start a new session.
707* **Sessions take minutes to start**: the initial clone usually dominates. Watch the `claude_code_self_hosted_runner_session_init_duration_seconds` [metric](/docs/en/self-hosted-environments-reference.md#prometheus-metrics) to confirm, and cut the clone with a [pre-warmed checkout](#reuse-a-pre-warmed-checkout) or a smaller `CLAUDE_RUNNER_FETCH_DEPTH`.
708* **Turns fail with a 401**: when a turn ends with a 401 or 403 from the Anthropic API, the runner fetches a fresh [`CLAUDE_CODE_OAUTH_TOKEN`](/docs/en/self-hosted-environments-configuration#wrapper-scripts) from Anthropic and passes it to the session. The failed turn isn't retried. This token is short-lived, and the runner rotates it over the session's stdin.
707* **Sessions take minutes to start**: the initial clone usually dominates. Watch the `claude_code_self_hosted_runner_session_init_duration_seconds` [metric](https://code.claude.com/docs/en/self-hosted-environments-reference.md#prometheus-metrics) to confirm, and cut the clone with a [pre-warmed checkout](#reuse-a-pre-warmed-checkout) or a smaller `CLAUDE_RUNNER_FETCH_DEPTH`.
708* **Turns fail with a 401**: when a turn ends with a 401 or 403 from the Anthropic API, the runner fetches a fresh [`CLAUDE_CODE_OAUTH_TOKEN`](https://code.claude.com/docs/en/self-hosted-environments-configuration#wrapper-scripts) from Anthropic and passes it to the session. The failed turn isn't retried. This token is short-lived, and the runner rotates it over the session's stdin.
709709 
710710 When a fetch fails, the runner logs an `inference_token refresh failed` line that says when it will retry, and it keeps retrying for as long as the session runs.
711711 
712 If every call starts failing about 30 minutes into a session, a wrapper script has likely severed the session's stdin, so token rotations can't reach it; see [Keep stdin and file descriptor 3 attached](/docs/en/self-hosted-environments-configuration.md#keep-stdin-and-file-descriptor-3-attached).
712 If every call starts failing about 30 minutes into a session, a wrapper script has likely severed the session's stdin, so token rotations can't reach it; see [Keep stdin and file descriptor 3 attached](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#keep-stdin-and-file-descriptor-3-attached).
713713 
714714 Before v2.1.274, the runner stopped retrying a failed fetch after a few attempts and waited for the next scheduled one. A failed turn didn't trigger a fetch, so every turn failed with a 401 until the next scheduled fetch.
715* **A turn fails in claude.ai/code with `This model needs a newer version of Claude Code`**: the Claude Code version that your runners start sessions with is older than the session's model requires, so the API [rejects the request](/docs/en/errors.md#claude-code-does-not-support-this-model). Upgrade the runners to the version the message asks for, or a later one, as described in [Pin the version](#pin-the-version). Until then, people can pick a different model in the session.
715* **A turn fails in claude.ai/code with `This model needs a newer version of Claude Code`**: the Claude Code version that your runners start sessions with is older than the session's model requires, so the API [rejects the request](https://code.claude.com/docs/en/errors.md#claude-code-does-not-support-this-model). Upgrade the runners to the version the message asks for, or a later one, as described in [Pin the version](#pin-the-version). Until then, people can pick a different model in the session.
716716* **Pod is killed mid-drain**: raise `terminationGracePeriodSeconds` to at least the value the runner logs at startup. See [Shutdown timing](#shutdown-timing).
717717 
718718Once logging is initialized, the runner writes its lifecycle log, including `[runner:fatal]` lines, to stdout, and debug output to stderr, all as plain-text lines rather than JSON. The base directory failures described in the troubleshooting entries above print to stderr before that point. Capture both streams with `--log-file`, which also lets `self-hosted-runner doctor` tail them, or with your platform's log collection.
719719 
720Each session's child process writes a separate debug log. On failure the runner surfaces the log's tail alongside the session in claude.ai/code. Unless you started the runner with [`--remove-session-state`](/docs/en/self-hosted-environments-reference#runner-cli-flags), it also keeps a failed session's log on disk and prints its path in the runner log.
720Each session's child process writes a separate debug log. On failure the runner surfaces the log's tail alongside the session in claude.ai/code. Unless you started the runner with [`--remove-session-state`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags), it also keeps a failed session's log on disk and prints its path in the runner log.
721721 
722722### When the runner exits
723723 
724Don't restart an [on-demand runner](/docs/en/self-hosted-environments-configuration.md#on-demand-runners), because its work order is single-use. A runner that exits right after it starts needs different handling from one that exits for any other reason.
724Don't restart an [on-demand runner](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), because its work order is single-use. A runner that exits right after it starts needs different handling from one that exits for any other reason.
725725 
726* **A normal exit**: the runner finished its sessions and drained, reached its retire time, or was told to stop. Restart it so the environment has capacity again. [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle) describes these exits.
726* **A normal exit**: the runner finished its sessions and drained, reached its retire time, or was told to stop. Restart it so the environment has capacity again. [Runner lifecycle](https://code.claude.com/docs/en/self-hosted-environments#runner-lifecycle) describes these exits.
727727* **A failed start**: the runner can't start with the configuration or host it was given, so it exits seconds after it starts, and it exits the same way every time you restart it. Restarting it faster doesn't help. Someone needs to read its output and fix the cause.
728* **Lost contact**: a runner that can't reach Anthropic for longer than its [lease](/docs/en/self-hosted-environments#session-lifecycle), for example while its host sleeps, can be removed from the environment. When a removed runner reconnects, it exits. Its log can show a `[runner:fatal]` line that contains `runner record gone server-side` or, after a longer outage, [`poll auth failed`](/docs/en/self-hosted-environments-quickstart#set-up-an-environment-and-runner). The runner doesn't register again by itself, so restart it.
728* **Lost contact**: a runner that can't reach Anthropic for longer than its [lease](https://code.claude.com/docs/en/self-hosted-environments#session-lifecycle), for example while its host sleeps, can be removed from the environment. When a removed runner reconnects, it exits. Its log can show a `[runner:fatal]` line that contains `runner record gone server-side` or, after a longer outage, [`poll auth failed`](https://code.claude.com/docs/en/self-hosted-environments-quickstart#set-up-an-environment-and-runner). The runner doesn't register again by itself, so restart it.
729729 
730730Configure your supervisor to restart the runner whenever it exits, to wait longer between restarts when the runner keeps exiting right after it starts, and to tell someone when that keeps happening.
731731 
from line 741
741741[runner:fatal] --use-anthropic-git-proxy requires --capacity 1 (the proxy URL is per-session and linked worktrees share origin). Omit --use-anthropic-git-proxy or set --capacity 1.
742742```
743743 
744Look for the line in the runner's standard output and standard error, in your platform's container logs, or in the file you set with [`--log-file`](/docs/en/self-hosted-environments-reference#runner-cli-flags). The runner prints an `error:` line before it opens the log file, so look for it in the terminal or your container logs, as [Troubleshooting](#troubleshooting) notes.
744Look for the line in the runner's standard output and standard error, in your platform's container logs, or in the file you set with [`--log-file`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags). The runner prints an `error:` line before it opens the log file, so look for it in the terminal or your container logs, as [Troubleshooting](#troubleshooting) notes.
745745 
746746These also help when you read a failed start:
747747 
from line 786
786786 
787787## What's next
788788 
789* [Customize sessions](/docs/en/self-hosted-environments-configuration.md): wrapper scripts, lifecycle hooks, on-demand runners, MCP servers, and permissions
790* [Test end to end](/docs/en/self-hosted-environments-testing.md): verify a new runner image from CI before promoting it
791* [Reference](/docs/en/self-hosted-environments-reference.md): every CLI flag, environment variable, and metric
789* [Customize sessions](https://code.claude.com/docs/en/self-hosted-environments-configuration.md): wrapper scripts, lifecycle hooks, on-demand runners, MCP servers, and permissions
790* [Test end to end](https://code.claude.com/docs/en/self-hosted-environments-testing.md): verify a new runner image from CI before promoting it
791* [Reference](https://code.claude.com/docs/en/self-hosted-environments-reference.md): every CLI flag, environment variable, and metric
792792 

self-hosted-environments-identity Changed · +12 / -12 lines

from line 4
44 
55> [!NOTE]
66>
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page covers session identity verification; see the [quickstart](/docs/en/self-hosted-environments-quickstart.md) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy.md) for the fleet recipes.
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page covers session identity verification; see the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md) for setup and [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md) for the fleet recipes.
88 
9A [self-hosted environment](/docs/en/self-hosted-environments) lets Claude Code [cloud sessions](/docs/en/claude-code-on-the-web.md) run on infrastructure you operate instead of on Anthropic's. Because the session runs inside your network, Claude can call your internal services directly. Those services need a way to confirm that a request came from a Claude Code session in your environment, and to identify the user or service identity that created that session.
9A [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) lets Claude Code [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md) run on infrastructure you operate instead of on Anthropic's. Because the session runs inside your network, Claude can call your internal services directly. Those services need a way to confirm that a request came from a Claude Code session in your environment, and to identify the user or service identity that created that session.
1010 
1111Every session in a self-hosted environment receives a signed JSON Web Token (JWT) in the `CLAUDE_CODE_SESSION_ACCESS_TOKEN` environment variable. A session presents the token like any bearer credential; for example, a script Claude runs can call your service with `curl -H "Authorization: Bearer $CLAUDE_CODE_SESSION_ACCESS_TOKEN"`. Anthropic signs the token and publishes the verification keys at a public JWKS endpoint. Your services fetch those keys, verify the signature, and read the claims to decide what access to grant.
1212 
from line 165
165165 
166166### Verify the token inside the session
167167 
168[Wrapper scripts](/docs/en/self-hosted-environments-configuration.md#wrapper-scripts) run inside the session, before Claude starts. Instead of calling a JWT library, they can run the runner binary's `self-hosted-runner decode-token` subcommand. The subcommand reads the token from a positional argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from piped stdin, in that order, then strips the prefix, verifies the signature against the JWKS endpoint, checks expiry, and prints the claims as JSON. The subcommand performs the signature and expiry checks only; it doesn't check `iss`, `aud`, or `ccr:role`. When your wrapper's auth decision depends on those claims, read them from the printed JSON and compare them explicitly.
168[Wrapper scripts](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts) run inside the session, before Claude starts. Instead of calling a JWT library, they can run the runner binary's `self-hosted-runner decode-token` subcommand. The subcommand reads the token from a positional argument, from `CLAUDE_CODE_SESSION_ACCESS_TOKEN`, or from piped stdin, in that order, then strips the prefix, verifies the signature against the JWKS endpoint, checks expiry, and prints the claims as JSON. The subcommand performs the signature and expiry checks only; it doesn't check `iss`, `aud`, or `ccr:role`. When your wrapper's auth decision depends on those claims, read them from the printed JSON and compare them explicitly.
169169 
170170This command extracts the creator identity, preferring the email address, then the creator's `act.sub` subject, `user:<id>` or `agent:<id>`:
171171 
from line 192
192192| `iat` | number | Issued-at as a Unix timestamp. |
193193| `jti` | string | Unique token identifier. |
194194| `ccr:role` | string | Always `session_worker` for session tokens. |
195| `ccr:session_id` | string | The session ID in `cse_...` form. Same value as the suffix of `sub`, and the same string as [`CLAUDE_CODE_REMOTE_SESSION_ID`](/docs/en/self-hosted-environments-configuration#wrapper-scripts). To match it to the `CLAUDE_RUNNER_SESSION_ID` that [lifecycle hooks](/docs/en/self-hosted-environments-configuration.md#lifecycle-hooks) receive, replace the `cse_` prefix with `session_`. |
195| `ccr:session_id` | string | The session ID in `cse_...` form. Same value as the suffix of `sub`, and the same string as [`CLAUDE_CODE_REMOTE_SESSION_ID`](https://code.claude.com/docs/en/self-hosted-environments-configuration#wrapper-scripts). To match it to the `CLAUDE_RUNNER_SESSION_ID` that [lifecycle hooks](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#lifecycle-hooks) receive, replace the `cse_` prefix with `session_`. |
196196| `ccr:pool_id` | string | Your environment ID. Same value that appears in `aud`. |
197197| `ccr:org_id` | string | Your Anthropic organization ID. |
198| `ccr:account_id` | string | The creating user's Anthropic account ID: the value of `act.sub` without the `user:` prefix, a tagged `user_...` ID. The same value the [spawn-runner hook](/docs/en/self-hosted-environments-configuration.md#the-spawn-runner-hook)'s `CLAUDE_RUNNER_ACCOUNT_ID` carries and [`--lock-to-account`](/docs/en/self-hosted-environments-reference#runner-cli-flags) accepts, so the three compare as equal strings. |
198| `ccr:account_id` | string | The creating user's Anthropic account ID: the value of `act.sub` without the `user:` prefix, a tagged `user_...` ID. The same value the [spawn-runner hook](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#the-spawn-runner-hook)'s `CLAUDE_RUNNER_ACCOUNT_ID` carries and [`--lock-to-account`](https://code.claude.com/docs/en/self-hosted-environments-reference#runner-cli-flags) accepts, so the three compare as equal strings. |
199199| `account_email` | string | Duplicate of `act.email`; absent whenever `act.email` is. |
200200| `organization_uuid` | string | Your Anthropic organization UUID. |
201201| `account_uuid` | string | The creating user's Anthropic account UUID. |
from line 203
203203 
204204### The `act` chain
205205 
206The `act` claim records the full delegation path from the user or service identity that created the session down to the [environment](/docs/en/self-hosted-environments#key-concepts) whose secret admitted the runner, and the identity that created that secret. The creator is the outermost actor, so `act.sub` identifies them directly.
206The `act` claim records the full delegation path from the user or service identity that created the session down to the [environment](https://code.claude.com/docs/en/self-hosted-environments#key-concepts) whose secret admitted the runner, and the identity that created that secret. The creator is the outermost actor, so `act.sub` identifies them directly.
207207 
208208| Path | Description |
209209| :- | :- |
from line 230
230230 
231231The creator identity also appears in plain environment variables on two surfaces that never verify the token:
232232 
233* **The [`spawn-runner` hook](/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook), on the orchestrator**: the hook runs before any runner exists for a queued session and receives the creator identity in variables such as `CLAUDE_RUNNER_ACCOUNT_EMAIL` and `CLAUDE_RUNNER_ACCOUNT_ID`. The orchestrator reads them from the work order, the signed single-use token that authorizes spawning one runner, without verifying the work order's signature itself; the claims are trusted because the work order arrives over the orchestrator's connection to Anthropic, which the environment secret authenticates.
234* **[Wrapper scripts](/docs/en/self-hosted-environments-configuration.md#wrapper-scripts), inside the session**: wrappers receive `CCR_SESSION_ACCOUNT_EMAIL`, the creator's email pre-extracted from the token without signature verification. The variable is suitable for labelling, such as commit trailers, not for auth decisions.
233* **The [`spawn-runner` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#the-spawn-runner-hook), on the orchestrator**: the hook runs before any runner exists for a queued session and receives the creator identity in variables such as `CLAUDE_RUNNER_ACCOUNT_EMAIL` and `CLAUDE_RUNNER_ACCOUNT_ID`. The orchestrator reads them from the work order, the signed single-use token that authorizes spawning one runner, without verifying the work order's signature itself; the claims are trusted because the work order arrives over the orchestrator's connection to Anthropic, which the environment secret authenticates.
234* **[Wrapper scripts](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts), inside the session**: wrappers receive `CCR_SESSION_ACCOUNT_EMAIL`, the creator's email pre-extracted from the token without signature verification. The variable is suitable for labelling, such as commit trailers, not for auth decisions.
235235 
236236Use the plain variables for orchestrator-side decisions such as selecting a machine image. Use `CLAUDE_CODE_SESSION_ACCESS_TOKEN` when a downstream service needs independent cryptographic proof rather than trusting the runner's environment.
237237 
238238## What's next
239239 
240* [Self-hosted environments](/docs/en/self-hosted-environments): the environment, runner, and session model; the [quickstart](/docs/en/self-hosted-environments-quickstart.md) and [Deploy to production](/docs/en/self-hosted-environments-deploy.md) hold setup and operations
241* [Customize sessions](/docs/en/self-hosted-environments-configuration.md): wrapper scripts that consume the token, and the `spawn-runner` hook
242* [Reference](/docs/en/self-hosted-environments-reference.md): CLI flags, environment variables, and metrics
240* [Self-hosted environments](https://code.claude.com/docs/en/self-hosted-environments): the environment, runner, and session model; the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md) and [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md) hold setup and operations
241* [Customize sessions](https://code.claude.com/docs/en/self-hosted-environments-configuration.md): wrapper scripts that consume the token, and the `spawn-runner` hook
242* [Reference](https://code.claude.com/docs/en/self-hosted-environments-reference.md): CLI flags, environment variables, and metrics
243243 

self-hosted-environments-quickstart Changed · +22 / -22 lines

from line 4
44 
55> [!NOTE]
66>
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page gets your first session running; see [Self-hosted environments](/docs/en/self-hosted-environments) for what they are and [Deploy to production](/docs/en/self-hosted-environments-deploy.md) for hardening and fleet recipes.
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](https://code.claude.com/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page gets your first session running; see [Self-hosted environments](https://code.claude.com/docs/en/self-hosted-environments) for what they are and [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md) for hardening and fleet recipes.
88 
9A [self-hosted environment](/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](/docs/en/claude-code-on-the-web.md) on infrastructure your organization operates, executed by runner processes you deploy. This quickstart stands up your first one, the smallest that works: one runner on a single host, running one test session. There are two steps: [create the environment, start a runner, and route a session to it](#set-up-an-environment-and-runner), then [message that session from your terminal](#send-a-follow-up-message-to-a-running-session). You'll move between two surfaces: claude.ai for creating the environment, checking its status, and routing a session, and a terminal on the host for everything the runner does.
9A [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) runs Claude Code [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md) on infrastructure your organization operates, executed by runner processes you deploy. This quickstart stands up your first one, the smallest that works: one runner on a single host, running one test session. There are two steps: [create the environment, start a runner, and route a session to it](#set-up-an-environment-and-runner), then [message that session from your terminal](#send-a-follow-up-message-to-a-running-session). You'll move between two surfaces: claude.ai for creating the environment, checking its status, and routing a session, and a terminal on the host for everything the runner does.
1010 
11By the end you'll have an environment on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), a runner polling for work, and a session running on your host. Before you connect real repositories or internal systems, work through [Deploy to production](/docs/en/self-hosted-environments-deploy.md), which covers the security posture, egress control, git credentials, and orchestration.
11By the end you'll have an environment on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments), a runner polling for work, and a session running on your host. Before you connect real repositories or internal systems, work through [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md), which covers the security posture, egress control, git credentials, and orchestration.
1212 
1313## Prerequisites
1414 
from line 16
1616 
1717The claude.ai side needs:
1818 
19* **Allow self-hosted environments** turned on by an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments); the **New** button doesn't appear until it is. If you don't hold the role, someone who does can create the environment and hand you its secret; the runner and terminal steps on this page need no claude.ai role, and where a step checks status in the admin UI, the runner's own log lines give you the same signal.
20* The [GitHub connector](https://claude.ai/admin-settings/connectors) turned on by an Owner, and each developer's own [GitHub connection](/docs/en/web-quickstart.md#connect-github), so they can pick github.com repositories when they start sessions.
19* **Allow self-hosted environments** turned on by an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments); the **New** button doesn't appear until it is. If you don't hold the role, someone who does can create the environment and hand you its secret; the runner and terminal steps on this page need no claude.ai role, and where a step checks status in the admin UI, the runner's own log lines give you the same signal.
20* The [GitHub connector](https://claude.ai/admin-settings/connectors) turned on by an Owner, and each developer's own [GitHub connection](https://code.claude.com/docs/en/web-quickstart.md#connect-github), so they can pick github.com repositories when they start sessions.
2121 
2222### Host and network
2323 
2424The runner host needs:
2525 
26* A Linux or macOS host or container with outbound HTTPS to `api.anthropic.com`, to `claude.ai` and the download hosts it redirects to for the install step below, and to your git host for the clone; the [network requirements table](/docs/en/self-hosted-environments-deploy.md#network-requirements) has the full list. Windows isn't supported as a runner host; run the runner in a Linux container instead. Developer workstations aren't affected, since sessions start from claude.ai in a browser.
27* A repository for the test session: a public one, or one this host can already clone by its HTTPS URL without being asked for credentials. An SSH key alone isn't enough, because the runner clones the repository's `https://` URL. To clone over SSH instead, see [Rewrite git URLs for private networks](/docs/en/self-hosted-environments-deploy.md#rewrite-git-urls-for-private-networks).
28* A clock synchronized to real time, for example with NTP. Authentication fails when the clock is more than five minutes off; see [Troubleshooting](/docs/en/self-hosted-environments-deploy.md#troubleshooting).
26* A Linux or macOS host or container with outbound HTTPS to `api.anthropic.com`, to `claude.ai` and the download hosts it redirects to for the install step below, and to your git host for the clone; the [network requirements table](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#network-requirements) has the full list. Windows isn't supported as a runner host; run the runner in a Linux container instead. Developer workstations aren't affected, since sessions start from claude.ai in a browser.
27* A repository for the test session: a public one, or one this host can already clone by its HTTPS URL without being asked for credentials. An SSH key alone isn't enough, because the runner clones the repository's `https://` URL. To clone over SSH instead, see [Rewrite git URLs for private networks](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#rewrite-git-urls-for-private-networks).
28* A clock synchronized to real time, for example with NTP. Authentication fails when the clock is more than five minutes off; see [Troubleshooting](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#troubleshooting).
2929 
3030### Software on the runner host
3131 
3232Install on the host before you start:
3333 
34* **Claude Code v2.1.224 or later**, with any of the [standard install methods](/docs/en/setup.md). The runner is part of the standard `claude` binary, and earlier versions don't recognize the `self-hosted-runner` subcommand. To pin the exact version your fleet runs, see [Install a specific version](/docs/en/setup.md#install-a-specific-version). For container images, see the Dockerfile in [Deploy to production](/docs/en/self-hosted-environments-deploy.md#build-the-runner-image).
35* **Git 2.24 or newer**. Some git options on the deploy page need newer versions; [Configure git](/docs/en/self-hosted-environments-deploy.md#configure-git) states each floor.
34* **Claude Code v2.1.224 or later**, with any of the [standard install methods](https://code.claude.com/docs/en/setup.md). The runner is part of the standard `claude` binary, and earlier versions don't recognize the `self-hosted-runner` subcommand. To pin the exact version your fleet runs, see [Install a specific version](https://code.claude.com/docs/en/setup.md#install-a-specific-version). For container images, see the Dockerfile in [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#build-the-runner-image).
35* **Git 2.24 or newer**. Some git options on the deploy page need newer versions; [Configure git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#configure-git) states each floor.
3636 
3737Confirm the host is ready:
3838 
from line 40
4040claude self-hosted-runner --help
4141```
4242 
43A ready host prints the runner's usage text, listing flags such as `--environment-secret-file`. On versions older than 2.1.224, the command prints the general `claude --help` output instead. If you see that output, [update Claude Code](/docs/en/setup.md#update-claude-code). If you run `claude` from a container image, [rebuild the image](/docs/en/self-hosted-environments-deploy.md#build-the-runner-image) with version 2.1.224 or later.
43A ready host prints the runner's usage text, listing flags such as `--environment-secret-file`. On versions older than 2.1.224, the command prints the general `claude --help` output instead. If you see that output, [update Claude Code](https://code.claude.com/docs/en/setup.md#update-claude-code). If you run `claude` from a container image, [rebuild the image](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#build-the-runner-image) with version 2.1.224 or later.
4444 
4545## Set up an environment and runner
4646 
from line 67
6767 
6868**1. Create an environment**
6969 
70Go to the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments) in admin settings. Under **Self-hosted environments**, select **New**, name the environment, and select **Create**. On the wizard's second step, select **Copy environment key** to copy the environment secret, which the admin UI labels an environment key. claude.ai shows the secret once, and you can't retrieve it later; it expires 365 days after creation. The environment's `ccpool_...` ID stays visible in its detail dialog; you'll need it for the `aud` check in [token verification](/docs/en/self-hosted-environments-identity.md) and for dispatching [test sessions from CI](/docs/en/self-hosted-environments-testing.md#run-the-test-loop).
70Go to the [**Cloud environments** page](https://claude.ai/admin-settings/cloud-environments) in admin settings. Under **Self-hosted environments**, select **New**, name the environment, and select **Create**. On the wizard's second step, select **Copy environment key** to copy the environment secret, which the admin UI labels an environment key. claude.ai shows the secret once, and you can't retrieve it later; it expires 365 days after creation. The environment's `ccpool_...` ID stays visible in its detail dialog; you'll need it for the `aud` check in [token verification](https://code.claude.com/docs/en/self-hosted-environments-identity.md) and for dispatching [test sessions from CI](https://code.claude.com/docs/en/self-hosted-environments-testing.md#run-the-test-loop).
7171 
7272If you lose the secret or need to rotate it, create a new secret from the environment's **Configuration** tab, roll the new secret out to your runners, then revoke the old one. Runners holding a revoked secret fail their next authenticated poll and exit, logging `poll auth failed`, and your orchestrator restarts them with the new secret.
7373 
from line 91
9191>
9292> Use a directory dedicated to the runner, not your home directory or a directory of your own clones. The runner treats whatever it finds at `<writable-dir>/<repo-owner>/<repo>` as its own clone, and can reset it and overwrite or delete files in it.
9393 
94If the runner can't create or write to the base directory, it exits at startup with an error naming the directory instead of registering. See [Troubleshooting](/docs/en/self-hosted-environments-deploy.md#troubleshooting).
94If the runner can't create or write to the base directory, it exits at startup with an error naming the directory instead of registering. See [Troubleshooting](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#troubleshooting).
9595 
9696Then start the runner with `--environment-secret-file` and `--base-dir`:
9797 
from line 113
113113 
114114If the session doesn't start working, match what you see:
115115 
116* **The session sits queued**: see [Troubleshooting](/docs/en/self-hosted-environments-deploy.md#troubleshooting).
116* **The session sits queued**: see [Troubleshooting](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#troubleshooting).
117117* **The session fails to start with a git error**: if the error includes git's `could not read Username for` followed by your git host's URL, the runner had no HTTPS credentials for that host.
118 * **Supply HTTPS credentials**: see [Configure git](/docs/en/self-hosted-environments-deploy.md#configure-git)
119 * **Clone over SSH instead**: if the runner host already has an SSH key, see [Rewrite git URLs for private networks](/docs/en/self-hosted-environments-deploy.md#rewrite-git-urls-for-private-networks)
118 * **Supply HTTPS credentials**: see [Configure git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#configure-git)
119 * **Clone over SSH instead**: if the runner host already has an SSH key, see [Rewrite git URLs for private networks](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#rewrite-git-urls-for-private-networks)
120120 
121121### If the runner exits
122122 
123123If the runner exits during this quickstart, start it again. The runner can exit on its own:
124124 
125* **Nothing left running**: the log shows `[runner:exit] account workload drained — exiting`. The runner [exits by design](/docs/en/self-hosted-environments#runner-lifecycle) once it's no longer running any session, for example:
125* **Nothing left running**: the log shows `[runner:exit] account workload drained — exiting`. The runner [exits by design](https://code.claude.com/docs/en/self-hosted-environments#runner-lifecycle) once it's no longer running any session, for example:
126126 * **You archived or deleted the session**: start the runner again
127127 * **The session failed to start**: [fix the cause](#session-doesnt-start), then start the runner again
128128* **Lost contact**: the log shows a `[runner:fatal]` line with `runner record gone server-side` or with `poll auth failed`. If the runner loses contact with Anthropic for a while, for example because the host sleeps, it can exit when it next reaches Anthropic.
from line 129
129129 
130130A finished turn doesn't end your test session. After the first turn the session is still attached and the runner is still up, so you can [send the session a follow-up message](#send-a-follow-up-message-to-a-running-session) without restarting the runner first.
131131 
132For production, deploy the runner under an orchestrator that restarts it on exit and waits longer between restarts when the runner keeps exiting right after it starts. See [Deploy to production](/docs/en/self-hosted-environments-deploy.md) and [When the runner exits](/docs/en/self-hosted-environments-deploy.md#when-the-runner-exits).
132For production, deploy the runner under an orchestrator that restarts it on exit and waits longer between restarts when the runner keeps exiting right after it starts. See [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md) and [When the runner exits](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#when-the-runner-exits).
133133 
134134## Send a follow-up message to a running session
135135 
from line 139
139139claude -p "your message" --cloud <session-id>
140140```
141141 
142For `<session-id>`, pass the bare `session_...` or `cse_...` ID or the session's claude.ai/code URL. A successful send prints `Sent to cloud session.` with the session ID and a view link. Accepted ID forms, JSON output, and the account and policy requirements are on [Send follow-ups from the CLI](/docs/en/claude-code-on-the-web.md#send-follow-ups-from-the-cli), since the command works the same against Anthropic-hosted sessions.
142For `<session-id>`, pass the bare `session_...` or `cse_...` ID or the session's claude.ai/code URL. A successful send prints `Sent to cloud session.` with the session ID and a view link. Accepted ID forms, JSON output, and the account and policy requirements are on [Send follow-ups from the CLI](https://code.claude.com/docs/en/claude-code-on-the-web.md#send-follow-ups-from-the-cli), since the command works the same against Anthropic-hosted sessions.
143143 
144144## What's next
145145 
146* [Deploy to production](/docs/en/self-hosted-environments-deploy.md): harden the deployment, control egress, configure git credentials, and run the fleet under Kubernetes or Compose
147* [Customize sessions](/docs/en/self-hosted-environments-configuration.md): wrapper scripts, lifecycle hooks, on-demand runners, MCP servers, and permissions
148* [Test end to end](/docs/en/self-hosted-environments-testing.md): a CI smoke test that dispatches a session and reads Claude's replies
146* [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md): harden the deployment, control egress, configure git credentials, and run the fleet under Kubernetes or Compose
147* [Customize sessions](https://code.claude.com/docs/en/self-hosted-environments-configuration.md): wrapper scripts, lifecycle hooks, on-demand runners, MCP servers, and permissions
148* [Test end to end](https://code.claude.com/docs/en/self-hosted-environments-testing.md): a CI smoke test that dispatches a session and reads Claude's replies
149149 

self-hosted-environments-reference Changed · +43 / -43 lines

from line 4
44 
55> [!NOTE]
66>
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](/docs/en/cloud-environments.md#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page is the flag and metric reference; see the [quickstart](/docs/en/self-hosted-environments-quickstart.md) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy.md) for the fleet recipes.
7> Self-hosted environments are in public beta on Team and Enterprise plans; an [Owner](https://code.claude.com/docs/en/cloud-environments.md#organization-shared-environments) enables them by turning on **Allow self-hosted environments** on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments). This page is the flag and metric reference; see the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md) for setup and [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md) for the fleet recipes.
88 
9This page is the reference for the two processes you run in a [self-hosted environment](/docs/en/self-hosted-environments): the runner, which executes Claude Code [cloud sessions](/docs/en/claude-code-on-the-web.md) on your hosts, and the optional autoscaling orchestrator, which starts runners as sessions queue. Each has its own flag table. Both run on Linux or macOS hosts, which the defaults such as `/workspace` and `~/.claude` assume. Run `claude self-hosted-runner --help` for the authoritative list on your installed version.
9This page is the reference for the two processes you run in a [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments): the runner, which executes Claude Code [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md) on your hosts, and the optional autoscaling orchestrator, which starts runners as sessions queue. Each has its own flag table. Both run on Linux or macOS hosts, which the defaults such as `/workspace` and `~/.claude` assume. Run `claude self-hosted-runner --help` for the authoritative list on your installed version.
1010 
1111Metric series and a few API fields still use `pool` for what these pages call an environment; both terms name the same thing. The environment ID is the `pool_id` field, with the form `ccpool_...`: wherever these pages show a `pool` identifier, it names the environment. CLI flags and environment variables spell it `environment`, such as `--environment-secret-file`; the deprecated `pool` spellings still work, as the [`--environment-secret-file` row](#runner-cli-flags) describes.
1212 
from line 17
1717| Flag | Env var | Default | Description |
1818| :- | :- | :- | :- |
1919| `--api-url <url>` | none | `https://api.anthropic.com` | API base URL. Override only for testing. |
20| `--base-dir <path>` | `SELF_HOSTED_RUNNER_BASE_DIR` | `/workspace`; none on Windows | Directory for repository checkouts and per-session working directories. The runner needs write access to this path or its parent. The runner creates the directory at startup and exits with `cannot create or write to base directory` when it can't create or write to it. Before v2.1.225, the runner created the directory when the first session started, so an unusable path failed sessions rather than startup. On Windows, which isn't a supported runner host, there is no default: the runner exits at startup unless you pass the flag or set the variable. Use the same value on every runner in an environment. See [Keep the base directory and capacity identical across runners](/docs/en/self-hosted-environments-deploy.md#keep-the-base-directory-and-capacity-identical-across-runners). |
21| `--capacity <n>` | none | `1` | Maximum concurrent sessions this runner handles. All sessions belong to the same locked [owner](/docs/en/self-hosted-environments#key-concepts). Use the same value on every runner in an environment; see [Keep the base directory and capacity identical across runners](/docs/en/self-hosted-environments-deploy.md#keep-the-base-directory-and-capacity-identical-across-runners). |
20| `--base-dir <path>` | `SELF_HOSTED_RUNNER_BASE_DIR` | `/workspace`; none on Windows | Directory for repository checkouts and per-session working directories. The runner needs write access to this path or its parent. The runner creates the directory at startup and exits with `cannot create or write to base directory` when it can't create or write to it. Before v2.1.225, the runner created the directory when the first session started, so an unusable path failed sessions rather than startup. On Windows, which isn't a supported runner host, there is no default: the runner exits at startup unless you pass the flag or set the variable. Use the same value on every runner in an environment. See [Keep the base directory and capacity identical across runners](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#keep-the-base-directory-and-capacity-identical-across-runners). |
21| `--capacity <n>` | none | `1` | Maximum concurrent sessions this runner handles. All sessions belong to the same locked [owner](https://code.claude.com/docs/en/self-hosted-environments#key-concepts). Use the same value on every runner in an environment; see [Keep the base directory and capacity identical across runners](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#keep-the-base-directory-and-capacity-identical-across-runners). |
2222| `--client-label <label>` | `SELF_HOSTED_RUNNER_CLIENT_LABEL` | the host's hostname | Label the runner sends when it registers. The runner also reports it as the `client_label` label of [`claude_code_self_hosted_runner_info`](#prometheus-metrics). Requires Claude Code v2.1.248 or later. |
23| `--configure-git` | `SELF_HOSTED_RUNNER_CONFIGURE_GIT=1` | off | At startup, write global git identity, enable Anthropic commit signing, turn on git push negotiation, and install commit hooks that append a `Co-authored-by:` trailer. Push negotiation requires Claude Code v2.1.257 or later. See [Configure git](/docs/en/self-hosted-environments-deploy.md#configure-git). |
24| `--confine-repo-settings <mode>` | `SELF_HOSTED_RUNNER_CONFINE_REPO_SETTINGS` | `warn` | Sets the mode of the guard that flags a session when a repository's committed settings try to grant write or read access outside that session's own workspace, set environment variables, or override the operator's sandbox or hooks posture, such as `sandbox.enabled: false` or `disableAllHooks`. The default `warn` logs the violation and still starts the session, `enforce` refuses the session, and `off` disables the scan. See [Harden your deployment](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment). |
23| `--configure-git` | `SELF_HOSTED_RUNNER_CONFIGURE_GIT=1` | off | At startup, write global git identity, enable Anthropic commit signing, turn on git push negotiation, and install commit hooks that append a `Co-authored-by:` trailer. Push negotiation requires Claude Code v2.1.257 or later. See [Configure git](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#configure-git). |
24| `--confine-repo-settings <mode>` | `SELF_HOSTED_RUNNER_CONFINE_REPO_SETTINGS` | `warn` | Sets the mode of the guard that flags a session when a repository's committed settings try to grant write or read access outside that session's own workspace, set environment variables, or override the operator's sandbox or hooks posture, such as `sandbox.enabled: false` or `disableAllHooks`. The default `warn` logs the violation and still starts the session, `enforce` refuses the session, and `off` disables the scan. See [Harden your deployment](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment). |
2525| `--debug-token-dir <path>` | `SELF_HOSTED_RUNNER_DEBUG_TOKEN_DIR` | unset | Write live tokens to disk for inspection. Debug only; don't use in production. |
26| `--defer-shutdown-max-min <n>` | `SELF_HOSTED_RUNNER_DEFER_SHUTDOWN_MAX_MS` | `0` | On the first `SIGTERM` or `SIGINT`, keep serving the sessions already attached instead of draining them, then release whatever is still attached N minutes later and exit. Raise your host's stop timeout before you set this. See [Defer the drain past the first signal](/docs/en/self-hosted-environments-deploy.md#defer-the-drain-past-the-first-signal). `0` disables. Requires Claude Code v2.1.238 or later. |
27| `--drain-grace-sec <n>` | `SELF_HOSTED_RUNNER_DRAIN_GRACE_MS` | `0` | Until the runner receives a shutdown signal or reaches its retire time, controls when the runner exits after its active sessions finish: `0` exits immediately without polling for more, and a positive value keeps the runner alive and re-polling the locked owner's queue for that many seconds first, at the cost of the per-session container isolation described in the [hardening section](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment). After a first signal you deferred with [`--defer-shutdown-max-min`](/docs/en/self-hosted-environments-deploy#defer-the-drain-past-the-first-signal), the runner exits as soon as it holds no sessions, whatever you set here. |
26| `--defer-shutdown-max-min <n>` | `SELF_HOSTED_RUNNER_DEFER_SHUTDOWN_MAX_MS` | `0` | On the first `SIGTERM` or `SIGINT`, keep serving the sessions already attached instead of draining them, then release whatever is still attached N minutes later and exit. Raise your host's stop timeout before you set this. See [Defer the drain past the first signal](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#defer-the-drain-past-the-first-signal). `0` disables. Requires Claude Code v2.1.238 or later. |
27| `--drain-grace-sec <n>` | `SELF_HOSTED_RUNNER_DRAIN_GRACE_MS` | `0` | Until the runner receives a shutdown signal or reaches its retire time, controls when the runner exits after its active sessions finish: `0` exits immediately without polling for more, and a positive value keeps the runner alive and re-polling the locked owner's queue for that many seconds first, at the cost of the per-session container isolation described in the [hardening section](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment). After a first signal you deferred with [`--defer-shutdown-max-min`](https://code.claude.com/docs/en/self-hosted-environments-deploy#defer-the-drain-past-the-first-signal), the runner exits as soon as it holds no sessions, whatever you set here. |
2828| `--drain-marker-file <path>` | `SELF_HOSTED_RUNNER_DRAIN_MARKER_FILE` | unset | Marker file that your host writes to announce a graceful drain before sending `SIGTERM`. When the file exists as the drain starts, the runner reports its exit to Anthropic as a host drain rather than a plain shutdown signal. The drain itself, including the `--drain-wait-sec` hold, runs the same as without the flag. Name a path on a local filesystem that sessions can't write to. Requires Claude Code v2.1.271 or later. |
29| `--drain-wait-sec <n>` | `SELF_HOSTED_RUNNER_DRAIN_WAIT_MS` | `0` | Once the drain starts, which is on `SIGTERM` unless you set [`--defer-shutdown-max-min`](/docs/en/self-hosted-environments-deploy#defer-the-drain-past-the-first-signal), wait up to N seconds for each session's in-flight turn and background tasks to finish before terminating the child. During this wait, the runner counts a background task that has just finished as still running until the follow-up turn that reads its result starts, for at most the [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](#environment-variable-only-settings) window. [Raise your stop timeout too](/docs/en/self-hosted-environments-deploy.md#let-running-turns-finish-before-a-restart). |
30| `--environment-secret-file <path>` | `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` | required | Path to a file containing the environment secret, or, for runners spawned by the [orchestrator](/docs/en/self-hosted-environments-configuration.md#on-demand-runners), the single-use work-order JWT. `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` carries the secret value directly, not a file path. The older `--pool-secret-file` flag and `SELF_HOSTED_RUNNER_POOL_SECRET` variable still work and print a deprecation notice to stderr; preview-program runner builds older than 2.1.216 only recognize those older names. |
31| `--exec-path <path>` | `SELF_HOSTED_RUNNER_EXEC_PATH` | own binary | Binary or wrapper script to spawn for each session. See [Wrapper scripts](/docs/en/self-hosted-environments-configuration.md#wrapper-scripts). |
29| `--drain-wait-sec <n>` | `SELF_HOSTED_RUNNER_DRAIN_WAIT_MS` | `0` | Once the drain starts, which is on `SIGTERM` unless you set [`--defer-shutdown-max-min`](https://code.claude.com/docs/en/self-hosted-environments-deploy#defer-the-drain-past-the-first-signal), wait up to N seconds for each session's in-flight turn and background tasks to finish before terminating the child. During this wait, the runner counts a background task that has just finished as still running until the follow-up turn that reads its result starts, for at most the [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](#environment-variable-only-settings) window. [Raise your stop timeout too](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#let-running-turns-finish-before-a-restart). |
30| `--environment-secret-file <path>` | `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` | required | Path to a file containing the environment secret, or, for runners spawned by the [orchestrator](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), the single-use work-order JWT. `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET` carries the secret value directly, not a file path. The older `--pool-secret-file` flag and `SELF_HOSTED_RUNNER_POOL_SECRET` variable still work and print a deprecation notice to stderr; preview-program runner builds older than 2.1.216 only recognize those older names. |
31| `--exec-path <path>` | `SELF_HOSTED_RUNNER_EXEC_PATH` | own binary | Binary or wrapper script to spawn for each session. See [Wrapper scripts](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts). |
3232| `--exit-if-unused-min <n>` | `SELF_HOSTED_RUNNER_IDLE_SHUTDOWN_MS` | `0` | Exit after N minutes of polling with no work ever assigned, for autoscaler scale-down. `0` disables. |
3333| `--git-host-rewrite <from>=<to>` | none | unset | Rewrite `https://<from>/...` source URLs to `https://<to>/...` before cloning, for split-horizon DNS. Repeatable; flag only. |
3434| `--git-ssh-rewrite <host>` | none | unset | Rewrite `https://<host>/...` source URLs to `git@<host>:...` before cloning, for SSH-only git hosts. Repeatable; flag only. |
3535| `--health-port <port>` | `SELF_HOSTED_RUNNER_HEALTH_PORT` | `8080` | Port for the `/healthz` and `/metrics` listener. Set `0` to disable. |
36| `--hooks-dir <path>` | `SELF_HOSTED_RUNNER_HOOKS_DIR` | unset | Directory of lifecycle hook scripts. See [Lifecycle hooks](/docs/en/self-hosted-environments-configuration.md#lifecycle-hooks). |
36| `--hooks-dir <path>` | `SELF_HOSTED_RUNNER_HOOKS_DIR` | unset | Directory of lifecycle hook scripts. See [Lifecycle hooks](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#lifecycle-hooks). |
3737| `--host-config-snapshot <mode>` | `SELF_HOSTED_RUNNER_HOST_CONFIG_SNAPSHOT` | `disk` | Where the runner keeps the startup snapshot of the [host config directory](#environment-variable-only-settings) that it seeds each session from. `disk` copies the snapshot into a runner-owned directory under `--base-dir` and, at each session start, verifies every file against an in-memory digest. If a file in the copy has been modified, the session fails and the runner refuses sessions until you restart it. `memory` holds the whole snapshot on the heap, capped at 64 MiB; over the cap, sessions start without host config and show a notice saying so. When the runner can't write the disk snapshot, it logs the failure and uses `memory` for that run. Requires Claude Code v2.1.271 or later. |
38| `--kill-session-after-min <n>` | `SELF_HOSTED_RUNNER_MAX_LIFETIME_MS` | `0` | Limit a session to N minutes wall-clock, as a safety limit for stuck sessions. On v2.1.260 or later, the runner releases a session that reaches the limit so it can resume on its user's next message, and terminates it only if it's still on the runner when the [`SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS`](#environment-variable-only-settings) grace window ends. Before v2.1.260, the runner terminated the session at the limit. See [Some sessions don't count as idle](/docs/en/self-hosted-environments-deploy.md#some-sessions-don’t-count-as-idle) for the details and how to choose a value. `0` disables. |
38| `--kill-session-after-min <n>` | `SELF_HOSTED_RUNNER_MAX_LIFETIME_MS` | `0` | Limit a session to N minutes wall-clock, as a safety limit for stuck sessions. On v2.1.260 or later, the runner releases a session that reaches the limit so it can resume on its user's next message, and terminates it only if it's still on the runner when the [`SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS`](#environment-variable-only-settings) grace window ends. Before v2.1.260, the runner terminated the session at the limit. See [Some sessions don't count as idle](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#some-sessions-don’t-count-as-idle) for the details and how to choose a value. `0` disables. |
3939| `--lock-to-account <id>` | `SELF_HOSTED_RUNNER_LOCK_TO_ACCOUNT` | unset | Pre-lock the runner to a specific account at startup instead of locking on first session. Accepts an email address or `user_...` ID in the environment's organization. A pre-locked runner never picks up Claude Tag channel sessions, which have no account. |
4040| `--log-file <path>` | `SELF_HOSTED_RUNNER_LOG_FILE` | unset | Mirror runner logs to a file in addition to stdout and stderr, created with `0600` permissions. Required for `self-hosted-runner doctor` to tail logs locally. |
4141| `--log-level <level>` | none | `info` | `info` or `debug` |
42| `--post-session-hook-timeout-sec <n>` | `SELF_HOSTED_RUNNER_POST_SESSION_HOOK_TIMEOUT_MS` | `60` | Budget for the [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session) on every session end, including runner shutdown |
43| `--proxy-authorization-command <command>` | `SELF_HOSTED_RUNNER_PROXY_AUTHORIZATION_COMMAND` | unset | Shell command the runner runs for every connection to your egress proxy, using its trimmed stdout as the `Proxy-Authorization` header value. Requires `HTTPS_PROXY` or `HTTP_PROXY`, and can't be combined with `--proxy-authorization-file`. See [Authenticate to an egress proxy](/docs/en/self-hosted-environments-deploy.md#authenticate-to-an-egress-proxy). Requires Claude Code v2.1.238 or later. |
44| `--proxy-authorization-file <path>` | `SELF_HOSTED_RUNNER_PROXY_AUTHORIZATION_FILE` | unset | File the runner reads for every connection to your egress proxy, using its trimmed contents as the `Proxy-Authorization` header value. Use this flag for a token another process rotates in place. Carries the same requirements as `--proxy-authorization-command`, and can't be combined with it. See [Authenticate to an egress proxy](/docs/en/self-hosted-environments-deploy.md#authenticate-to-an-egress-proxy). Requires Claude Code v2.1.238 or later. |
45| `--push-outcome-on-release` | `SELF_HOSTED_RUNNER_PUSH_OUTCOME_ON_RELEASE` | off | On a runner-initiated session end such as a drain or idle release, push tracked outcome branches to `origin` before deleting the workspace, so in-flight commits survive a restart. Best-effort; adds 30 seconds to the shutdown budget, and requires git 2.29 or newer to resume from the pushed branch. Restrict push access to `claude/*` refs before enabling; see [Resumed sessions lose unpushed work](/docs/en/self-hosted-environments-deploy.md#additional-limitations). Repositories checked out via a `checkout` lifecycle hook aren't pushed; snapshot those from the [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session) instead. |
46| `--release-idle-session-min <n>` | `SELF_HOSTED_RUNNER_SESSION_IDLE_MS` | `0` | Release a session slot after N minutes of inactivity once a turn finishes or the session waits for the user's action. A session that's still mid-turn, including one holding a never-finishing background task or an approval requested from inside a running tool call, doesn't count as idle; pair with `--kill-session-after-min` as the hard backstop. After a session's background task finishes, the runner considers the session busy until the follow-up turn that reads the result starts, for at most the [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](#environment-variable-only-settings) window. Until the runner receives a shutdown signal or reaches its retire time, a release that leaves the runner with no active sessions starts the same exit path as a normal drain, governed by `--drain-grace-sec`. After a first signal you deferred with [`--defer-shutdown-max-min`](/docs/en/self-hosted-environments-deploy#defer-the-drain-past-the-first-signal), the runner exits as soon as a release leaves it holding no sessions. `0` disables. |
47| `--remove-session-state [bool]` | `SELF_HOSTED_RUNNER_REMOVE_SESSION_STATE` | off | Remove a session's per-session directories under `<base-dir>/_sessions/` when the session ends on this runner, whatever the outcome. [Reuse a pre-warmed checkout](/docs/en/self-hosted-environments-deploy.md#reuse-a-pre-warmed-checkout) describes what they hold and who can read them when they stay. The removal is best-effort: the per-session directories stay in place when the runner is killed or reaches its drain deadline before cleanup runs. With the flag on, a failed or interrupted session's debug log isn't kept on disk. Requires Claude Code v2.1.268 or later. |
48| `--retire-at <epoch-seconds>` | `SELF_HOSTED_RUNNER_RETIRE_AT` | unset | Retire the runner at an absolute Unix timestamp in seconds, for infrastructure that kills the runner at a known time; [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle) describes the release sequence and how to size the margin. Values before 2001 or after the year 5138 are rejected by the flag and ignored by the environment variable. |
49| `--server-auto-mode-lists <mode>` | `SELF_HOSTED_RUNNER_SERVER_AUTO_MODE_LISTS` | `no-allow` | Which of the [auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) classifier rule lists that the control plane sends with a session may reach that session: `all`, `no-allow`, or `none`. See [Auto mode rule lists](#auto-mode-rule-lists) for what each value applies. An invalid value stops the runner at startup. Requires Claude Code v2.1.295 or later. |
42| `--post-session-hook-timeout-sec <n>` | `SELF_HOSTED_RUNNER_POST_SESSION_HOOK_TIMEOUT_MS` | `60` | Budget for the [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session) on every session end, including runner shutdown |
43| `--proxy-authorization-command <command>` | `SELF_HOSTED_RUNNER_PROXY_AUTHORIZATION_COMMAND` | unset | Shell command the runner runs for every connection to your egress proxy, using its trimmed stdout as the `Proxy-Authorization` header value. Requires `HTTPS_PROXY` or `HTTP_PROXY`, and can't be combined with `--proxy-authorization-file`. See [Authenticate to an egress proxy](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#authenticate-to-an-egress-proxy). Requires Claude Code v2.1.238 or later. |
44| `--proxy-authorization-file <path>` | `SELF_HOSTED_RUNNER_PROXY_AUTHORIZATION_FILE` | unset | File the runner reads for every connection to your egress proxy, using its trimmed contents as the `Proxy-Authorization` header value. Use this flag for a token another process rotates in place. Carries the same requirements as `--proxy-authorization-command`, and can't be combined with it. See [Authenticate to an egress proxy](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#authenticate-to-an-egress-proxy). Requires Claude Code v2.1.238 or later. |
45| `--push-outcome-on-release` | `SELF_HOSTED_RUNNER_PUSH_OUTCOME_ON_RELEASE` | off | On a runner-initiated session end such as a drain or idle release, push tracked outcome branches to `origin` before deleting the workspace, so in-flight commits survive a restart. Best-effort; adds 30 seconds to the shutdown budget, and requires git 2.29 or newer to resume from the pushed branch. Restrict push access to `claude/*` refs before enabling; see [Resumed sessions lose unpushed work](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#additional-limitations). Repositories checked out via a `checkout` lifecycle hook aren't pushed; snapshot those from the [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session) instead. |
46| `--release-idle-session-min <n>` | `SELF_HOSTED_RUNNER_SESSION_IDLE_MS` | `0` | Release a session slot after N minutes of inactivity once a turn finishes or the session waits for the user's action. A session that's still mid-turn, including one holding a never-finishing background task or an approval requested from inside a running tool call, doesn't count as idle; pair with `--kill-session-after-min` as the hard backstop. After a session's background task finishes, the runner considers the session busy until the follow-up turn that reads the result starts, for at most the [`SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS`](#environment-variable-only-settings) window. Until the runner receives a shutdown signal or reaches its retire time, a release that leaves the runner with no active sessions starts the same exit path as a normal drain, governed by `--drain-grace-sec`. After a first signal you deferred with [`--defer-shutdown-max-min`](https://code.claude.com/docs/en/self-hosted-environments-deploy#defer-the-drain-past-the-first-signal), the runner exits as soon as a release leaves it holding no sessions. `0` disables. |
47| `--remove-session-state [bool]` | `SELF_HOSTED_RUNNER_REMOVE_SESSION_STATE` | off | Remove a session's per-session directories under `<base-dir>/_sessions/` when the session ends on this runner, whatever the outcome. [Reuse a pre-warmed checkout](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#reuse-a-pre-warmed-checkout) describes what they hold and who can read them when they stay. The removal is best-effort: the per-session directories stay in place when the runner is killed or reaches its drain deadline before cleanup runs. With the flag on, a failed or interrupted session's debug log isn't kept on disk. Requires Claude Code v2.1.268 or later. |
48| `--retire-at <epoch-seconds>` | `SELF_HOSTED_RUNNER_RETIRE_AT` | unset | Retire the runner at an absolute Unix timestamp in seconds, for infrastructure that kills the runner at a known time; [Runner lifecycle](https://code.claude.com/docs/en/self-hosted-environments#runner-lifecycle) describes the release sequence and how to size the margin. Values before 2001 or after the year 5138 are rejected by the flag and ignored by the environment variable. |
49| `--server-auto-mode-lists <mode>` | `SELF_HOSTED_RUNNER_SERVER_AUTO_MODE_LISTS` | `no-allow` | Which of the [auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) classifier rule lists that the control plane sends with a session may reach that session: `all`, `no-allow`, or `none`. See [Auto mode rule lists](#auto-mode-rule-lists) for what each value applies. An invalid value stops the runner at startup. Requires Claude Code v2.1.295 or later. |
5050| `--session-stop-grace-sec <n>` | `SELF_HOSTED_RUNNER_SESSION_STOP_GRACE_MS` | `5` | How long to wait for the Claude process to exit cleanly after a session ends, before force-killing it. Raise the value if the child's own `SessionEnd` hooks need more time. |
51| `--startup-timeout-min <n>` | `SELF_HOSTED_RUNNER_STARTUP_TIMEOUT_MS` | `15` | Release a session slot if the child hasn't signaled on the [activity channel](/docs/en/self-hosted-environments-configuration.md#keep-stdin-and-file-descriptor-3-attached) that it initialized within N minutes. The N minutes start at spawn, after cloning, and start over at each `user` or `assistant` message the child emits before it has initialized or reported its state. Before v2.1.299, this timeout stopped applying at the session's first such message. For release after the init signal, see `--release-idle-session-min`. `0` disables. |
52| `--trust-workspace [bool]` | `SELF_HOSTED_RUNNER_TRUST_WORKSPACE` | on | Seed persisted trust for each session's repository paths so repo-committed `permissions.allow` and `additionalDirectories` are honored. Set `false` to drop repo-committed permission grants and configure allow rules in the host config's `settings.json` instead; repository-committed `sandbox.*` settings still apply either way, which is why the [repo-settings guard](/docs/en/self-hosted-environments-deploy.md#harden-your-deployment) scans them regardless of this flag. |
53| `--use-anthropic-git-proxy` | `CLAUDE_RUNNER_USE_GIT_PROXY=1` | off | Clone repositories on github.com via the [Anthropic git proxy](/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy) instead of customer-managed git auth. Requires `--capacity 1` and git 2.32 or later; the runner refuses to start otherwise. Supersedes the rewrite flags. |
51| `--startup-timeout-min <n>` | `SELF_HOSTED_RUNNER_STARTUP_TIMEOUT_MS` | `15` | Release a session slot if the child hasn't signaled on the [activity channel](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#keep-stdin-and-file-descriptor-3-attached) that it initialized within N minutes. The N minutes start at spawn, after cloning, and start over at each `user` or `assistant` message the child emits before it has initialized or reported its state. Before v2.1.299, this timeout stopped applying at the session's first such message. For release after the init signal, see `--release-idle-session-min`. `0` disables. |
52| `--trust-workspace [bool]` | `SELF_HOSTED_RUNNER_TRUST_WORKSPACE` | on | Seed persisted trust for each session's repository paths so repo-committed `permissions.allow` and `additionalDirectories` are honored. Set `false` to drop repo-committed permission grants and configure allow rules in the host config's `settings.json` instead; repository-committed `sandbox.*` settings still apply either way, which is why the [repo-settings guard](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#harden-your-deployment) scans them regardless of this flag. |
53| `--use-anthropic-git-proxy` | `CLAUDE_RUNNER_USE_GIT_PROXY=1` | off | Clone repositories on github.com via the [Anthropic git proxy](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#use-the-anthropic-git-proxy) instead of customer-managed git auth. Requires `--capacity 1` and git 2.32 or later; the runner refuses to start otherwise. Supersedes the rewrite flags. |
5454 
5555Most duration flags have a maximum, chosen to keep each timeout inside the runtime's 32-bit timer ceiling of roughly 24.85 days. The `--*-min` flags cap at 10080 minutes, 7 days; `--drain-grace-sec` at 604800 seconds, also 7 days; and `--drain-wait-sec` at 86400 seconds, 24 hours. `--session-stop-grace-sec` and `--post-session-hook-timeout-sec` are uncapped. Overrunning a cap behaves differently per surface:
5656 
from line 59
5959 
6060### Auto mode rule lists
6161 
62`--server-auto-mode-lists` lets you decide which [auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) classifier rules from outside the runner reach the sessions on your runners. Anthropic's control plane can send rule lists with a session and ask the runner to apply them. Some entries may be rules an admin of your organization wrote. The lists are `environment`, `soft_deny`, and `allow`:
62`--server-auto-mode-lists` lets you decide which [auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) classifier rules from outside the runner reach the sessions on your runners. Anthropic's control plane can send rule lists with a session and ask the runner to apply them. Some entries may be rules an admin of your organization wrote. The lists are `environment`, `soft_deny`, and `allow`:
6363 
6464* **`environment`**: an entry can make the classifier allow more as well as less.
6565* **`soft_deny`**: an entry blocks an action unless the user explicitly asked for it or an `allow` exception applies.
from line 75
7575 
7676## Orchestrator CLI flags
7777 
78The `self-hosted-runner orchestrator` subcommand, which spawns [on-demand runners](/docs/en/self-hosted-environments-configuration.md#on-demand-runners), accepts `--api-url`, `--environment-secret-file`, `--hooks-dir`, `--health-port`, and `--log-level` with the same defaults as the runner and, where the runner's flag has one, the same environment variable, except that `--hooks-dir` is required and must contain a `spawn-runner` hook. It also takes its own flags:
78The `self-hosted-runner orchestrator` subcommand, which spawns [on-demand runners](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners), accepts `--api-url`, `--environment-secret-file`, `--hooks-dir`, `--health-port`, and `--log-level` with the same defaults as the runner and, where the runner's flag has one, the same environment variable, except that `--hooks-dir` is required and must contain a `spawn-runner` hook. It also takes its own flags:
7979 
8080| Flag | Default | Description |
8181| :- | :- | :- |
from line 89
8989 
9090The SCM connector isn't available, so leave the flags in this section unset. If you set `--scm-connector-host`, the connection doesn't open, and the orchestrator keeps retrying it. Runners still start as sessions queue.
9191 
92The connector is a standing WebSocket connection from the orchestrator to Anthropic's control plane. It's designed to let hosted pre-session flows, such as the repository picker and the branch or ref resolver, reach a GitHub Enterprise Server host that's only routable from inside your network. See [Network requirements](/docs/en/github-enterprise-server.md#network-requirements) on the GitHub Enterprise Server page for what those flows need.
92The connector is a standing WebSocket connection from the orchestrator to Anthropic's control plane. It's designed to let hosted pre-session flows, such as the repository picker and the branch or ref resolver, reach a GitHub Enterprise Server host that's only routable from inside your network. See [Network requirements](https://code.claude.com/docs/en/github-enterprise-server.md#network-requirements) on the GitHub Enterprise Server page for what those flows need.
9393 
9494| Flag | Default | Description |
9595| :- | :- | :- |
from line 107
107107 
108108| Env var | Default | Description |
109109| :- | :- | :- |
110| `SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS` | `30000` | How long the runner considers a session busy after a background task finishes while the follow-up turn that reads the result hasn't started. The [`--drain-wait-sec` and `--release-idle-session-min` rows](#runner-cli-flags) describe where the hold applies on drain and idle release, and [Runner lifecycle](/docs/en/self-hosted-environments#runner-lifecycle) describes where it applies on `--retire-at` retirement. `0` or an unusable value falls back to the default, so the hold can't be turned off. Requires Claude Code v2.1.228 or later. |
111| `SELF_HOSTED_RUNNER_HOST_CONFIG_DIR` | `~/.claude` | Directory captured into the runner's startup snapshot and seeded into each session's `CLAUDE_CONFIG_DIR`; changes on disk apply after a runner restart. Setting the variable also moves where the runner reads `.claude.json` for [MCP seeding](/docs/en/self-hosted-environments-configuration.md#mcp-servers), so setting it, including to its own default, relocates that lookup; point at an empty directory to disable seeding entirely. |
110| `SELF_HOSTED_RUNNER_BG_RESULT_GRACE_MS` | `30000` | How long the runner considers a session busy after a background task finishes while the follow-up turn that reads the result hasn't started. The [`--drain-wait-sec` and `--release-idle-session-min` rows](#runner-cli-flags) describe where the hold applies on drain and idle release, and [Runner lifecycle](https://code.claude.com/docs/en/self-hosted-environments#runner-lifecycle) describes where it applies on `--retire-at` retirement. `0` or an unusable value falls back to the default, so the hold can't be turned off. Requires Claude Code v2.1.228 or later. |
111| `SELF_HOSTED_RUNNER_HOST_CONFIG_DIR` | `~/.claude` | Directory captured into the runner's startup snapshot and seeded into each session's `CLAUDE_CONFIG_DIR`; changes on disk apply after a runner restart. Setting the variable also moves where the runner reads `.claude.json` for [MCP seeding](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#mcp-servers), so setting it, including to its own default, relocates that lookup; point at an empty directory to disable seeding entirely. |
112112| `SELF_HOSTED_RUNNER_MAX_LIFETIME_GRACE_MS` | `900000` | How long the runner waits after a session reaches its `--kill-session-after-min` limit, for a running turn to finish or the release to complete, before it terminates the session |
113113| `SELF_HOSTED_RUNNER_POST_TURN_SETTLE_MS` | `7000` | Cap on how long the runner counts a session as busy for the `--drain-wait-sec` drain after a turn finishes, while the session's process reports the turn's end to Anthropic. `0` or an unusable value falls back to the default, so the hold can't be turned off. Requires Claude Code v2.1.275 or later. |
114114| `SELF_HOSTED_RUNNER_SIGKILL_GRACE_MS` | `30000` | How long the runner waits for the OS to deliver `SIGKILL` to a child stuck in uninterruptible I/O before exiting itself. Floored at `--post-session-hook-timeout-sec` plus 15 seconds, and 30 more when `--push-outcome-on-release` is set, so the effective minimum is 75 seconds at defaults. |
from line 122
122122 
123123Session children send operational telemetry to Anthropic unless you turn it off. No code or repository content is sent. Set telemetry variables on the runner process; the runner re-asserts them after applying server-provided environment variables, so the operator's setting always takes precedence.
124124 
125One control is specific to self-hosted environments: `CLAUDE_CODE_BYOC_ENABLE_DATADOG=1` opts in to Datadog operational metrics, which are off by default in self-hosted environments. The general Claude Code telemetry controls, `DISABLE_TELEMETRY`, `DO_NOT_TRACK`, `DISABLE_ERROR_REPORTING`, and `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, apply to session children as documented in the [environment variable reference](/docs/en/env-vars.md). `DISABLE_GROWTHBOOK` is related but different: setting `DISABLE_GROWTHBOOK=1` disables feature-flag fetching, and telemetry stays on unless `DISABLE_TELEMETRY` is also set.
125One control is specific to self-hosted environments: `CLAUDE_CODE_BYOC_ENABLE_DATADOG=1` opts in to Datadog operational metrics, which are off by default in self-hosted environments. The general Claude Code telemetry controls, `DISABLE_TELEMETRY`, `DO_NOT_TRACK`, `DISABLE_ERROR_REPORTING`, and `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, apply to session children as documented in the [environment variable reference](https://code.claude.com/docs/en/env-vars.md). `DISABLE_GROWTHBOOK` is related but different: setting `DISABLE_GROWTHBOOK=1` disables feature-flag fetching, and telemetry stays on unless `DISABLE_TELEMETRY` is also set.
126126 
127`CLAUDE_CODE_ENABLE_TELEMETRY` is unrelated: it enables OpenTelemetry export to your own collector, as described in [Monitoring](/docs/en/monitoring-usage.md), and doesn't control Anthropic's analytics.
127`CLAUDE_CODE_ENABLE_TELEMETRY` is unrelated: it enables OpenTelemetry export to your own collector, as described in [Monitoring](https://code.claude.com/docs/en/monitoring-usage.md), and doesn't control Anthropic's analytics.
128128 
129129## Health endpoint
130130 
from line 197
197197 
198198During a full poll outage, every replica disconnected, the gated query returns no data. HPA holds the current replica count on a missing metric, but KEDA's Prometheus scaler at its default `ignoreNullValues: "true"` reads the empty result as zero and scales in; set `ignoreNullValues: "false"` on the ScaledObject, optionally with a `fallback` replica floor.
199199 
200The following Prometheus Operator `PodMonitor` covers both processes. It selects pods by the `app.kubernetes.io/part-of: claude-code-self-hosted-runner` label and the named `health` port that the [Kubernetes recipe](/docs/en/self-hosted-environments-deploy.md#kubernetes) sets; adjust the namespaces to match your deployment:
200The following Prometheus Operator `PodMonitor` covers both processes. It selects pods by the `app.kubernetes.io/part-of: claude-code-self-hosted-runner` label and the named `health` port that the [Kubernetes recipe](https://code.claude.com/docs/en/self-hosted-environments-deploy.md#kubernetes) sets; adjust the namespaces to match your deployment:
201201 
202202```yaml theme={null}
203203# Example Prometheus Operator PodMonitor for the Claude Code self-hosted
from line 302
302302 
303303### Pass through session-child metrics
304304 
305Each session runs in its own child process with its own OpenTelemetry metrics; at `--capacity` above one, the runner rewrites how those child metrics are exposed. Setting `OTEL_METRICS_EXPORTER=prometheus` on the runner host and `CLAUDE_CODE_ENABLE_TELEMETRY=1` in the session's environment, for example from your [wrapper script](/docs/en/self-hosted-environments-configuration.md#wrapper-scripts) or the runner's own environment, which sessions inherit, re-exposes each child's counter and gauge instruments on the runner's own `/metrics` endpoint, alongside the runner's series. The runner rewrites the child's exporter to push over OTLP to a loopback-only receiver on the health port, tags each series with `session_id` and `client_platform` labels, and evicts a session's series when that session ends. Histograms don't pass through, and a child metric whose name would collide with the runner's own prefix is dropped.
305Each session runs in its own child process with its own OpenTelemetry metrics; at `--capacity` above one, the runner rewrites how those child metrics are exposed. Setting `OTEL_METRICS_EXPORTER=prometheus` on the runner host and `CLAUDE_CODE_ENABLE_TELEMETRY=1` in the session's environment, for example from your [wrapper script](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#wrapper-scripts) or the runner's own environment, which sessions inherit, re-exposes each child's counter and gauge instruments on the runner's own `/metrics` endpoint, alongside the runner's series. The runner rewrites the child's exporter to push over OTLP to a loopback-only receiver on the health port, tags each series with `session_id` and `client_platform` labels, and evicts a session's series when that session ends. Histograms don't pass through, and a child metric whose name would collide with the runner's own prefix is dropped.
306306 
307307At the default `--capacity 1`, the rewrite doesn't apply: the session's child binds its own Prometheus endpoint on port 9464 as usual.
308308 
from line 311
311311To put a session's own metrics beside `claude_code_self_hosted_runner_session_idle_seconds`, join on the runner's `session_id`:
312312 
313313* **At `--capacity` above one**: join directly. On the runner's `/metrics`, the session's series carry the same `session_id` as that metric, in `session_...` form.
314* **At `--capacity 1`**: join on a converted `ccr_session_id`. By default, each Claude Code metric on port 9464 carries that label, which holds the session ID in `cse_...` form. In a new label, replace its `cse_` prefix with `session_` to get the runner's `session_id`. The metric's own `session_id` holds Claude Code's own [`session.id`](/docs/en/monitoring-usage#standard-attributes), which matches no runner series.
314* **At `--capacity 1`**: join on a converted `ccr_session_id`. By default, each Claude Code metric on port 9464 carries that label, which holds the session ID in `cse_...` form. In a new label, replace its `cse_` prefix with `session_` to get the runner's `session_id`. The metric's own `session_id` holds Claude Code's own [`session.id`](https://code.claude.com/docs/en/monitoring-usage#standard-attributes), which matches no runner series.
315315 
316316### Session lifecycle counter semantics
317317 
from line 323
323323 
324324Before v2.1.260, the runner terminated every session that reached its `--kill-session-after-min` limit and counted it in `sessions_interrupted_total`.
325325 
326The [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session)'s `CLAUDE_RUNNER_EXIT_REASON` classifies clean handoffs differently. The hook reports these as `interrupted`, because the runner stopped the child: a release, a startup timeout, a server deassign, and an archive or delete that the poll noticed first. These counters record the same events as `completed`, because the slot was handed back cleanly.
326The [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session)'s `CLAUDE_RUNNER_EXIT_REASON` classifies clean handoffs differently. The hook reports these as `interrupted`, because the runner stopped the child: a release, a startup timeout, a server deassign, and an archive or delete that the poll noticed first. These counters record the same events as `completed`, because the slot was handed back cleanly.
327327 
328328If you reconcile hook receipts against `sessions_completed_total` directly, you undercount completions. Use the hook for per-session guarantees and the counters for aggregate rates.
329329 
from line 338
338338| Backlog | `claude_code_self_hosted_orchestrator_pool_pending_sessions` for queue depth, and `claude_code_self_hosted_orchestrator_queue_circuit_broken_sessions`, alerting if above zero |
339339| Failures | `claude_code_self_hosted_runner_sessions_failed_total`, best effort: real crashes after spawn do increment it, and `rate()` is meaningful on runners that outlive their sessions with `--drain-grace-sec` above `0`. A one-shot environment has the same scrape-window problem as the other terminal counters, so treat any non-zero value you do see as worth investigating. Failures before spawn, such as a checkout hook failure, git preparation, or a token issue, appear only in `session_init_errors_total`. |
340340 
341The `orchestrator_*` rows exist only on environments running the [on-demand orchestrator](/docs/en/self-hosted-environments-configuration.md#on-demand-runners). On a fixed fleet whose runners outlive their sessions, with `--drain-grace-sec` above `0`, use `sum(rate(claude_code_self_hosted_runner_sessions_started_total[5m]))` for throughput; on a one-shot fleet that series has the same scrape-window problem as the terminal counters, so rely on the queued-sessions count instead. Check backlog on the environment's **Activity** tab, on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments): the runners don't export a queue-depth series.
341The `orchestrator_*` rows exist only on environments running the [on-demand orchestrator](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#on-demand-runners). On a fixed fleet whose runners outlive their sessions, with `--drain-grace-sec` above `0`, use `sum(rate(claude_code_self_hosted_runner_sessions_started_total[5m]))` for throughput; on a one-shot fleet that series has the same scrape-window problem as the terminal counters, so rely on the queued-sessions count instead. Check backlog on the environment's **Activity** tab, on the [**Cloud environments** admin page](https://claude.ai/admin-settings/cloud-environments): the runners don't export a queue-depth series.
342342 
343For per-session outcome reporting, use the [`post-session` hook](/docs/en/self-hosted-environments-configuration#post-session) instead: it fires at every session end where a child process was spawned, apart from abrupt runner termination such as a VM preemption, per the [hook's own contract](/docs/en/self-hosted-environments-configuration.md#post-session).
343For per-session outcome reporting, use the [`post-session` hook](https://code.claude.com/docs/en/self-hosted-environments-configuration#post-session) instead: it fires at every session end where a child process was spawned, apart from abrupt runner termination such as a VM preemption, per the [hook's own contract](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#post-session).
344344 
345345## What's next
346346 
347* [Self-hosted environments](/docs/en/self-hosted-environments): the environment, runner, and session model; the [quickstart](/docs/en/self-hosted-environments-quickstart.md) and [Deploy to production](/docs/en/self-hosted-environments-deploy.md) hold setup and operations
348* [Customize sessions](/docs/en/self-hosted-environments-configuration.md): wrapper scripts, lifecycle hooks, and on-demand runners
349* [Verify session identity](/docs/en/self-hosted-environments-identity.md): the session token, its claims, and how to verify it
347* [Self-hosted environments](https://code.claude.com/docs/en/self-hosted-environments): the environment, runner, and session model; the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md) and [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md) hold setup and operations
348* [Customize sessions](https://code.claude.com/docs/en/self-hosted-environments-configuration.md): wrapper scripts, lifecycle hooks, and on-demand runners
349* [Verify session identity](https://code.claude.com/docs/en/self-hosted-environments-identity.md): the session token, its claims, and how to verify it
350350 

self-hosted-environments-testing Changed · +13 / -13 lines

from line 4
44 
55> [!NOTE]
66>
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page is the CI test recipe; see the [quickstart](/docs/en/self-hosted-environments-quickstart.md) for setup and [Deploy to production](/docs/en/self-hosted-environments-deploy.md) for the fleet recipes.
7> Self-hosted environments are in public beta on Team and Enterprise plans; [Availability and limitations](https://code.claude.com/docs/en/self-hosted-environments#availability-and-limitations) covers the enablement path. This page is the CI test recipe; see the [quickstart](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md) for setup and [Deploy to production](https://code.claude.com/docs/en/self-hosted-environments-deploy.md) for the fleet recipes.
88 
9In a [self-hosted environment](/docs/en/self-hosted-environments), Claude Code [cloud sessions](/docs/en/claude-code-on-the-web.md) run on a runner image you build and maintain. Before rolling a new image to your production environment, drive a full session against a test environment from a script: create a session, read Claude's reply, send a follow-up, and read that reply too. This is the shape of a CI smoke test that verifies your runner image, git access, and any custom tools before you promote a change.
9In a [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments), Claude Code [cloud sessions](https://code.claude.com/docs/en/claude-code-on-the-web.md) run on a runner image you build and maintain. Before rolling a new image to your production environment, drive a full session against a test environment from a script: create a session, read Claude's reply, send a follow-up, and read that reply too. This is the shape of a CI smoke test that verifies your runner image, git access, and any custom tools before you promote a change.
1010 
11This recipe assumes you've already [set up an environment and a runner](/docs/en/self-hosted-environments-quickstart.md#set-up-an-environment-and-runner), and that your CI job starts the runner process on the same host as the test script, the natural setup for testing a new runner image. A Stop hook you install on the runner writes each turn's final reply to a local file, and the script reads it from there, so the only calls to the Anthropic API are the two dispatches themselves. If your test runners are on separate infrastructure, see [Remote test runners](#remote-test-runners).
11This recipe assumes you've already [set up an environment and a runner](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md#set-up-an-environment-and-runner), and that your CI job starts the runner process on the same host as the test script, the natural setup for testing a new runner image. A Stop hook you install on the runner writes each turn's final reply to a local file, and the script reads it from there, so the only calls to the Anthropic API are the two dispatches themselves. If your test runners are on separate infrastructure, see [Remote test runners](#remote-test-runners).
1212 
1313## Install the capture hook on your test runner
1414 
15The read-back works through a Claude Code [Stop hook](/docs/en/hooks.md#stop): when Claude finishes a turn, the hook receives the final assistant message as `last_assistant_message` in its stdin JSON and appends it to `$E2E_REPLY_DIR/<session_id>.txt`. Install it the same way as the [commit-nudge Stop hook](/docs/en/self-hosted-environments-configuration.md#prompt-sessions-to-push-their-work), on the runner host's `~/.claude/`, which the runner seeds into every session.
15The read-back works through a Claude Code [Stop hook](https://code.claude.com/docs/en/hooks.md#stop): when Claude finishes a turn, the hook receives the final assistant message as `last_assistant_message` in its stdin JSON and appends it to `$E2E_REPLY_DIR/<session_id>.txt`. Install it the same way as the [commit-nudge Stop hook](https://code.claude.com/docs/en/self-hosted-environments-configuration.md#prompt-sessions-to-push-their-work), on the runner host's `~/.claude/`, which the runner seeds into every session.
1616 
1717### Save the hook files
1818 
from line 80
8080 * **Session creation failed**: the line `{"ok":false,"error":"..."}`, and the command exits with status 1
8181 * **Some earlier errors**, such as cloud sessions being unavailable for your organization or a missing prompt: the error on stderr with no JSON line, and the command exits with status 1
82822. Waits for the reply to appear in `$E2E_REPLY_DIR/<session_id>.txt`, written by the Stop hook on the runner once the turn completes.
833. Sends a follow-up with `claude -p "<message>" --cloud <session_id> --output-format json` (see [Send a follow-up message to a running session](/docs/en/claude-code-on-the-web.md#send-follow-ups-from-the-cli)), which posts a user event to the existing session and exits.
833. Sends a follow-up with `claude -p "<message>" --cloud <session_id> --output-format json` (see [Send a follow-up message to a running session](https://code.claude.com/docs/en/claude-code-on-the-web.md#send-follow-ups-from-the-cli)), which posts a user event to the existing session and exits.
84844. Waits for the follow-up's reply the same way as step 2.
8585 
8686### `--environment` dispatch behavior
from line 87
8787 
8888Claude Code creates the session, prints the session ID and a link to it, and exits.
8989 
90The flag takes precedence over the [`remote.defaultEnvironmentId`](/docs/en/settings-reference#remote-defaultenvironmentid) setting. It doesn't support `--output-format stream-json`, and can't be combined with flags that resume, attach to, or preconfigure a session, such as `--resume`, `--continue`, `--teleport`, `--session-id`, or `--init-only`. `--cloud` is rejected with a session ID or URL, and in non-interactive runs when it carries a description. A bare `--cloud` is treated as absent. From a terminal, you can pass the task as the `--cloud` description instead of a positional prompt.
90The flag takes precedence over the [`remote.defaultEnvironmentId`](https://code.claude.com/docs/en/settings-reference#remote-defaultenvironmentid) setting. It doesn't support `--output-format stream-json`, and can't be combined with flags that resume, attach to, or preconfigure a session, such as `--resume`, `--continue`, `--teleport`, `--session-id`, or `--init-only`. `--cloud` is rejected with a session ID or URL, and in non-interactive runs when it carries a description. A bare `--cloud` is treated as absent. From a terminal, you can pass the task as the `--cloud` description instead of a positional prompt.
9191 
9292## Example script
9393 
from line 194
194194 
195195### Long-lived CI host
196196 
197Run `claude auth login` once interactively on the machine that executes the script, using a dedicated user account for automation. Claude Code stores the token in the OS keychain on macOS, or in `~/.claude/.credentials.json` on Linux and Windows. On a macOS host whose Keychain can't be written, as is typical in an SSH session where the login Keychain stays locked, Claude Code stores the token in `~/.claude/.credentials.json` there too. See [Credential management](/docs/en/authentication.md#credential-management).
197Run `claude auth login` once interactively on the machine that executes the script, using a dedicated user account for automation. Claude Code stores the token in the OS keychain on macOS, or in `~/.claude/.credentials.json` on Linux and Windows. On a macOS host whose Keychain can't be written, as is typical in an SSH session where the login Keychain stays locked, Claude Code stores the token in `~/.claude/.credentials.json` there too. See [Credential management](https://code.claude.com/docs/en/authentication.md#credential-management).
198198 
199199The CLI refreshes the short-lived access token automatically on each invocation, but the underlying refresh-token grant is capped at 30 days from the initial login, so re-run `claude auth login` interactively on that host every 30 days.
200200 
201201### Ephemeral CI runners
202202 
203There is no long-lived CI token for this today. The scope that grants cloud-session control, `user:sessions:claude_code`, is capped server-side at 30 days, so `claude setup-token`, which mints a one-year inference-only token, doesn't cover it. The [environment secret](/docs/en/self-hosted-environments-quickstart.md#set-up-an-environment-and-runner) isn't accepted either, since it only authorizes a runner to register with the environment, not to create sessions.
203There is no long-lived CI token for this today. The scope that grants cloud-session control, `user:sessions:claude_code`, is capped server-side at 30 days, so `claude setup-token`, which mints a one-year inference-only token, doesn't cover it. The [environment secret](https://code.claude.com/docs/en/self-hosted-environments-quickstart.md#set-up-an-environment-and-runner) isn't accepted either, since it only authorizes a runner to register with the environment, not to create sessions.
204204 
205To provision a stored login onto an ephemeral runner, set [`CLAUDE_CODE_OAUTH_REFRESH_TOKEN` and `CLAUDE_CODE_OAUTH_SCOPES`](/docs/en/env-vars#variables) so `claude auth login` exchanges the token without a browser; the same 30-day cap applies to the refresh grant. Contact your Anthropic account team if you need a machine-identity path that isn't bound to a human account.
205To provision a stored login onto an ephemeral runner, set [`CLAUDE_CODE_OAUTH_REFRESH_TOKEN` and `CLAUDE_CODE_OAUTH_SCOPES`](https://code.claude.com/docs/en/env-vars#variables) so `claude auth login` exchanges the token without a browser; the same 30-day cap applies to the refresh grant. Contact your Anthropic account team if you need a machine-identity path that isn't bound to a human account.
206206 
207207## Create a dedicated test environment
208208 
from line 231
231231ENVIRONMENT_SECRET=$(jq -er .pool_secret <<<"$create")
232232```
233233 
234Until an [Owner turns on **Allow self-hosted environments**](/docs/en/self-hosted-environments#availability-and-limitations) for the organization, the call fails with a `403` `permission_error` reading `self-hosted runners are disabled by your organization's policy`.
234Until an [Owner turns on **Allow self-hosted environments**](https://code.claude.com/docs/en/self-hosted-environments#availability-and-limitations) for the organization, the call fails with a `403` `permission_error` reading `self-hosted runners are disabled by your organization's policy`.
235235 
236236Start a runner on this host with `SELF_HOSTED_RUNNER_ENVIRONMENT_SECRET=$ENVIRONMENT_SECRET`, plus the capture hook and `E2E_REPLY_DIR` per [Install the capture hook](#install-the-capture-hook-on-your-test-runner), then run the test script.
237237 

server-managed-settings Changed · +74 / -74 lines

from line 18
1818 
1919## Choose between server-managed and endpoint-managed settings
2020 
21Claude Code supports two approaches for centralized configuration. Server-managed settings deliver configuration from Anthropic's servers. [Endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms) are deployed directly to devices through native OS policies (macOS managed preferences, Windows registry) or managed settings files.
21Claude Code supports two approaches for centralized configuration. Server-managed settings deliver configuration from Anthropic's servers. [Endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) are deployed directly to devices through native OS policies (macOS managed preferences, Windows registry) or managed settings files.
2222 
2323| Approach | Best for | Security model |
2424| :- | :- | :- |
2525| **Server-managed settings** | Organizations without MDM, or users on unmanaged devices | Settings that Claude Code fetches from Anthropic's servers at startup and refreshes hourly during the session |
26| **[Endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms)** | Organizations with MDM or endpoint management | Settings deployed to devices via MDM configuration profiles, registry policies, or managed settings files |
26| **[Endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms)** | Organizations with MDM or endpoint management | Settings deployed to devices via MDM configuration profiles, registry policies, or managed settings files |
2727 
28If your devices are enrolled in an MDM or endpoint management solution, endpoint-managed settings provide stronger security guarantees because the settings file can be protected from user modification at the OS level. Endpoint-managed settings don't reach [cloud sessions](/docs/en/model-config.md#surface-coverage) in Anthropic-hosted environments, so organizations whose developers run cloud sessions should configure server-managed settings as well. Sessions in a [self-hosted environment](/docs/en/self-hosted-environments) also read the managed settings file in the runner image. The [settings precedence](#settings-precedence) below says when that file applies.
28If your devices are enrolled in an MDM or endpoint management solution, endpoint-managed settings provide stronger security guarantees because the settings file can be protected from user modification at the OS level. Endpoint-managed settings don't reach [cloud sessions](https://code.claude.com/docs/en/model-config.md#surface-coverage) in Anthropic-hosted environments, so organizations whose developers run cloud sessions should configure server-managed settings as well. Sessions in a [self-hosted environment](https://code.claude.com/docs/en/self-hosted-environments) also read the managed settings file in the runner image. The [settings precedence](#settings-precedence) below says when that file applies.
2929 
3030## Configure server-managed settings
3131 
from line 37
3737 
3838**2. Define your settings**
3939 
40Add your configuration as JSON. All [settings available in `settings.json`](/docs/en/settings-reference#all-settings) are supported except those restricted to OS-level policy delivery; see [Current limitations](#current-limitations) for that short list. This includes [hooks](/docs/en/hooks.md), [environment variables](/docs/en/env-vars.md), and [managed-only settings](/docs/en/managed-settings.md#managed-only-settings) like `allowManagedPermissionRulesOnly`.
40Add your configuration as JSON. All [settings available in `settings.json`](https://code.claude.com/docs/en/settings-reference#all-settings) are supported except those restricted to OS-level policy delivery; see [Current limitations](#current-limitations) for that short list. This includes [hooks](https://code.claude.com/docs/en/hooks.md), [environment variables](https://code.claude.com/docs/en/env-vars.md), and [managed-only settings](https://code.claude.com/docs/en/managed-settings.md#managed-only-settings) like `allowManagedPermissionRulesOnly`.
4141 
42This example enforces a permission deny list, prevents users from bypassing permissions, and restricts permission rules to those defined in managed settings. The `Bash(curl *)` rule matches `curl` [as Claude writes it](/docs/en/permissions.md#bash-rule-limits), not `/usr/bin/curl` or `sh -c 'curl …'`; for network enforcement that doesn't depend on the command text, add a [`sandbox` block with `allowManagedDomainsOnly`](/docs/en/sandboxing#configure-the-sandbox-for-your-organization).
42This example enforces a permission deny list, prevents users from bypassing permissions, and restricts permission rules to those defined in managed settings. The `Bash(curl *)` rule matches `curl` [as Claude writes it](https://code.claude.com/docs/en/permissions.md#bash-rule-limits), not `/usr/bin/curl` or `sh -c 'curl …'`; for network enforcement that doesn't depend on the command text, add a [`sandbox` block with `allowManagedDomainsOnly`](https://code.claude.com/docs/en/sandboxing#configure-the-sandbox-for-your-organization).
4343 
4444```json theme={null}
4545{
from line 77
7777 
7878Because hooks execute shell commands, users in interactive sessions see a [security approval dialog](#security-approval-dialogs) before Claude Code applies them.
7979 
80To configure the [auto mode](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) classifier so it knows which repos, buckets, and domains your organization trusts, deliver an `autoMode` block the same way; see [Configure auto mode](/docs/en/auto-mode-config.md) for how the `autoMode` entries affect what the classifier blocks and important warnings about the `environment`, `allow`, `soft_deny`, and `hard_deny` fields.
80To configure the [auto mode](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) classifier so it knows which repos, buckets, and domains your organization trusts, deliver an `autoMode` block the same way; see [Configure auto mode](https://code.claude.com/docs/en/auto-mode-config.md) for how the `autoMode` entries affect what the classifier blocks and important warnings about the `environment`, `allow`, `soft_deny`, and `hard_deny` fields.
8181 
8282**3. Save and deploy**
8383 
from line 85
8585 
8686The editor checks your JSON against the published JSON schema for Claude Code settings. If it finds a problem in JSON that parses, it shows a warning and relabels the save button. The label is **Update with errors** when settings are already saved, and **Add with errors** when no settings are saved yet. That button still saves, because a schema warning doesn't block the save.
8787 
88The schema [can lag behind the newest releases](/docs/en/settings.md#edit-a-settings-file), so the editor can flag a key or value that the [settings reference](/docs/en/settings-reference.md#all-settings) documents. Claude Code receives the keys and values you saved and runs [its own validation](#invalid-entries-in-delivered-settings) when it loads them.
88The schema [can lag behind the newest releases](https://code.claude.com/docs/en/settings.md#edit-a-settings-file), so the editor can flag a key or value that the [settings reference](https://code.claude.com/docs/en/settings-reference.md#all-settings) documents. Claude Code receives the keys and values you saved and runs [its own validation](#invalid-entries-in-delivered-settings) when it loads them.
8989 
9090### Verify settings delivery
9191 
from line 113
113113 
114114### Managed-only settings
115115 
116Most [settings keys](/docs/en/settings-reference.md#all-settings) work in any scope. A handful of keys are only read from managed settings and have no effect when placed in user or project settings files. See [managed-only settings](/docs/en/managed-settings.md#managed-only-settings) for the permission and plugin controls, or read the Scope column of the [All settings](/docs/en/settings-reference.md#all-settings) index for the full set.
116Most [settings keys](https://code.claude.com/docs/en/settings-reference.md#all-settings) work in any scope. A handful of keys are only read from managed settings and have no effect when placed in user or project settings files. See [managed-only settings](https://code.claude.com/docs/en/managed-settings.md#managed-only-settings) for the permission and plugin controls, or read the Scope column of the [All settings](https://code.claude.com/docs/en/settings-reference.md#all-settings) index for the full set.
117117 
118118### Current limitations
119119 
from line 120
120120Server-managed settings have the following limitations:
121121 
122122* Settings apply uniformly to all users in the organization. Per-group configurations are not yet supported.
123* You can't distribute a [`managed-mcp.json`](/docs/en/managed-mcp) file through server-managed settings. Deliver the `allowedMcpServers` and `deniedMcpServers` policy keys there instead. On Claude Code v2.1.259 or later, you can also provide remote servers with [`managedMcpServers`](/docs/en/managed-mcp#provide-servers-through-managed-settings), which accepts `http` and `sse` servers only and doesn't take exclusive control the way the file does.
123* You can't distribute a [`managed-mcp.json`](https://code.claude.com/docs/en/managed-mcp) file through server-managed settings. Deliver the `allowedMcpServers` and `deniedMcpServers` policy keys there instead. On Claude Code v2.1.259 or later, you can also provide remote servers with [`managedMcpServers`](https://code.claude.com/docs/en/managed-mcp#provide-servers-through-managed-settings), which accepts `http` and `sse` servers only and doesn't take exclusive control the way the file does.
124124 
125 Claude Code reads a `managed-mcp.json` deployed at its [system path](/docs/en/managed-mcp.md#exclusive-control-with-managed-mcp-json) separately from the managed settings tier, so the file still applies when server-managed settings are in effect.
126* Settings restricted to OS-level policy sources, such as `policyHelper` and `wslInheritsWindowsSettings`, aren't honored. Deploy them through MDM or a system `managed-settings.json` file instead. A `policyHelper` deployed that way runs only when its source is the one selected under [precedence within the managed tier](/docs/en/managed-settings.md#precedence-within-the-managed-tier).
125 Claude Code reads a `managed-mcp.json` deployed at its [system path](https://code.claude.com/docs/en/managed-mcp.md#exclusive-control-with-managed-mcp-json) separately from the managed settings tier, so the file still applies when server-managed settings are in effect.
126* Settings restricted to OS-level policy sources, such as `policyHelper` and `wslInheritsWindowsSettings`, aren't honored. Deploy them through MDM or a system `managed-settings.json` file instead. A `policyHelper` deployed that way runs only when its source is the one selected under [precedence within the managed tier](https://code.claude.com/docs/en/managed-settings.md#precedence-within-the-managed-tier).
127127 
128128## Settings delivery
129129 
130130### Settings precedence
131131 
132Server-managed settings and [endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms) both occupy the highest tier in the Claude Code [settings hierarchy](/docs/en/settings.md#settings-precedence). A key you set here takes precedence over the same key in a user's own settings files or in a `--settings` value, apart from the [exceptions to managed settings precedence](/docs/en/settings.md#exceptions-to-managed-settings-precedence).
132Server-managed settings and [endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) both occupy the highest tier in the Claude Code [settings hierarchy](https://code.claude.com/docs/en/settings.md#settings-precedence). A key you set here takes precedence over the same key in a user's own settings files or in a `--settings` value, apart from the [exceptions to managed settings precedence](https://code.claude.com/docs/en/settings.md#exceptions-to-managed-settings-precedence).
133133 
134Within the managed tier, Claude Code by default uses the first source that delivers at least one policy key, checking server-managed settings first and then endpoint-managed settings, apart from the [exception keys covered next](#per-key-exceptions-across-managed-sources). [How Claude Code combines managed sources](/docs/en/managed-settings.md#precedence-within-the-managed-tier) has the full ranking, the carve-out for the control keys, and the opt-in that applies every source.
134Within the managed tier, Claude Code by default uses the first source that delivers at least one policy key, checking server-managed settings first and then endpoint-managed settings, apart from the [exception keys covered next](#per-key-exceptions-across-managed-sources). [How Claude Code combines managed sources](https://code.claude.com/docs/en/managed-settings.md#precedence-within-the-managed-tier) has the full ranking, the carve-out for the control keys, and the opt-in that applies every source.
135135 
136If the selected source is an MDM policy or managed settings file whose [`policyHelper`](/docs/en/settings-reference#policyhelper) supplies managed settings, the helper's output replaces that source as the only managed configuration for the run. Claude Code doesn't consult a `policyHelper` configured in MDM or file-based settings while server-managed settings deliver a policy key.
136If the selected source is an MDM policy or managed settings file whose [`policyHelper`](https://code.claude.com/docs/en/settings-reference#policyhelper) supplies managed settings, the helper's output replaces that source as the only managed configuration for the run. Claude Code doesn't consult a `policyHelper` configured in MDM or file-based settings while server-managed settings deliver a policy key.
137137 
138If a later fetch finds the server-managed settings removed, Claude Code runs that helper right away instead of at the next launch. The [`policyHelper`](/docs/en/settings-reference#policyhelper) entry covers what happens when that run fails.
138If a later fetch finds the server-managed settings removed, Claude Code runs that helper right away instead of at the next launch. The [`policyHelper`](https://code.claude.com/docs/en/settings-reference#policyhelper) entry covers what happens when that run fails.
139139 
140140If you clear your server-managed configuration in the admin console with the intent of falling back to an endpoint-managed plist or registry policy, be aware that [cached settings](#fetch-and-caching-behavior) persist on client machines until the next successful fetch, and the keys that [apply only at the next launch](#fetch-and-caching-behavior), such as `model`, stay in effect until each client relaunches. Run `/status` to see which managed source is active.
141141 
from line 143
143143 
144144These keys are exceptions to the no-merge rule:
145145 
146* **Cross-source lock keys**: a small set of keys, such as the sandbox allowlist locks, [listed on the managed settings page](/docs/en/managed-settings.md#precedence-within-the-managed-tier). Claude Code honors them when any admin-controlled managed source sets them; the user-writable HKCU registry tier is excluded.
146* **Cross-source lock keys**: a small set of keys, such as the sandbox allowlist locks, [listed on the managed settings page](https://code.claude.com/docs/en/managed-settings.md#precedence-within-the-managed-tier). Claude Code honors them when any admin-controlled managed source sets them; the user-writable HKCU registry tier is excluded.
147147 
148 When a [`policyHelper`](/docs/en/settings-reference#policyhelper) supplies managed settings, its output is the only source these checks read, apart from [`forceRemoteSettingsRefresh`](/docs/en/settings-reference#forceremotesettingsrefresh), which Claude Code reads from the admin sources directly at startup.
148 When a [`policyHelper`](https://code.claude.com/docs/en/settings-reference#policyhelper) supplies managed settings, its output is the only source these checks read, apart from [`forceRemoteSettingsRefresh`](https://code.claude.com/docs/en/settings-reference#forceremotesettingsrefresh), which Claude Code reads from the admin sources directly at startup.
149149* **The `env` block**: apart from the telemetry unit and routing variables paired with a credential key, both covered below, it merges per key across the admin-controlled sources. For each environment variable, the highest-priority source defining it wins, and lower admin sources fill in variables the higher sources leave unset. An endpoint-managed `env` entry therefore applies whenever the server-managed configuration leaves that variable unset, or while a cached server value for it is [withheld pending server confirmation](#fetch-and-caching-behavior). Requires Claude Code v2.1.223 or later. Before v2.1.223, Claude Code applies the selected source's whole `env` block only.
150150 * **Telemetry unit**: the `OTEL_EXPORTER_OTLP_*` exporter keys, the `OTEL_LOG_*` content-capture toggles, `OTEL_LOGS_EXPORTER`, and the beta tracing variables `ENABLE_BETA_TRACING_DETAILED` and `BETA_TRACING_ENDPOINT` follow the highest source that sets any of them as a unit. A source that delivers the `otelHeadersHelper` credential key claims the unit too, but lands these variables only when it is the selected source: a source that isn't selected but delivers the key contributes none of them and still blocks lower sources from filling them in. Either way, an exporter endpoint from one source can never pair with credentials from another.
151151 * **Credential-paired routing**: a source that pairs routing variables with a selected-source-only credential key, such as `apiKeyHelper` or `otelHeadersHelper`, contributes those routing variables only when it wins the slot.
152* **`allowedProviders`**: a list set on the machine and a server-managed list combine as [its entry's Scope note](/docs/en/settings-reference.md#allowedproviders) states. Requires Claude Code v2.1.285 or later
153* **Gateway sign-in keys**: Claude Code never reads [`forceLoginGatewayUrl`](/docs/en/settings-reference#forcelogingatewayurl), [`gatewayInternalNetworks`](/docs/en/settings-reference#gatewayinternalnetworks), or the `"gateway"` value of [`forceLoginMethod`](/docs/en/settings-reference#forceloginmethod) from server-managed settings, so a value there neither applies nor hides one set in an MDM policy or managed settings file. The [`managedSourcesBehavior` entry](/docs/en/settings-reference#managedsourcesbehavior) says which admin source on the machine supplies them.
152* **`allowedProviders`**: a list set on the machine and a server-managed list combine as [its entry's Scope note](https://code.claude.com/docs/en/settings-reference.md#allowedproviders) states. Requires Claude Code v2.1.285 or later
153* **Gateway sign-in keys**: Claude Code never reads [`forceLoginGatewayUrl`](https://code.claude.com/docs/en/settings-reference#forcelogingatewayurl), [`gatewayInternalNetworks`](https://code.claude.com/docs/en/settings-reference#gatewayinternalnetworks), or the `"gateway"` value of [`forceLoginMethod`](https://code.claude.com/docs/en/settings-reference#forceloginmethod) from server-managed settings, so a value there neither applies nor hides one set in an MDM policy or managed settings file. The [`managedSourcesBehavior` entry](https://code.claude.com/docs/en/settings-reference#managedsourcesbehavior) says which admin source on the machine supplies them.
154154 
155155### Fetch and caching behavior
156156 
from line 160
160160 
161161**First launch without cached settings:**
162162 
163* When a developer signs in at startup, such as on a first run or after `/logout`, Claude Code waits up to five seconds for the fetch before it opens the session. When the policy arrives in time, Claude Code enforces it from the first screen and shows your [`companyAnnouncements`](/docs/en/settings-reference#companyannouncements) on it. When the payload needs [security approval](#security-approval-dialogs), Claude Code ends the wait and applies the payload once the developer approves
163* When a developer signs in at startup, such as on a first run or after `/logout`, Claude Code waits up to five seconds for the fetch before it opens the session. When the policy arrives in time, Claude Code enforces it from the first screen and shows your [`companyAnnouncements`](https://code.claude.com/docs/en/settings-reference#companyannouncements) on it. When the payload needs [security approval](#security-approval-dialogs), Claude Code ends the wait and applies the payload once the developer approves
164164* In any other startup, and when that five-second wait runs out, Claude Code opens the session while the fetch continues, so a brief window passes before the settings load and the restrictions take effect
165165* If the fetch fails, Claude Code continues without server-managed settings and warns in interactive sessions that no remote policy applies; endpoint-managed settings still apply. If a managed source sets [`forceRemoteSettingsRefresh`](#enforce-fail-closed-startup), Claude Code exits instead
166166 
from line 167
167167**Subsequent launches with cached settings:**
168168 
169169* Cached settings apply immediately at startup, except for the cached `modelPricing` and `managedMcpServers` values and the environment variables that Claude Code withholds until the server confirms the payload
170* A cached [`modelPricing`](/docs/en/settings-reference#modelpricing) doesn't apply until the session's fetch confirms the payload. Until then, the cost figures developers see in `/usage` and the status line are at list price
171* A cached [`managedMcpServers`](/docs/en/settings-reference#managedmcpservers) block doesn't apply until the session's fetch confirms the payload. Claude Code waits up to 30 seconds for that fetch before connecting MCP servers. If the fetch fails or times out, the session starts without the organization's servers, `/status` says so, and they connect once a later fetch confirms them. See [When provided servers connect](/docs/en/managed-mcp.md#when-provided-servers-connect) for the full behavior, including first launch. Requires Claude Code v2.1.259 or later
170* A cached [`modelPricing`](https://code.claude.com/docs/en/settings-reference#modelpricing) doesn't apply until the session's fetch confirms the payload. Until then, the cost figures developers see in `/usage` and the status line are at list price
171* A cached [`managedMcpServers`](https://code.claude.com/docs/en/settings-reference#managedmcpservers) block doesn't apply until the session's fetch confirms the payload. Claude Code waits up to 30 seconds for that fetch before connecting MCP servers. If the fetch fails or times out, the session starts without the organization's servers, `/status` says so, and they connect once a later fetch confirms them. See [When provided servers connect](https://code.claude.com/docs/en/managed-mcp.md#when-provided-servers-connect) for the full behavior, including first launch. Requires Claude Code v2.1.259 or later
172172* Claude Code fetches fresh settings in the background
173173* Cached settings persist through network failures. If the startup fetch fails, Claude Code warns in interactive sessions that the cached policy is in effect
174174* Until a fetch succeeds, the values withheld at startup stay withheld
175175 
176Claude Code withholds several categories of variables in the cached `env` block until the server confirms the payload for the session. This keeps a cached proxy, certificate authority, endpoint, or credential value from redirecting, intercepting, or re-authenticating the settings fetch that confirms the payload. The hardening applies only to the server-fetched settings cache: [endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms) deployed through MDM or `managed-settings.json` are unaffected. The withheld categories include:
176Claude Code withholds several categories of variables in the cached `env` block until the server confirms the payload for the session. This keeps a cached proxy, certificate authority, endpoint, or credential value from redirecting, intercepting, or re-authenticating the settings fetch that confirms the payload. The hardening applies only to the server-fetched settings cache: [endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) deployed through MDM or `managed-settings.json` are unaffected. The withheld categories include:
177177 
178178* Proxy and TLS configuration, such as `HTTPS_PROXY`, `NODE_EXTRA_CA_CERTS`, and the mTLS client certificate variables `CLAUDE_CODE_CLIENT_CERT` and `CLAUDE_CODE_CLIENT_KEY`
179179* API routing and provider selection, including `ANTHROPIC_BASE_URL`, the provider selection variables such as `CLAUDE_CODE_USE_BEDROCK` and `CLAUDE_CODE_USE_VERTEX`, and the provider endpoint URLs such as `ANTHROPIC_BEDROCK_BASE_URL`
from line 181
181181* The configuration-directory selector `CLAUDE_CONFIG_DIR`
182182* Credential-source and configuration-directory selectors, in Claude Code v2.1.223 or later: the Workload Identity Federation variables such as `ANTHROPIC_FEDERATION_RULE_ID` and `ANTHROPIC_IDENTITY_TOKEN`, the profile and configuration-directory selectors `ANTHROPIC_PROFILE` and `ANTHROPIC_CONFIG_DIR`, and the operating-system directory variables `HOME`, `XDG_CONFIG_HOME`, `APPDATA`, and `USERPROFILE`
183183 
184Claude Code reads the Workload Identity Federation variables and the `ANTHROPIC_PROFILE` and `ANTHROPIC_CONFIG_DIR` selectors only at startup, so a server-delivered value for them doesn't switch the session's credential source even after the fetch succeeds. To deliver those selectors on Claude Code v2.1.223 or later, use [endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms) such as MDM or `managed-settings.json`. For `CLAUDE_CONFIG_DIR` and the operating-system directory variables, the withholding itself is the protection: the cached value stays out of the environment until the server confirms the payload.
184Claude Code reads the Workload Identity Federation variables and the `ANTHROPIC_PROFILE` and `ANTHROPIC_CONFIG_DIR` selectors only at startup, so a server-delivered value for them doesn't switch the session's credential source even after the fetch succeeds. To deliver those selectors on Claude Code v2.1.223 or later, use [endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) such as MDM or `managed-settings.json`. For `CLAUDE_CONFIG_DIR` and the operating-system directory variables, the withholding itself is the protection: the cached value stays out of the environment until the server confirms the payload.
185185 
186186Every other key in the cached `env` block applies at startup. Once the server confirms the payload, and you approve it if it needs [security approval](#security-approval-dialogs), the withheld variables apply for the rest of the session.
187187 
188If your organization needs a proxy to reach `api.anthropic.com`, the withholding only affects the server-delivered `env` block itself: a proxy set in an [endpoint-managed](/docs/en/managed-settings.md#delivery-mechanisms) `env` block through MDM or `managed-settings.json`, in the shell environment, or in [user settings](/docs/en/settings.md#where-settings-live) reaches the settings fetch. The endpoint-managed source requires Claude Code v2.1.223 or later: the cached server-managed proxy value is withheld until the fetch confirms it, so the endpoint-managed value fills in per key and reaches the fetch itself. Before v2.1.223, use the shell environment or user settings so the proxy applies alongside a cached server payload. The first launch has no cache, so an endpoint-managed source, the shell environment, or user settings is still required for the initial fetch.
188If your organization needs a proxy to reach `api.anthropic.com`, the withholding only affects the server-delivered `env` block itself: a proxy set in an [endpoint-managed](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) `env` block through MDM or `managed-settings.json`, in the shell environment, or in [user settings](https://code.claude.com/docs/en/settings.md#where-settings-live) reaches the settings fetch. The endpoint-managed source requires Claude Code v2.1.223 or later: the cached server-managed proxy value is withheld until the fetch confirms it, so the endpoint-managed value fills in per key and reaches the fetch itself. Before v2.1.223, use the shell environment or user settings so the proxy applies alongside a cached server payload. The first launch has no cache, so an endpoint-managed source, the shell environment, or user settings is still required for the initial fetch.
189189 
190190Claude Code applies most settings updates to running sessions without a restart. Some updates apply only at the next launch, including OpenTelemetry exporter configuration, the `model` key, and the removal of a variable from the `env` block.
191191 
192192### Invalid entries in delivered settings
193193 
194When part of a payload fails schema validation, Claude Code surfaces a validation error and applies every remaining valid setting; [Invalid entries in managed settings](/docs/en/managed-settings.md#invalid-entries-in-managed-settings) says what it drops and which keys fall back to a stricter value. Requires Claude Code v2.1.169 or later.
194When part of a payload fails schema validation, Claude Code surfaces a validation error and applies every remaining valid setting; [Invalid entries in managed settings](https://code.claude.com/docs/en/managed-settings.md#invalid-entries-in-managed-settings) says what it drops and which keys fall back to a stricter value. Requires Claude Code v2.1.169 or later.
195195 
196196Server-managed delivery adds these behaviors:
197197 
198198* A startup that runs on the cache at `~/.claude/remote-settings.json` treats invalid entries the way the fetch that wrote the cache did:
199199 * Entries that failed validation stay dropped.
200 * [Keys that fail closed](/docs/en/managed-settings.md#keys-that-fail-closed) keep their stricter values.
200 * [Keys that fail closed](https://code.claude.com/docs/en/managed-settings.md#keys-that-fail-closed) keep their stricter values.
201201 * An invalid `cleanupPeriodDays` or `desktopSessionCleanupPeriodDays` value stays in the cached copy and is never applied.
202202* Claude Code applies nothing from a payload and leaves the cache unchanged when all three of these are true:
203203 
from line 205
205205 * None of them falls back to a stricter value.
206206 * The payload holds a key other than those two retention keys.
207207 
208 The startup notice, `/status`, and `claude doctor` then report the [failed load](/docs/en/errors.md#remote-managed-settings-failed-to-load) with the cause `no setting in the server response could be applied as written`, and that entry says which policy the session runs on. Clients that [enforce fail-closed startup](#enforce-fail-closed-startup) exit at startup instead.
208 The startup notice, `/status`, and `claude doctor` then report the [failed load](https://code.claude.com/docs/en/errors.md#remote-managed-settings-failed-to-load) with the cause `no setting in the server response could be applied as written`, and that entry says which policy the session runs on. Clients that [enforce fail-closed startup](#enforce-fail-closed-startup) exit at startup instead.
209209* The [security approval dialog](#security-approval-dialogs) evaluates the salvaged payload, so a stripped invalid entry is never presented for approval and never executes.
210210 
211211To debug delivery issues, run `claude --debug-file <path>` and search the log for `Remote settings`. Validate a payload change with `claude doctor` on a test machine before rolling it out to the organization.
from line 212
212212 
213213### Enforce fail-closed startup
214214 
215By default, if the remote settings fetch fails at startup, the CLI continues with the settings cached from the last successful fetch, except for the [values Claude Code withholds](#fetch-and-caching-behavior) until a fetch succeeds. On a machine that has never fetched them, the CLI continues without server-managed settings and still applies any [endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms) on the device.
215By default, if the remote settings fetch fails at startup, the CLI continues with the settings cached from the last successful fetch, except for the [values Claude Code withholds](#fetch-and-caching-behavior) until a fetch succeeds. On a machine that has never fetched them, the CLI continues without server-managed settings and still applies any [endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) on the device.
216216 
217217To stop clients from starting on cached or absent server-managed settings, set `forceRemoteSettingsRefresh: true` in your managed settings.
218218 
219219Clients signed in through a [Claude apps gateway](#platform-availability) wait for the startup fetch whether or not you set this, and handle a failed fetch as follows:
220220 
221* If the gateway answers an attended interactive launch with a `401` and this setting is off, the gateway has ended that sign-in. Claude Code prints [`Cloud gateway session expired — run /login to reconnect.`](/docs/en/errors#cloud-gateway-session-expired) and opens the session signed out of the gateway until the user runs `/login`.
221* If the gateway answers an attended interactive launch with a `401` and this setting is off, the gateway has ended that sign-in. Claude Code prints [`Cloud gateway session expired — run /login to reconnect.`](https://code.claude.com/docs/en/errors#cloud-gateway-session-expired) and opens the session signed out of the gateway until the user runs `/login`.
222222* When the fetch fails in any other way, or in any other kind of launch except a `claude auth` subcommand, the client exits with an error.
223223 
224224When this setting is active in a session that fetches server-managed settings, the CLI blocks at startup until remote settings are freshly fetched. If the fetch fails, the CLI exits rather than proceeding without the policy. This setting self-perpetuates: once delivered from the server, it is also cached locally so that subsequent startups enforce the same behavior even before the first successful fetch of a new session. A session that [doesn't fetch server-managed settings](#platform-availability) starts without waiting.
from line 231
231231}
232232```
233233 
234You can also set this key in an [endpoint-managed](/docs/en/managed-settings.md#delivery-mechanisms) MDM profile or system `managed-settings.json` file to enforce fail-closed behavior on first launch, before any server payload has arrived. This flag is an exception to the [precedence rule](#settings-precedence) above: Claude Code honors it when any admin-controlled managed source sets it, even if a cached server-managed payload is also present, so it doesn't ignore an MDM-delivered value when server-managed settings exist.
234You can also set this key in an [endpoint-managed](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) MDM profile or system `managed-settings.json` file to enforce fail-closed behavior on first launch, before any server payload has arrived. This flag is an exception to the [precedence rule](#settings-precedence) above: Claude Code honors it when any admin-controlled managed source sets it, even if a cached server-managed payload is also present, so it doesn't ignore an MDM-delivered value when server-managed settings exist.
235235 
236When a [`policyHelper`](/docs/en/settings-reference#policyhelper) supplies managed settings, its output replaces every other managed source for the keys Claude Code reads after startup. For the sources Claude Code reads this key from, see [its settings entry](/docs/en/settings-reference.md#forceremotesettingsrefresh). The `policyHelper` entry says which sources Claude Code reads the helper from and when it runs.
236When a [`policyHelper`](https://code.claude.com/docs/en/settings-reference#policyhelper) supplies managed settings, its output replaces every other managed source for the keys Claude Code reads after startup. For the sources Claude Code reads this key from, see [its settings entry](https://code.claude.com/docs/en/settings-reference.md#forceremotesettingsrefresh). The `policyHelper` entry says which sources Claude Code reads the helper from and when it runs.
237237 
238238The settings fetch also sends a `Cache-Control: no-cache` header so intermediate HTTP proxies don't serve a stale response.
239239 
from line 247
247247 
248248* **Shell command settings**: settings that execute shell commands, such as `apiKeyHelper`, `statusLine`, and `otelHeadersHelper`
249249* **Sandbox binary settings**: `sandbox.bwrapPath`, `sandbox.socatPath`, and `sandbox.ripgrep`. Each of these settings points at an executable, and Claude Code runs that executable
250* **Sandbox network and isolation settings**: [sandbox](/docs/en/sandboxing.md) settings that let the sandbox proxy read, reroute, or authenticate traffic, or that weaken the sandbox's isolation: `sandbox.network.tlsTerminate`, `sandbox.network.httpProxyPort`, `sandbox.network.socksProxyPort`, `sandbox.credentials`, `sandbox.allowAppleEvents`, `sandbox.allowNestedUserNamespaces`, `sandbox.enableWeakerNestedSandbox`, `sandbox.enableWeakerNetworkIsolation`, `sandbox.filesystem.disabled`, `sandbox.network.allowAllUnixSockets`, `sandbox.network.allowUnixSockets`, and `sandbox.network.allowMachLookup`. A `sandbox.credentials` block that contains only `deny` rules doesn't need approval, since it restricts the sandbox without giving the proxy a credential. Before v2.1.251, Claude Code applied these settings without approval
250* **Sandbox network and isolation settings**: [sandbox](https://code.claude.com/docs/en/sandboxing.md) settings that let the sandbox proxy read, reroute, or authenticate traffic, or that weaken the sandbox's isolation: `sandbox.network.tlsTerminate`, `sandbox.network.httpProxyPort`, `sandbox.network.socksProxyPort`, `sandbox.credentials`, `sandbox.allowAppleEvents`, `sandbox.allowNestedUserNamespaces`, `sandbox.enableWeakerNestedSandbox`, `sandbox.enableWeakerNetworkIsolation`, `sandbox.filesystem.disabled`, `sandbox.network.allowAllUnixSockets`, `sandbox.network.allowUnixSockets`, and `sandbox.network.allowMachLookup`. A `sandbox.credentials` block that contains only `deny` rules doesn't need approval, since it restricts the sandbox without giving the proxy a credential. Before v2.1.251, Claude Code applied these settings without approval
251251* **Custom environment variables**: delivered `env` variables that require the user's approval, such as proxy and base-URL variables; see [Environment variables and the approval dialog](#environment-variables-and-the-approval-dialog)
252252* **Hook configurations**: any hook definition
253253 
254254When these settings are present, users see a security dialog explaining what is being configured. Users must approve to proceed. If a user rejects the settings, Claude Code exits.
255255 
256A managed CLAUDE.md delivered through the [`claudeMd`](/docs/en/settings-reference#claudemd) key doesn't require approval, because it's instruction text for Claude rather than a command Claude Code runs. Claude Code still checks [permissions](/docs/en/permissions.md) for the tools Claude uses while following those instructions. Before v2.1.260, a `claudeMd` value required approval too.
256A managed CLAUDE.md delivered through the [`claudeMd`](https://code.claude.com/docs/en/settings-reference#claudemd) key doesn't require approval, because it's instruction text for Claude rather than a command Claude Code runs. Claude Code still checks [permissions](https://code.claude.com/docs/en/permissions.md) for the tools Claude uses while following those instructions. Before v2.1.260, a `claudeMd` value required approval too.
257257 
258258#### Approval memory
259259 
260Claude Code records your approval in your configuration directory, `~/.claude` unless you set [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars). What it records depends on the credential the settings fetch uses:
260Claude Code records your approval in your configuration directory, `~/.claude` unless you set [`CLAUDE_CONFIG_DIR`](https://code.claude.com/docs/en/env-vars). What it records depends on the credential the settings fetch uses:
261261 
262* **A claude.ai login saved by `/login` or `claude auth login`, or the [keyless Console sign-in](/docs/en/authentication.md#sign-in-without-an-api-key)**: one approval per organization, held by the account that approved most recently.
263* **A [Claude apps gateway](/docs/en/claude-apps-gateway) sign-in**: one approval per gateway.
262* **A claude.ai login saved by `/login` or `claude auth login`, or the [keyless Console sign-in](https://code.claude.com/docs/en/authentication.md#sign-in-without-an-api-key)**: one approval per organization, held by the account that approved most recently.
263* **A [Claude apps gateway](https://code.claude.com/docs/en/claude-apps-gateway) sign-in**: one approval per gateway.
264264 
265265 If you sign out and back in to the same gateway, Claude Code doesn't show the dialog again while the settings that require approval are unchanged. Claude Code shows it again when those settings change, when you sign in to a different gateway, and when you accept a new certificate for the same gateway.
266266 
from line 267
267267 Claude Code saves no approval for a loopback development gateway reached over plain HTTP, so the dialog appears again after each sign-in.
268268* **Any other credential**, such as an API key or `CLAUDE_CODE_OAUTH_TOKEN`: one approval for the delivered settings, kept with the cached copy of the settings in that configuration directory. Claude Code shows the dialog again when the settings that require approval change, and after you run `/logout` or `claude auth logout`, either of which deletes the cached copy.
269269 
270An approval for `sandbox.credentials` or `sandbox.network.tlsTerminate` also covers the [`sandbox.network.allowedDomains`](/docs/en/settings-reference#sandbox-network-alloweddomains) entries in those same delivered settings, because both settings act on that allowlist. The dialog appears again when your administrator adds or removes one of those entries, even though `sandbox.network.allowedDomains` doesn't require approval on its own.
270An approval for `sandbox.credentials` or `sandbox.network.tlsTerminate` also covers the [`sandbox.network.allowedDomains`](https://code.claude.com/docs/en/settings-reference#sandbox-network-alloweddomains) entries in those same delivered settings, because both settings act on that allowlist. The dialog appears again when your administrator adds or removes one of those entries, even though `sandbox.network.allowedDomains` doesn't require approval on its own.
271271 
272272With a saved claude.ai login:
273273 
from line 277
277277Claude Code can't always show the dialog. Each case below says which settings apply when it can't and when you next see the dialog:
278278 
279279* **An interactive session that can't show the dialog**: Claude Code doesn't apply the delivered settings and keeps the last-approved settings. The dialog appears in the next session that can show it.
280* **`claude install` or `claude update`**: Claude Code doesn't show the dialog during either command. The command runs with the last-approved settings, and the dialog appears in your next interactive session. If Claude Code waits for the settings fetch at startup, such as with [`forceRemoteSettingsRefresh`](#enforce-fail-closed-startup) set or on a [Claude apps gateway](/docs/en/claude-apps-gateway) deployment, it shows the dialog during the command instead, and an install run from a pipe fails; see [`Raw mode is not supported` during install](/docs/en/troubleshoot-install#raw-mode-is-not-supported-during-install). Before v2.1.246, Claude Code tried to show the dialog during these commands too.
281* **A `claude plugin` command that prints to a terminal but can't read from the keyboard**: when Claude Code doesn't wait for the settings fetch at startup, the command doesn't apply the delivered settings. Without [cached settings](#fetch-and-caching-behavior), a command that changes your plugins or marketplaces [stops with an error](/docs/en/errors.md#managed-settings-need-your-approval). With cached settings, it runs with the last-approved settings. The dialog appears when you next run `claude` in a terminal.
280* **`claude install` or `claude update`**: Claude Code doesn't show the dialog during either command. The command runs with the last-approved settings, and the dialog appears in your next interactive session. If Claude Code waits for the settings fetch at startup, such as with [`forceRemoteSettingsRefresh`](#enforce-fail-closed-startup) set or on a [Claude apps gateway](https://code.claude.com/docs/en/claude-apps-gateway) deployment, it shows the dialog during the command instead, and an install run from a pipe fails; see [`Raw mode is not supported` during install](https://code.claude.com/docs/en/troubleshoot-install#raw-mode-is-not-supported-during-install). Before v2.1.246, Claude Code tried to show the dialog during these commands too.
281* **A `claude plugin` command that prints to a terminal but can't read from the keyboard**: when Claude Code doesn't wait for the settings fetch at startup, the command doesn't apply the delivered settings. Without [cached settings](#fetch-and-caching-behavior), a command that changes your plugins or marketplaces [stops with an error](https://code.claude.com/docs/en/errors.md#managed-settings-need-your-approval). With cached settings, it runs with the last-approved settings. The dialog appears when you next run `claude` in a terminal.
282282 
283283 The command can't read from the keyboard when you pipe or redirect its input and, on macOS and Linux, it also can't open `/dev/tty`. If you pipe or redirect its output too, the run is non-interactive instead, and the delivered settings apply for that run only.
284284* **An error closes the dialog before you answer**: Claude Code doesn't apply the delivered settings and keeps the last-approved settings. It shows the dialog again in the next session that can show it.
from line 304
304304 
305305Claude Code decides whether four privacy toggles need approval by the delivered value rather than by the variable name: `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`, `DISABLE_ERROR_REPORTING`, `DISABLE_TELEMETRY`, and `DO_NOT_TRACK`. A truthy value such as `1` or `true` only turns tracking, reporting, or other nonessential traffic off, so Claude Code applies it without asking the user. For any other non-empty value, Claude Code shows the dialog. Before v2.1.218, all of them except `DO_NOT_TRACK` applied without approval at any value, and `DO_NOT_TRACK` triggered the dialog at any non-empty value.
306306 
307Claude Code also decides whether [`API_FORCE_IDLE_TIMEOUT`](/docs/en/env-vars) needs approval by the delivered value: a truthy value only turns the [body idle timeout](/docs/en/network-config.md#streaming-idle-watchdogs) on, so Claude Code applies it without asking the user. For any other non-empty value, Claude Code shows the dialog. Before v2.1.248, any non-empty value triggered the dialog.
307Claude Code also decides whether [`API_FORCE_IDLE_TIMEOUT`](https://code.claude.com/docs/en/env-vars) needs approval by the delivered value: a truthy value only turns the [body idle timeout](https://code.claude.com/docs/en/network-config.md#streaming-idle-watchdogs) on, so Claude Code applies it without asking the user. For any other non-empty value, Claude Code shows the dialog. Before v2.1.248, any non-empty value triggered the dialog.
308308 
309Whether [`ANTHROPIC_CUSTOM_HEADERS`](/docs/en/env-vars#variables) needs approval also depends on the delivered value. Headers that only tag requests, such as `Accept-Language`, apply without the dialog. A line that names a credential, an org or tenant selector, a routing or host override, or an API-behavior header, such as `Authorization`, `X-Api-Key`, `Host`, `anthropic-beta`, or the `X-Amzn-Bedrock-*` headers, requires approval. A line also requires approval when its name isn't a valid HTTP header token or its value contains a character an HTTP header can't carry. The check matches words inside the header name, so `X-Client-Version`, which contains `client` and `version`, requires approval too. Before v2.1.251, any `ANTHROPIC_CUSTOM_HEADERS` value applied without it.
309Whether [`ANTHROPIC_CUSTOM_HEADERS`](https://code.claude.com/docs/en/env-vars#variables) needs approval also depends on the delivered value. Headers that only tag requests, such as `Accept-Language`, apply without the dialog. A line that names a credential, an org or tenant selector, a routing or host override, or an API-behavior header, such as `Authorization`, `X-Api-Key`, `Host`, `anthropic-beta`, or the `X-Amzn-Bedrock-*` headers, requires approval. A line also requires approval when its name isn't a valid HTTP header token or its value contains a character an HTTP header can't carry. The check matches words inside the header name, so `X-Client-Version`, which contains `client` and `version`, requires approval too. Before v2.1.251, any `ANTHROPIC_CUSTOM_HEADERS` value applied without it.
310310 
311A falsy value such as `0` or `false` for [`ENABLE_BETA_TRACING_DETAILED`](/docs/en/env-vars#variables) or [`OTEL_LOG_RAW_API_BODIES`](/docs/en/env-vars#variables) applies without the dialog, because it only turns detailed tracing or raw API body capture off. Any other non-empty value for either variable requires approval.
311A falsy value such as `0` or `false` for [`ENABLE_BETA_TRACING_DETAILED`](https://code.claude.com/docs/en/env-vars#variables) or [`OTEL_LOG_RAW_API_BODIES`](https://code.claude.com/docs/en/env-vars#variables) applies without the dialog, because it only turns detailed tracing or raw API body capture off. Any other non-empty value for either variable requires approval.
312312 
313313## Platform availability
314314 
from line 317
317317* A Team or Enterprise OAuth login
318318* An OAuth token supplied through `CLAUDE_CODE_OAUTH_TOKEN`
319319* A directly configured API key
320* A `user_oauth` [Anthropic profile](/docs/en/authentication.md#anthropic-profiles-and-federation-credentials), unless the profile sets a `base_url` other than the Anthropic API. Requires Claude Code v2.1.257 or later.
320* A `user_oauth` [Anthropic profile](https://code.claude.com/docs/en/authentication.md#anthropic-profiles-and-federation-credentials), unless the profile sets a `base_url` other than the Anthropic API. Requires Claude Code v2.1.257 or later.
321321 
322Neither keys returned by an [`apiKeyHelper`](/docs/en/settings-reference#apikeyhelper) script nor [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation) credentials trigger the settings fetch.
322Neither keys returned by an [`apiKeyHelper`](https://code.claude.com/docs/en/settings-reference#apikeyhelper) script nor [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation) credentials trigger the settings fetch.
323323 
324A session receives the managed settings of the organization that owns the credential it authenticates with. An API key from the [Claude Console](https://platform.claude.com) belongs to the Console organization it was created in, which is a separate organization from your claude.ai Team or Enterprise organization. Settings you configure in claude.ai Admin Settings therefore don't reach a session that authenticates with that key, such as a CI job that uses your company's Console API key. To apply them to that job, use one of these options. The OAuth token option doesn't apply to a job that runs with [`--bare`](/docs/en/headless#start-faster-with-bare-mode), because bare mode doesn't read `CLAUDE_CODE_OAUTH_TOKEN`.
324A session receives the managed settings of the organization that owns the credential it authenticates with. An API key from the [Claude Console](https://platform.claude.com) belongs to the Console organization it was created in, which is a separate organization from your claude.ai Team or Enterprise organization. Settings you configure in claude.ai Admin Settings therefore don't reach a session that authenticates with that key, such as a CI job that uses your company's Console API key. To apply them to that job, use one of these options. The OAuth token option doesn't apply to a job that runs with [`--bare`](https://code.claude.com/docs/en/headless#start-faster-with-bare-mode), because bare mode doesn't read `CLAUDE_CODE_OAUTH_TOKEN`.
325325 
326* **OAuth token**: generate a token with [`claude setup-token`](/docs/en/authentication#generate-a-long-lived-token), authorize it for your Team or Enterprise organization, and set it as `CLAUDE_CODE_OAUTH_TOKEN` in the job's environment. Remove any credential that [takes precedence](/docs/en/authentication.md#authentication-precedence) over the token from that environment, such as `ANTHROPIC_API_KEY`.
327* **Endpoint-managed settings**: deploy a [managed settings file](/docs/en/managed-settings.md#delivery-mechanisms) to the machine that runs the job.
326* **OAuth token**: generate a token with [`claude setup-token`](https://code.claude.com/docs/en/authentication#generate-a-long-lived-token), authorize it for your Team or Enterprise organization, and set it as `CLAUDE_CODE_OAUTH_TOKEN` in the job's environment. Remove any credential that [takes precedence](https://code.claude.com/docs/en/authentication.md#authentication-precedence) over the token from that environment, such as `ANTHROPIC_API_KEY`.
327* **Endpoint-managed settings**: deploy a [managed settings file](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) to the machine that runs the job.
328328 
329In a [Cowork](https://claude.com/docs/cowork/overview) session in the Claude Desktop app, Claude Code doesn't fetch server-managed settings from the claude.ai admin console, even when the user signs in with a Team or Enterprise account. [Where and when a policy applies](/docs/en/managed-settings.md#where-and-when-a-policy-applies) covers which policy reaches Cowork sessions on the user's machine and remote Cowork sessions. claude.ai still applies your [`strictKnownMarketplaces`](/docs/en/settings-reference#strictknownmarketplaces) and [`blockedMarketplaces`](/docs/en/settings-reference#blockedmarketplaces) lists itself when a Cowork user adds a marketplace from a git repository on claude.ai or from **Customize** in Cowork. [How restrictions work](/docs/en/plugins/org.md#restrict-what-users-can-install) describes that check.
329In a [Cowork](https://claude.com/docs/cowork/overview) session in the Claude Desktop app, Claude Code doesn't fetch server-managed settings from the claude.ai admin console, even when the user signs in with a Team or Enterprise account. [Where and when a policy applies](https://code.claude.com/docs/en/managed-settings.md#where-and-when-a-policy-applies) covers which policy reaches Cowork sessions on the user's machine and remote Cowork sessions. claude.ai still applies your [`strictKnownMarketplaces`](https://code.claude.com/docs/en/settings-reference#strictknownmarketplaces) and [`blockedMarketplaces`](https://code.claude.com/docs/en/settings-reference#blockedmarketplaces) lists itself when a Cowork user adds a marketplace from a git repository on claude.ai or from **Customize** in Cowork. [How restrictions work](https://code.claude.com/docs/en/plugins/org.md#restrict-what-users-can-install) describes that check.
330330 
331331If you export a `CLAUDE_CODE_USE_*` provider variable or a non-default `ANTHROPIC_BASE_URL` in your shell, Claude Code skips the settings fetch for your sessions. [`claude doctor` and `/status` report the skipped fetch and its cause](#verify-settings-delivery).
332332 
333You can't clear the export with a server-managed `env` block, because the block arrives through the fetch that the export prevents. An [endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms) `env` block doesn't restore the fetch either: Claude Code checks eligibility before it applies managed `env` blocks, so the endpoint-managed value changes the session's provider selection but the fetch stays skipped.
333You can't clear the export with a server-managed `env` block, because the block arrives through the fetch that the export prevents. An [endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) `env` block doesn't restore the fetch either: Claude Code checks eligibility before it applies managed `env` blocks, so the endpoint-managed value changes the session's provider selection but the fetch stays skipped.
334334 
335335To restore server-managed delivery, remove the export from your shell, or set the variable to `""` in your user settings `env` block, which applies before the eligibility check. To enforce policy without relying on users to change their shells, deliver the settings through the endpoint-managed channel instead.
336336 
337For Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and [Claude Platform on AWS](/docs/en/claude-platform-on-aws.md) deployments, a self-hosted [Claude apps gateway](/docs/en/claude-apps-gateway) provides the equivalent remote managed-settings delivery: gateway-signed-in clients fetch managed settings from the gateway instead of `api.anthropic.com`. The failure semantics differ at startup: a gateway client that can't reach the gateway exits with an error instead of falling back to cached settings, while the hourly background refresh is fail-open on both channels.
337For Amazon Bedrock, Google Cloud's Agent Platform, Microsoft Foundry, and [Claude Platform on AWS](https://code.claude.com/docs/en/claude-platform-on-aws.md) deployments, a self-hosted [Claude apps gateway](https://code.claude.com/docs/en/claude-apps-gateway) provides the equivalent remote managed-settings delivery: gateway-signed-in clients fetch managed settings from the gateway instead of `api.anthropic.com`. The failure semantics differ at startup: a gateway client that can't reach the gateway exits with an error instead of falling back to cached settings, while the hourly background refresh is fail-open on both channels.
338338 
339339## Audit logging
340340 
from line 352
352352| User deletes the cached settings file | [First-launch behavior](#fetch-and-caching-behavior) occurs |
353353| User runs a modified Claude Code binary | A user who can run a modified client can bypass any client-side control |
354354| User runs an older Claude Code version | Versions that predate server-managed settings don't fetch or apply them |
355| API is unavailable | Cached settings apply if available, except for the [values Claude Code withholds](#fetch-and-caching-behavior) until a fetch succeeds. Without a cache, Claude Code enforces no server-managed settings until the next successful fetch and still applies any [endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms) on the device. With `forceRemoteSettingsRefresh: true`, the CLI exits instead of continuing, except for [`claude auth` subcommands](#enforce-fail-closed-startup). Clients signed in through a [Claude apps gateway](#platform-availability) exit at startup without that setting, with the same `claude auth` exemption |
355| API is unavailable | Cached settings apply if available, except for the [values Claude Code withholds](#fetch-and-caching-behavior) until a fetch succeeds. Without a cache, Claude Code enforces no server-managed settings until the next successful fetch and still applies any [endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) on the device. With `forceRemoteSettingsRefresh: true`, the CLI exits instead of continuing, except for [`claude auth` subcommands](#enforce-fail-closed-startup). Clients signed in through a [Claude apps gateway](#platform-availability) exit at startup without that setting, with the same `claude auth` exemption |
356356| User authenticates with a different organization | Settings are not delivered for accounts outside the managed organization, including a session that authenticates with a [Console API key](#platform-availability) |
357357| User configures a [third-party model provider](#platform-availability) | Server-managed settings are bypassed. This includes setting `CLAUDE_CODE_USE_BEDROCK`, `CLAUDE_CODE_USE_MANTLE`, `CLAUDE_CODE_USE_VERTEX`, `CLAUDE_CODE_USE_FOUNDRY`, `CLAUDE_CODE_USE_ANTHROPIC_AWS`, or a non-default `ANTHROPIC_BASE_URL` |
358358| Network traffic is intercepted or redirected | Disabled TLS validation or intercepted traffic can alter the settings the client receives |
359359 
360To log edits to local settings files, including `managed-settings.json`, use [`ConfigChange` hooks](/docs/en/hooks#configchange). Claude Code doesn't run them when server-managed settings arrive or refresh, or when an MDM profile or registry policy changes, and a hook can't block a `policy_settings` change.
360To log edits to local settings files, including `managed-settings.json`, use [`ConfigChange` hooks](https://code.claude.com/docs/en/hooks#configchange). Claude Code doesn't run them when server-managed settings arrive or refresh, or when an MDM profile or registry policy changes, and a hook can't block a `policy_settings` change.
361361 
362To restrict which organizations your users can access with credentials the client supplies, see [Enforce network-level access control with Tenant Restrictions](https://support.claude.com/en/articles/13198485-enforce-network-level-access-control-with-tenant-restrictions) in the Claude Help Center. For stronger enforcement guarantees, use [endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms) on devices enrolled in an MDM solution.
362To restrict which organizations your users can access with credentials the client supplies, see [Enforce network-level access control with Tenant Restrictions](https://support.claude.com/en/articles/13198485-enforce-network-level-access-control-with-tenant-restrictions) in the Claude Help Center. For stronger enforcement guarantees, use [endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms) on devices enrolled in an MDM solution.
363363 
364364## See also
365365 
366366Related pages for managing Claude Code configuration:
367367 
368* [All settings](/docs/en/settings-reference.md): every settings key
369* [Endpoint-managed settings](/docs/en/managed-settings.md#delivery-mechanisms): managed settings deployed to devices by IT
370* [Authentication](/docs/en/authentication.md): set up user access to Claude Code
371* [Security](/docs/en/security.md): security safeguards and best practices
368* [All settings](https://code.claude.com/docs/en/settings-reference.md): every settings key
369* [Endpoint-managed settings](https://code.claude.com/docs/en/managed-settings.md#delivery-mechanisms): managed settings deployed to devices by IT
370* [Authentication](https://code.claude.com/docs/en/authentication.md): set up user access to Claude Code
371* [Security](https://code.claude.com/docs/en/security.md): security safeguards and best practices
372372 

sessions Changed · +66 / -66 lines

from line 2
22 
33> Name, resume, branch, and switch between Claude Code conversations. Covers `--continue`, `--resume`, `--from-pr`, the `/resume` picker, session naming, exporting transcripts, and where transcripts are stored.
44 
5A [session](/docs/en/glossary.md#session) is a saved conversation tied to a project directory. Claude Code stores it locally as you work, so you can resume where you left off, branch to try a different approach, or switch between tasks.
5A [session](https://code.claude.com/docs/en/glossary.md#session) is a saved conversation tied to a project directory. Claude Code stores it locally as you work, so you can resume where you left off, branch to try a different approach, or switch between tasks.
66 
7The [desktop app](/docs/en/desktop.md#work-in-parallel-with-sessions), [claude.ai/code](/docs/en/claude-code-on-the-web.md), and the [VS Code extension](/docs/en/vs-code.md#resume-past-conversations) each keep their own session list, and the desktop app can also [resume a CLI session](/docs/en/desktop.md#coming-from-the-cli). This page covers the CLI.
7The [desktop app](https://code.claude.com/docs/en/desktop.md#work-in-parallel-with-sessions), [claude.ai/code](https://code.claude.com/docs/en/claude-code-on-the-web.md), and the [VS Code extension](https://code.claude.com/docs/en/vs-code.md#resume-past-conversations) each keep their own session list, and the desktop app can also [resume a CLI session](https://code.claude.com/docs/en/desktop.md#coming-from-the-cli). This page covers the CLI.
88 
99## Resume a session
1010 
from line 21
2121 
2222### Resume a running background session
2323 
24When the conversation you resume with `claude --resume` or `/resume` belongs to a [background session](/docs/en/agent-view.md) that is still running, Claude Code opens the running session itself. With `--bg` on the command line, the resume is a [background dispatch](/docs/en/agent-view.md#from-your-shell) instead. Before v2.1.285, Claude Code refused and told you to open the session with `claude attach <id>`, or to stop it with `claude stop <id>` first.
24When the conversation you resume with `claude --resume` or `/resume` belongs to a [background session](https://code.claude.com/docs/en/agent-view.md) that is still running, Claude Code opens the running session itself. With `--bg` on the command line, the resume is a [background dispatch](https://code.claude.com/docs/en/agent-view.md#from-your-shell) instead. Before v2.1.285, Claude Code refused and told you to open the session with `claude attach <id>`, or to stop it with `claude stop <id>` first.
2525 
26* **From your shell**: `claude --resume <session>` runs [`claude attach`](/docs/en/agent-view#attach-to-a-session) on that session in the same terminal instead of loading the transcript itself. A prompt you pass on the command line, as in `claude --resume <session> "check the tests too"`, goes to the session as its next turn first, and Claude Code prints `Sent your prompt to the background session (<id>); opening it…` before attaching. `claude -p --resume <session> "prompt"` typed at a terminal does the same, so `-p` doesn't keep that run non-interactive.
26* **From your shell**: `claude --resume <session>` runs [`claude attach`](https://code.claude.com/docs/en/agent-view#attach-to-a-session) on that session in the same terminal instead of loading the transcript itself. A prompt you pass on the command line, as in `claude --resume <session> "check the tests too"`, goes to the session as its next turn first, and Claude Code prints `Sent your prompt to the background session (<id>); opening it…` before attaching. `claude -p --resume <session> "prompt"` typed at a terminal does the same, so `-p` doesn't keep that run non-interactive.
2727 
2828 Claude Code doesn't open the session when the command line has any of these:
2929 
from line 32
3232 * Flags that read the output, such as `--output-format json` or `--json-schema`
3333 * Flags that limit or rewind the run, such as `--max-turns` or `--max-budget-usd`
3434 
35 With any of these, or when [agent view is turned off](/docs/en/agent-view.md#turn-off-agent-view), Claude Code sends nothing and exits with status 1, printing that the session is running in the background along with the `claude attach <id>` command that opens it, or telling you to find it in `claude agents` when it can't determine the ID. Add `--fork-session` to resume a copy of the conversation instead. To continue the conversation itself in a session of your own, with your flags applied, run `claude stop <id>` and then repeat the command.
35 With any of these, or when [agent view is turned off](https://code.claude.com/docs/en/agent-view.md#turn-off-agent-view), Claude Code sends nothing and exits with status 1, printing that the session is running in the background along with the `claude attach <id>` command that opens it, or telling you to find it in `claude agents` when it can't determine the ID. Add `--fork-session` to resume a copy of the conversation instead. To continue the conversation itself in a session of your own, with your flags applied, run `claude stop <id>` and then repeat the command.
3636 
3737 A prompt that starts with `/` or `!` isn't sent, and neither is any prompt while the session waits on your answer to a question. In both cases Claude Code doesn't open the session, and the message includes `Your prompt was not sent to it` with the reason.
3838* **From inside a session**: `/resume` moves your current conversation to the background and attaches this terminal to the running session, printing `Opening "<title>", running in the background (<id>)`. Press `←` on an empty prompt to return to agent view, which also lists the conversation you left. When the current conversation can't move to the background, for example because you're already attached to a background session or session persistence is off, `/resume` prints the `claude attach` command to run instead.
from line 41
4141 
4242When Claude Code loads a conversation from its transcript, the resumed session restores the conversation along with the state saved in it:
4343 
44* Conversation history: the messages, tool calls, and results. If the session was [compacted](/docs/en/context-window.md#what-survives-compaction), the conversation opens at a `Conversation compacted` row. Claude continues with the last compaction's summary, the messages that compaction left unsummarized, and every message since. The messages that the last compaction summarized aren't loaded again.
45* Interrupted tool calls: a tool that was still running when the previous process ended, for example in a crash, doesn't finish or get rerun when you resume. Claude sees the call marked as cut off before its result was recorded and is told to check whether it took effect before running it again, unless [`CLAUDE_CODE_RESUME_INTERRUPTED_TURN`](/docs/en/env-vars#variables) is set.
46* Model: the session continues on the model it was using, except in the cases in [Setting your model](/docs/en/model-config.md#setting-your-model).
47* Agent: a session started with [`--agent`](/docs/en/sub-agents#invoke-subagents-explicitly) or the `agent` setting continues as that agent, keeping its tool restrictions and model. Pass `--agent` when resuming to pick a different one; for the system prompt in either case, see [System prompt flags in resumed conversations](/docs/en/cli-reference.md#system-prompt-flags-in-resumed-conversations). Claude Code looks for the agent in two places: the session's original directory, provided you have [trusted that workspace](/docs/en/permissions.md#project-allow-rules-and-workspace-trust), and then the directory you resume from, so a project-scoped agent still loads when you resume from another directory. If Claude Code doesn't find the agent in either place, the session resumes with the default tools and shows a [warning naming the agent](/docs/en/errors.md#session-agent-no-longer-available).
44* Conversation history: the messages, tool calls, and results. If the session was [compacted](https://code.claude.com/docs/en/context-window.md#what-survives-compaction), the conversation opens at a `Conversation compacted` row. Claude continues with the last compaction's summary, the messages that compaction left unsummarized, and every message since. The messages that the last compaction summarized aren't loaded again.
45* Interrupted tool calls: a tool that was still running when the previous process ended, for example in a crash, doesn't finish or get rerun when you resume. Claude sees the call marked as cut off before its result was recorded and is told to check whether it took effect before running it again, unless [`CLAUDE_CODE_RESUME_INTERRUPTED_TURN`](https://code.claude.com/docs/en/env-vars#variables) is set.
46* Model: the session continues on the model it was using, except in the cases in [Setting your model](https://code.claude.com/docs/en/model-config.md#setting-your-model).
47* Agent: a session started with [`--agent`](https://code.claude.com/docs/en/sub-agents#invoke-subagents-explicitly) or the `agent` setting continues as that agent, keeping its tool restrictions and model. Pass `--agent` when resuming to pick a different one; for the system prompt in either case, see [System prompt flags in resumed conversations](https://code.claude.com/docs/en/cli-reference.md#system-prompt-flags-in-resumed-conversations). Claude Code looks for the agent in two places: the session's original directory, provided you have [trusted that workspace](https://code.claude.com/docs/en/permissions.md#project-allow-rules-and-workspace-trust), and then the directory you resume from, so a project-scoped agent still loads when you resume from another directory. If Claude Code doesn't find the agent in either place, the session resumes with the default tools and shows a [warning naming the agent](https://code.claude.com/docs/en/errors.md#session-agent-no-longer-available).
4848* Permission mode: if you resume from a terminal with `claude --continue`, `claude --resume <session-id>`, or `claude --resume <name>` when the name matches one session, without `-p`, Claude Code restores the permission mode the session was in, except in the cases in [permission mode on resume](#permission-mode-on-resume), which also covers the session picker, `/resume`, and resuming with `claude -p`. Pass `--permission-mode` or `--dangerously-skip-permissions` to override the restored mode.
49* Active goal: a [goal](/docs/en/goal.md#resume-with-an-active-goal) that was still active when the session ended carries over; its turn count, timer, and token-spend baseline reset.
50* Session-scoped scheduled tasks: [tasks that haven't expired](/docs/en/scheduled-tasks.md#limitations) are restored. Background Bash and monitor tasks aren't.
51* Background work: a [background subagent](/docs/en/sub-agents.md#run-subagents-in-foreground-or-background), background Bash command, or [workflow](/docs/en/workflows.md) that ended with the previous process shows up in the resumed transcript as a note that it didn't finish. Claude Code doesn't start a turn from those notes; Claude reads them with your next prompt.
49* Active goal: a [goal](https://code.claude.com/docs/en/goal.md#resume-with-an-active-goal) that was still active when the session ended carries over; its turn count, timer, and token-spend baseline reset.
50* Session-scoped scheduled tasks: [tasks that haven't expired](https://code.claude.com/docs/en/scheduled-tasks.md#limitations) are restored. Background Bash and monitor tasks aren't.
51* Background work: a [background subagent](https://code.claude.com/docs/en/sub-agents.md#run-subagents-in-foreground-or-background), background Bash command, or [workflow](https://code.claude.com/docs/en/workflows.md) that ended with the previous process shows up in the resumed transcript as a note that it didn't finish. Claude Code doesn't start a turn from those notes; Claude reads them with your next prompt.
5252 
53Not every configuration flag from the original launch is restored. If the session depended on `--mcp-config`, `--settings`, `--plugin-dir`, `--fallback-model`, or directories added with `--add-dir`, pass them again when you resume; directories added mid-session with `/add-dir` aren't restored either, though the session picker still uses them to locate the session. The standard settings files, such as `settings.json` and `settings.local.json`, are re-read at launch, so configuration that lives in them doesn't need to be passed again. For `--system-prompt` and `--append-system-prompt`, see [System prompt flags in resumed conversations](/docs/en/cli-reference.md#system-prompt-flags-in-resumed-conversations).
53Not every configuration flag from the original launch is restored. If the session depended on `--mcp-config`, `--settings`, `--plugin-dir`, `--fallback-model`, or directories added with `--add-dir`, pass them again when you resume; directories added mid-session with `/add-dir` aren't restored either, though the session picker still uses them to locate the session. The standard settings files, such as `settings.json` and `settings.local.json`, are re-read at launch, so configuration that lives in them doesn't need to be passed again. For `--system-prompt` and `--append-system-prompt`, see [System prompt flags in resumed conversations](https://code.claude.com/docs/en/cli-reference.md#system-prompt-flags-in-resumed-conversations).
5454 
5555#### Permission mode on resume
5656 
from line 58
5858 
5959* Terminal: `claude --continue`, `claude --resume <session-id>`, or `claude --resume <name>` when the name matches one session, without `-p`. Claude Code restores the permission mode the session was in, except in the cases in the table. Pass `--permission-mode` or `--dangerously-skip-permissions` to override the restored mode.
6060* Non-interactive: `claude -p --resume` or `claude -p --continue`. Claude Code starts the run in the permission mode a new `claude -p` run would start in, except that a session that ended in plan mode resumes in plan mode under the [conditions below](#resume-in-plan-mode-with-p).
61* VS Code: the extension's conversation panel. The table covers only a conversation that ended in plan mode; for the rest, see [resume past conversations](/docs/en/vs-code.md#resume-past-conversations).
61* VS Code: the extension's conversation panel. The table covers only a conversation that ended in plan mode; for the rest, see [resume past conversations](https://code.claude.com/docs/en/vs-code.md#resume-past-conversations).
6262* Session picker at launch: a session you select from the [session picker](#use-the-session-picker), whether you opened it with `claude --resume` alone, `claude --from-pr`, or a name that matches more than one session. Claude Code starts the session in the permission mode it would start a new session in from the same command line, except that a session that ended in plan mode resumes in plan mode. If you pass `--permission-mode`, `--dangerously-skip-permissions`, or `--fork-session`, Claude Code doesn't restore plan mode. No other stored permission mode is restored.
6363* `/resume` inside a session, with or without an argument: the conversation you switch to continues in the permission mode your current session is in, except that a conversation that ended in plan mode resumes in plan mode, even if you launched Claude Code with `--permission-mode` or `--dangerously-skip-permissions`. If that conversation was already open earlier in this run of Claude Code, such as the conversation you started in or one you left with `/clear` or `/resume`, it continues in your current permission mode instead.
6464 
65If a [deny rule](/docs/en/permissions.md#manage-permissions) removes the [`ExitPlanMode`](/docs/en/tools-reference) tool, Claude can't present a plan for approval, so Claude Code doesn't restore plan mode. The session starts in the permission mode a new session would start in from the same command line. With `/resume`, the conversation continues in your current permission mode.
65If a [deny rule](https://code.claude.com/docs/en/permissions.md#manage-permissions) removes the [`ExitPlanMode`](https://code.claude.com/docs/en/tools-reference) tool, Claude can't present a plan for approval, so Claude Code doesn't restore plan mode. The session starts in the permission mode a new session would start in from the same command line. With `/resume`, the conversation continues in your current permission mode.
6666 
6767Restoring plan mode on the non-interactive and VS Code paths requires Claude Code v2.1.246 or later. Each row names the permission mode the session ended in, which of the terminal, non-interactive, and VS Code paths you resume it by, and the permission mode Claude Code starts the resumed session in.
6868 
6969| Session ended in | How you resume | Permission mode after you resume |
7070| :- | :- | :- |
71| `bypassPermissions` | Terminal | The permission mode a new session would start in. To [bypass permissions](/docs/en/permission-modes.md#skip-all-checks-with-bypasspermissions-mode) again, enable it at launch with one of its launch flags or `permissions.defaultMode: "bypassPermissions"` in [user, `--settings`, or managed settings](/docs/en/settings-reference#permissions-defaultmode) |
71| `bypassPermissions` | Terminal | The permission mode a new session would start in. To [bypass permissions](https://code.claude.com/docs/en/permission-modes.md#skip-all-checks-with-bypasspermissions-mode) again, enable it at launch with one of its launch flags or `permissions.defaultMode: "bypassPermissions"` in [user, `--settings`, or managed settings](https://code.claude.com/docs/en/settings-reference#permissions-defaultmode) |
7272| `plan` | Terminal | Plan mode. With `--fork-session`, the permission mode a new session would start in |
7373| `plan` | Terminal, when a deny rule removes `ExitPlanMode` | The permission mode a new session would start in |
74| `auto` | Terminal | `auto`, only when your account still meets the [auto mode requirements](/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) |
75| Manual | Terminal | Manual when a new session would start in auto mode from the [built-in default](/docs/en/permission-modes.md#which-mode-a-session-starts-in). When a `defaultMode` from a settings file [takes effect](/docs/en/permission-modes.md#which-mode-a-session-starts-in), Claude Code starts the resumed session in that mode instead |
74| `auto` | Terminal | `auto`, only when your account still meets the [auto mode requirements](https://code.claude.com/docs/en/permission-modes.md#eliminate-prompts-with-auto-mode) |
75| Manual | Terminal | Manual when a new session would start in auto mode from the [built-in default](https://code.claude.com/docs/en/permission-modes.md#which-mode-a-session-starts-in). When a `defaultMode` from a settings file [takes effect](https://code.claude.com/docs/en/permission-modes.md#which-mode-a-session-starts-in), Claude Code starts the resumed session in that mode instead |
7676| `plan` | Non-interactive, under the [conditions below](#resume-in-plan-mode-with-p) | Plan mode |
7777| Any mode | Non-interactive, in any other case | The permission mode a new `claude -p` run would start in |
78| `plan` | VS Code | Plan mode, with [the exceptions on the VS Code page](/docs/en/vs-code.md#resume-past-conversations) |
78| `plan` | VS Code | Plan mode, with [the exceptions on the VS Code page](https://code.claude.com/docs/en/vs-code.md#resume-past-conversations) |
7979 
8080##### Resume in plan mode with `-p`
8181 
8282A `claude -p --resume` or `claude -p --continue` run resumes in plan mode only when all of these conditions hold:
8383 
84* You pass [`--permission-prompt-tool`](/docs/en/cli-reference#cli-flags) and don't pass [`--permission-prompts none`](/docs/en/headless#turn-off-permission-prompts-in-unattended-runs), so that Claude Code can present the plan for approval
84* You pass [`--permission-prompt-tool`](https://code.claude.com/docs/en/cli-reference#cli-flags) and don't pass [`--permission-prompts none`](https://code.claude.com/docs/en/headless#turn-off-permission-prompts-in-unattended-runs), so that Claude Code can present the plan for approval
8585* You don't pass `--permission-mode` or `--dangerously-skip-permissions`
8686* You don't pass `--fork-session`
87* The run isn't started through [channels](/docs/en/channels.md)
88* No [deny rule](/docs/en/permissions.md#manage-permissions) removes the `ExitPlanMode` tool
87* The run isn't started through [channels](https://code.claude.com/docs/en/channels.md)
88* No [deny rule](https://code.claude.com/docs/en/permissions.md#manage-permissions) removes the `ExitPlanMode` tool
8989 
9090### The "Resume this conversation?" dialog
9191 
9292If you see the "Resume this conversation?" dialog when resuming a session, its estimate is the share of your 5-hour usage limit that the saved conversation alone would use. The estimate doesn't count what you've already used.
9393 
94On Pro and Max plans, the dialog can appear when you resume a long conversation [after a long break](/docs/en/prompt-caching.md#resuming-a-session) or on a different model. The estimated use comes when you send your first message after **Resume**, not when you select it. Choose by whether your next task still needs the saved conversation:
94On Pro and Max plans, the dialog can appear when you resume a long conversation [after a long break](https://code.claude.com/docs/en/prompt-caching.md#resuming-a-session) or on a different model. The estimated use comes when you send your first message after **Resume**, not when you select it. Choose by whether your next task still needs the saved conversation:
9595 
9696* **Resume**: keep working in the saved conversation.
9797* **Start a new conversation**: switch to work that doesn't need the saved conversation. To reopen it later, run [`/resume`](#use-the-session-picker) at the prompt. The estimate doesn't apply to your first message in the new conversation. If the option says that Claude can look up this conversation, you can still refer to earlier work in the new conversation.
from line 100
100100 
101101Claude Code stores sessions per project directory. By default the session picker shows:
102102 
103* Sessions from the current worktree, including [background sessions](/docs/en/agent-view.md), which are marked `bg` in the list
103* Sessions from the current worktree, including [background sessions](https://code.claude.com/docs/en/agent-view.md), which are marked `bg` in the list
104104* Sessions started elsewhere that added the current directory with `/add-dir`
105105 
106106Use `Ctrl+W` to widen to all worktrees of the repository or `Ctrl+A` to widen to every project on this machine.
from line 107
107107 
108108#### `/loop`, `-p`, Agent SDK, and background sessions
109109 
110Sessions whose first prompt was a [`/loop`](/docs/en/scheduled-tasks#run-a-prompt-repeatedly-with-%2Floop) command don't appear in the picker, and `claude --continue` skips them too. Running `/loop` later in a conversation doesn't hide the session.
110Sessions whose first prompt was a [`/loop`](https://code.claude.com/docs/en/scheduled-tasks#run-a-prompt-repeatedly-with-%2Floop) command don't appear in the picker, and `claude --continue` skips them too. Running `/loop` later in a conversation doesn't hide the session.
111111 
112Claude Code leaves sessions created with [`claude -p`](/docs/en/headless) or the [Agent SDK](/docs/en/agent-sdk/overview.md) out of the session picker and out of `claude --continue`. You can still resume one by passing its session ID to `claude --resume <session-id>`. When you run [`claude -p --continue`](/docs/en/headless#continue-conversations), Claude Code includes `-p`, SDK, and `/loop` sessions.
112Claude Code leaves sessions created with [`claude -p`](https://code.claude.com/docs/en/headless) or the [Agent SDK](https://code.claude.com/docs/en/agent-sdk/overview.md) out of the session picker and out of `claude --continue`. You can still resume one by passing its session ID to `claude --resume <session-id>`. When you run [`claude -p --continue`](https://code.claude.com/docs/en/headless#continue-conversations), Claude Code includes `-p`, SDK, and `/loop` sessions.
113113 
114`claude --continue` opens a [background session](/docs/en/agent-view.md) that has finished, but not one that is still running; opening finished background sessions requires Claude Code v2.1.257 or later. If your most recent conversation is one you [moved to the background](/docs/en/agent-view.md#send-the-session-to-the-background) and it is still running there, Claude Code exits with `Your most recent conversation is running in the background` and that session's ID. Attach to the session from [`claude agents`](/docs/en/agent-view#attach-to-a-session), or run `claude --resume` to pick another one.
114`claude --continue` opens a [background session](https://code.claude.com/docs/en/agent-view.md) that has finished, but not one that is still running; opening finished background sessions requires Claude Code v2.1.257 or later. If your most recent conversation is one you [moved to the background](https://code.claude.com/docs/en/agent-view.md#send-the-session-to-the-background) and it is still running there, Claude Code exits with `Your most recent conversation is running in the background` and that session's ID. Attach to the session from [`claude agents`](https://code.claude.com/docs/en/agent-view#attach-to-a-session), or run `claude --resume` to pick another one.
115115 
116116#### Sessions in other worktrees and projects
117117 
118When you select a session from another worktree of the same repository, Claude Code resumes it in place; when the session's own worktree no longer exists, Claude Code [resumes it in your current directory](/docs/en/worktrees.md#resume-a-worktree-session). When you select a session from an unrelated project, Claude Code copies a `cd` and resume command to your clipboard instead. If that project's directory no longer exists, Claude Code resumes the session in your current directory rather than copying a `cd` command that would fail.
118When you select a session from another worktree of the same repository, Claude Code resumes it in place; when the session's own worktree no longer exists, Claude Code [resumes it in your current directory](https://code.claude.com/docs/en/worktrees.md#resume-a-worktree-session). When you select a session from an unrelated project, Claude Code copies a `cd` and resume command to your clipboard instead. If that project's directory no longer exists, Claude Code resumes the session in your current directory rather than copying a `cd` command that would fail.
119119 
120Moving a session with [`/cd`](/docs/en/commands) relocates it to the new directory's project storage, so it appears in that directory's picker afterward.
120Moving a session with [`/cd`](https://code.claude.com/docs/en/commands) relocates it to the new directory's project storage, so it appears in that directory's picker afterward.
121121 
122122#### Resume by session ID or name
123123 
124You can run `claude --resume <session-id>` from any directory, so you can resume a session that started elsewhere or moved with [`/cd`](/docs/en/commands). Claude Code looks for the ID in this order:
124You can run `claude --resume <session-id>` from any directory, so you can resume a session that started elsewhere or moved with [`/cd`](https://code.claude.com/docs/en/commands). Claude Code looks for the ID in this order:
125125 
1261261. The current project directory and its git worktrees
1271272. Every other project on this machine
from line 144
144144| At startup | `claude -n auth-refactor` |
145145| During a session | `/rename auth-refactor`. The name also appears on the prompt bar |
146146| From the session picker | Highlight a session and press `Ctrl+R` |
147| On plan accept | Accepting a plan in [plan mode](/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode) gives the session a generated title based on the plan unless you've already named it |
148| From claude.ai or the Claude app | Rename a [Remote Control session](/docs/en/remote-control.md#connect-from-another-device); Claude Code applies the same name in the CLI. Requires Claude Code v2.1.221 or later |
149| From the desktop app | Rename a session in the [desktop app](/docs/en/desktop.md#organize-and-find-sessions) |
147| On plan accept | Accepting a plan in [plan mode](https://code.claude.com/docs/en/permission-modes.md#analyze-before-you-edit-with-plan-mode) gives the session a generated title based on the plan unless you've already named it |
148| From claude.ai or the Claude app | Rename a [Remote Control session](https://code.claude.com/docs/en/remote-control.md#connect-from-another-device); Claude Code applies the same name in the CLI. Requires Claude Code v2.1.221 or later |
149| From the desktop app | Rename a session in the [desktop app](https://code.claude.com/docs/en/desktop.md#organize-and-find-sessions) |
150150 
151Once you name a session through a CLI route or from claude.ai, return to it with `claude --resume <name>` or `/resume <name>`; a desktop-app session resumes in the [desktop app](/docs/en/desktop.md#work-in-parallel-with-sessions). See [Resume a session](#resume-a-session) for how name resolution behaves across worktrees.
151Once you name a session through a CLI route or from claude.ai, return to it with `claude --resume <name>` or `/resume <name>`; a desktop-app session resumes in the [desktop app](https://code.claude.com/docs/en/desktop.md#work-in-parallel-with-sessions). See [Resume a session](#resume-a-session) for how name resolution behaves across worktrees.
152152 
153153Sessions you don't name still get two labels that Claude Code assigns. Only the generated title works as a resume handle:
154154 
155* Default display name: interactive sessions you never name still get a default display name when they start. The default combines the working directory's name with a two-character suffix, for example `my-app-3f`, and identifies the session in listings of running sessions, such as [agent view](/docs/en/agent-view.md) and `claude agents --json` output. The default isn't a resume handle. If you pass it to `claude --resume` or `/resume`, Claude Code doesn't find the session.
155* Default display name: interactive sessions you never name still get a default display name when they start. The default combines the working directory's name with a two-character suffix, for example `my-app-3f`, and identifies the session in listings of running sessions, such as [agent view](https://code.claude.com/docs/en/agent-view.md) and `claude agents --json` output. The default isn't a resume handle. If you pass it to `claude --resume` or `/resume`, Claude Code doesn't find the session.
156156* Generated title: if you don't name a session, Claude Code generates a session title for it. The title is a short summary of your first prompt, written by a background request to the small/fast model, normally a Haiku-class model. A `claude -p` run you start directly from a shell or script doesn't get one.
157157 
158158 Accepting a plan replaces the generated title with a title based on the plan. You can pass either title to `claude --resume` or `/resume`, and Claude Code resolves it the same way as a name you set.
from line 179
179179 
180180Sessions created with `/branch` or `--fork-session` get their own session IDs and appear as separate rows. When the picker finds more than one entry for the same session, it groups them under a single row. Press `→` to expand a group.
181181 
182If Claude Code can't load the session you select from the `claude --resume` picker, it prints [`Failed to resume the conversation`](/docs/en/errors#failed-to-resume-the-conversation) with a command to retry, then exits with code 1. From the `/resume` picker inside a session, Claude Code reports the failure and your current conversation keeps running.
182If Claude Code can't load the session you select from the `claude --resume` picker, it prints [`Failed to resume the conversation`](https://code.claude.com/docs/en/errors#failed-to-resume-the-conversation) with a command to retry, then exits with code 1. From the `/resume` picker inside a session, Claude Code reports the failure and your current conversation keeps running.
183183 
184184## Branch a session
185185 
from line 207
207207| :- | :- |
208208| Conversation history | Copied into the branch up to the point you ran `/branch` |
209209| "Allow for this session" permission grants | Carried over; the branch runs in the same process, so your existing grants still apply. If you fork into a separate process with `--fork-session`, the new process starts without them and you re-approve there |
210| In-flight [background subagents](/docs/en/sub-agents.md#run-subagents-in-foreground-or-background) and [background Bash commands](/docs/en/interactive-mode.md#background-bash-commands) | Keep running. Their output appears in the new branch you switched into, not in the original session |
211| [Remote Control](/docs/en/remote-control.md) connection | Stays connected. A phone or browser connected to the session follows you into the branch and keeps receiving new messages there |
210| In-flight [background subagents](https://code.claude.com/docs/en/sub-agents.md#run-subagents-in-foreground-or-background) and [background Bash commands](https://code.claude.com/docs/en/interactive-mode.md#background-bash-commands) | Keep running. Their output appears in the new branch you switched into, not in the original session |
211| [Remote Control](https://code.claude.com/docs/en/remote-control.md) connection | Stays connected. A phone or browser connected to the session follows you into the branch and keeps receiving new messages there |
212212 
213If you resume the same session in two terminals without forking, messages from both interleave into one transcript. For checkpoint-based rewind within a single session, see [Checkpointing](/docs/en/checkpointing.md).
213If you resume the same session in two terminals without forking, messages from both interleave into one transcript. For checkpoint-based rewind within a single session, see [Checkpointing](https://code.claude.com/docs/en/checkpointing.md).
214214 
215215## Manage context within a session
216216 
217217These commands control what's in the context window without leaving the session:
218218 
219* **`/clear`**: start fresh with an empty context. Claude Code saves the previous session; resume it with `/resume`, or, in the same Claude Code process, from [the rewind menu's previous-session entry](/docs/en/checkpointing.md#rewind-past-a-cleared-conversation). With no argument, the new session keeps a name you set with `--name` or `/rename`, but not an AI-generated session title. To name the session you're leaving instead, pass the name, as in `/clear release-prep`; the new session then starts unnamed
219* **`/clear`**: start fresh with an empty context. Claude Code saves the previous session; resume it with `/resume`, or, in the same Claude Code process, from [the rewind menu's previous-session entry](https://code.claude.com/docs/en/checkpointing.md#rewind-past-a-cleared-conversation). With no argument, the new session keeps a name you set with `--name` or `/rename`, but not an AI-generated session title. To name the session you're leaving instead, pass the name, as in `/clear release-prep`; the new session then starts unnamed
220220* **`/compact [instructions]`**: replace history with a summary, optionally focused on what you specify
221221* **`/context`**: show what is currently consuming context
222222 
223For how compaction interacts with CLAUDE.md, skills, and rules, see the [context window guide](/docs/en/context-window.md). For strategies on when to clear versus compact, see [Best practices](/docs/en/best-practices.md#manage-your-session).
223For how compaction interacts with CLAUDE.md, skills, and rules, see the [context window guide](https://code.claude.com/docs/en/context-window.md). For strategies on when to clear versus compact, see [Best practices](https://code.claude.com/docs/en/best-practices.md#manage-your-session).
224224 
225225## Export and locate session data
226226 
from line 230
230230 
231231`/export` produces a rendered transcript for a person to read. The interfaces below produce structured data for a script to parse: a JSON result from a run, the path to a session's transcript file, or a live stream of events. Pick by what triggers the script:
232232 
233* **Run Claude once and capture the result**: invoke `claude -p` with [`--output-format json` or `stream-json`](/docs/en/headless#get-structured-output) to capture the result, session ID, usage, and cost of a non-interactive run as structured JSON.
234* **Ask an existing session a question**: pass a session ID to [`claude -p --resume`](/docs/en/headless#continue-conversations) to send a follow-up prompt, such as a summary request, and capture the structured response.
235* **React to session events**: read the `transcript_path` field that [hooks](/docs/en/hooks.md#common-input-fields) and [status line commands](/docs/en/statusline.md#available-data) receive as input. A `SessionEnd` hook can archive the transcript when a session ends.
236* **Embed Claude in a TypeScript or Python app**: use the [Agent SDK](/docs/en/agent-sdk/overview.md) to receive each message programmatically.
233* **Run Claude once and capture the result**: invoke `claude -p` with [`--output-format json` or `stream-json`](https://code.claude.com/docs/en/headless#get-structured-output) to capture the result, session ID, usage, and cost of a non-interactive run as structured JSON.
234* **Ask an existing session a question**: pass a session ID to [`claude -p --resume`](https://code.claude.com/docs/en/headless#continue-conversations) to send a follow-up prompt, such as a summary request, and capture the structured response.
235* **React to session events**: read the `transcript_path` field that [hooks](https://code.claude.com/docs/en/hooks.md#common-input-fields) and [status line commands](https://code.claude.com/docs/en/statusline.md#available-data) receive as input. A `SessionEnd` hook can archive the transcript when a session ends.
236* **Embed Claude in a TypeScript or Python app**: use the [Agent SDK](https://code.claude.com/docs/en/agent-sdk/overview.md) to receive each message programmatically.
237237 
238238The example below uses the second interface. It sends a follow-up prompt to an existing session and reads the answer with `jq`:
239239 
from line 251
251251 
252252| To | Set | Where |
253253| - | - | - |
254| Move storage off `~/.claude` | [`CLAUDE_CONFIG_DIR`](/docs/en/env-vars) | Environment variable |
255| [Name the `<project>` directory yourself](#name-the-project-directory-yourself) | [`CLAUDE_CODE_PROJECT_DIR_NAME`](/docs/en/env-vars) | Environment variable |
256| Change the 30-day retention | [`cleanupPeriodDays`](/docs/en/settings-reference#cleanupperioddays) | `settings.json` |
257| Set an age limit for [Claude Desktop and Cowork transcripts](/docs/en/claude-directory.md#cleaned-up-automatically) | [`desktopSessionCleanupPeriodDays`](/docs/en/settings-reference#desktopsessioncleanupperioddays) | User settings, managed settings, or `--settings` |
258| Limit how large a `-p` or Agent SDK session's transcript file grows | [`CLAUDE_CODE_TRANSCRIPT_LOCAL_GC`](/docs/en/env-vars) | Environment variable |
259| Suppress transcript writes in all modes | [`CLAUDE_CODE_SKIP_PROMPT_HISTORY`](/docs/en/env-vars) | Environment variable |
260| Suppress writes for one non-interactive run | [`--no-session-persistence`](/docs/en/cli-reference) | CLI flag with `claude -p` |
254| Move storage off `~/.claude` | [`CLAUDE_CONFIG_DIR`](https://code.claude.com/docs/en/env-vars) | Environment variable |
255| [Name the `<project>` directory yourself](#name-the-project-directory-yourself) | [`CLAUDE_CODE_PROJECT_DIR_NAME`](https://code.claude.com/docs/en/env-vars) | Environment variable |
256| Change the 30-day retention | [`cleanupPeriodDays`](https://code.claude.com/docs/en/settings-reference#cleanupperioddays) | `settings.json` |
257| Set an age limit for [Claude Desktop and Cowork transcripts](https://code.claude.com/docs/en/claude-directory.md#cleaned-up-automatically) | [`desktopSessionCleanupPeriodDays`](https://code.claude.com/docs/en/settings-reference#desktopsessioncleanupperioddays) | User settings, managed settings, or `--settings` |
258| Limit how large a `-p` or Agent SDK session's transcript file grows | [`CLAUDE_CODE_TRANSCRIPT_LOCAL_GC`](https://code.claude.com/docs/en/env-vars) | Environment variable |
259| Suppress transcript writes in all modes | [`CLAUDE_CODE_SKIP_PROMPT_HISTORY`](https://code.claude.com/docs/en/env-vars) | Environment variable |
260| Suppress writes for one non-interactive run | [`--no-session-persistence`](https://code.claude.com/docs/en/cli-reference) | CLI flag with `claude -p` |
261261 
262262### Delete session data
263263 
264Transcripts age out under the [retention sweep rules](/docs/en/claude-directory.md#cleaned-up-automatically). To delete a project's transcripts and related state sooner, run [`claude purge`](/docs/en/claude-directory#clear-local-data). If you delete a [background session](/docs/en/agent-view.md) with [`claude rm <id>`](/docs/en/agent-view#what-deleting-a-session-removes), its transcript stays on disk and remains available through `claude --resume`.
264Transcripts age out under the [retention sweep rules](https://code.claude.com/docs/en/claude-directory.md#cleaned-up-automatically). To delete a project's transcripts and related state sooner, run [`claude purge`](https://code.claude.com/docs/en/claude-directory#clear-local-data). If you delete a [background session](https://code.claude.com/docs/en/agent-view.md) with [`claude rm <id>`](https://code.claude.com/docs/en/agent-view#what-deleting-a-session-removes), its transcript stays on disk and remains available through `claude --resume`.
265265 
266266### Name the project directory yourself
267267 
268By default, Claude Code derives the `<project>` name from the whole working directory path. To choose the name yourself, set `CLAUDE_CODE_PROJECT_DIR_NAME` alongside `CLAUDE_CONFIG_DIR`. Claude Code then stores that session's transcripts and [auto memory](/docs/en/memory.md#auto-memory) under your name. This suits a host that embeds Claude Code and gives each session its own config directory. Requires Claude Code v2.1.234 or later.
268By default, Claude Code derives the `<project>` name from the whole working directory path. To choose the name yourself, set `CLAUDE_CODE_PROJECT_DIR_NAME` alongside `CLAUDE_CONFIG_DIR`. Claude Code then stores that session's transcripts and [auto memory](https://code.claude.com/docs/en/memory.md#auto-memory) under your name. This suits a host that embeds Claude Code and gives each session its own config directory. Requires Claude Code v2.1.234 or later.
269269 
270270For example, this launch keeps tenant A's data under `/srv/tenant-a` and names its project directory `work`:
271271 
from line 287
287287 
288288These pages cover related session and parallelism mechanics:
289289 
290* [Worktrees](/docs/en/worktrees.md): run isolated parallel sessions on separate branches
291* [Checkpointing](/docs/en/checkpointing.md): rewind code and conversation to an earlier point
292* [Context window](/docs/en/context-window.md): what fills context and what survives compaction
293* [Non-interactive mode](/docs/en/headless.md): session behavior under `claude -p`
290* [Worktrees](https://code.claude.com/docs/en/worktrees.md): run isolated parallel sessions on separate branches
291* [Checkpointing](https://code.claude.com/docs/en/checkpointing.md): rewind code and conversation to an earlier point
292* [Context window](https://code.claude.com/docs/en/context-window.md): what fills context and what survives compaction
293* [Non-interactive mode](https://code.claude.com/docs/en/headless.md): session behavior under `claude -p`
294294 

settings-example Changed · +10 / -10 lines

from line 8
88* A team's `.claude/settings.json`, committed to the repository
99* An organization's `managed-settings.json`
1010 
11Each one is a plausible file for that reader, so you can see the shape and copy the parts you want. None of them is a recommended baseline. Every value comes from the key's entry on the [settings reference](/docs/en/settings-reference.md), which has its type, default, and where it can be set.
11Each one is a plausible file for that reader, so you can see the shape and copy the parts you want. None of them is a recommended baseline. Every value comes from the key's entry on the [settings reference](https://code.claude.com/docs/en/settings-reference.md), which has its type, default, and where it can be set.
1212 
1313Each example has two tabs. **Copyable settings file** is the file as you'd save it. **What each key does** is the same file with a comment above each key; Claude Code doesn't accept comments in a settings file, so copy from the first tab.
1414 
from line 90
9090 
9191One team's shared settings, committed to the repository so everyone who clones it gets the same permissions, hooks, and plugin marketplace. Save a file like this at `.claude/settings.json` at the top of the repository. What to know before you commit one:
9292 
93* **Cloud sessions outside a [project](/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories)'s threads read it too.** A [cloud session](/docs/en/settings.md#settings-in-cloud-sessions) with one repository starts inside the clone, so the committed file applies there as well.
94* **Telemetry goes in managed or personal settings.** Claude Code ignores the [OpenTelemetry exporter variables](/docs/en/settings-reference.md#variables-claude-code-ignores-in-env) in a repository's settings files, apart from some values that turn telemetry off. Set them in [managed settings](/docs/en/monitoring-usage.md#administrator-configuration) for your organization, or in each person's `~/.claude/settings.json`.
95* **Allow rules wait for trust.** Allow rules and `extraKnownMarketplaces` entries take effect after each person [trusts this folder itself](/docs/en/permissions.md#project-allow-rules-and-workspace-trust), not only a parent folder; deny and ask rules apply in every session, trusted or not.
96* **The hook is a script in the repo.** This file's hook runs `.claude/hooks/block-rm.sh`; [How a hook resolves](/docs/en/hooks.md#how-a-hook-resolves) walks through writing it.
97* **Rules match the command and path as written.** `Bash(git push *)` doesn't match [`git -C . push`](/docs/en/permissions#bash-rule-limits). `Read(./.env)` on its own stops the file tools and commands that name the file, such as `cat .env`, but not [`grep -r` run over the directory](/docs/en/permissions#read-and-edit); the `sandbox` block in this file closes that gap, because the sandbox [adds your `Read` deny paths](/docs/en/settings-reference#sandbox-filesystem-denyread) to what every sandboxed command can't read.
93* **Cloud sessions outside a [project](https://code.claude.com/docs/en/claude-projects.md#what-threads-pick-up-from-your-repositories)'s threads read it too.** A [cloud session](https://code.claude.com/docs/en/settings.md#settings-in-cloud-sessions) with one repository starts inside the clone, so the committed file applies there as well.
94* **Telemetry goes in managed or personal settings.** Claude Code ignores the [OpenTelemetry exporter variables](https://code.claude.com/docs/en/settings-reference.md#variables-claude-code-ignores-in-env) in a repository's settings files, apart from some values that turn telemetry off. Set them in [managed settings](https://code.claude.com/docs/en/monitoring-usage.md#administrator-configuration) for your organization, or in each person's `~/.claude/settings.json`.
95* **Allow rules wait for trust.** Allow rules and `extraKnownMarketplaces` entries take effect after each person [trusts this folder itself](https://code.claude.com/docs/en/permissions.md#project-allow-rules-and-workspace-trust), not only a parent folder; deny and ask rules apply in every session, trusted or not.
96* **The hook is a script in the repo.** This file's hook runs `.claude/hooks/block-rm.sh`; [How a hook resolves](https://code.claude.com/docs/en/hooks.md#how-a-hook-resolves) walks through writing it.
97* **Rules match the command and path as written.** `Bash(git push *)` doesn't match [`git -C . push`](https://code.claude.com/docs/en/permissions#bash-rule-limits). `Read(./.env)` on its own stops the file tools and commands that name the file, such as `cat .env`, but not [`grep -r` run over the directory](https://code.claude.com/docs/en/permissions#read-and-edit); the `sandbox` block in this file closes that gap, because the sandbox [adds your `Read` deny paths](https://code.claude.com/docs/en/settings-reference#sandbox-filesystem-denyread) to what every sandboxed command can't read.
9898 
9999**Copyable settings file**
100100 
from line 232
232232 
233233* `forceLoginMethod` and `forceLoginOrgUUID` pin the login method and organization
234234* `availableModels` and `enforceAvailableModels` restrict which models sessions can use
235* `permissions.deny` denies two file reads and `curl` commands [as Claude writes them](/docs/en/permissions.md#bash-rule-limits), and `disableBypassPermissionsMode` removes the bypass permission mode
236* [`allowManagedPermissionRulesOnly`](/docs/en/settings-reference#allowmanagedpermissionrulesonly) and [`allowManagedMcpServersOnly`](/docs/en/settings-reference#allowmanagedmcpserversonly) make the managed permission and MCP allowlists the only ones that apply
235* `permissions.deny` denies two file reads and `curl` commands [as Claude writes them](https://code.claude.com/docs/en/permissions.md#bash-rule-limits), and `disableBypassPermissionsMode` removes the bypass permission mode
236* [`allowManagedPermissionRulesOnly`](https://code.claude.com/docs/en/settings-reference#allowmanagedpermissionrulesonly) and [`allowManagedMcpServersOnly`](https://code.claude.com/docs/en/settings-reference#allowmanagedmcpserversonly) make the managed permission and MCP allowlists the only ones that apply
237237* `allowedMcpServers` pins the MCP server by URL
238238* `strictKnownMarketplaces` allows one plugin marketplace
239* `sandbox` sandboxes commands with a fixed network allowlist and no unsandboxed retry. Its `failIfUnavailable` key [stops Claude Code from starting where the sandbox can't run](/docs/en/sandboxing.md#enforce-sandboxing-with-managed-settings)
239* `sandbox` sandboxes commands with a fixed network allowlist and no unsandboxed retry. Its `failIfUnavailable` key [stops Claude Code from starting where the sandbox can't run](https://code.claude.com/docs/en/sandboxing.md#enforce-sandboxing-with-managed-settings)
240240* `requiredMinimumVersion` sets a minimum Claude Code version
241241* `cleanupPeriodDays` shortens retention of session transcripts and other local data to seven days
242242* `companyAnnouncements` shows a message at startup
243243 
244Administrators deploy a file like this as `managed-settings.json`, or the same JSON through MDM or [server-managed settings](/docs/en/server-managed-settings.md). One deployed file applies to every machine or account it reaches. To give a group different values, deploy a different file or profile to that group, since [server-managed settings don't support per-group policy yet](/docs/en/server-managed-settings.md#current-limitations).
244Administrators deploy a file like this as `managed-settings.json`, or the same JSON through MDM or [server-managed settings](https://code.claude.com/docs/en/server-managed-settings.md). One deployed file applies to every machine or account it reaches. To give a group different values, deploy a different file or profile to that group, since [server-managed settings don't support per-group policy yet](https://code.claude.com/docs/en/server-managed-settings.md#current-limitations).
245245 
246246**Copyable settings file**
247247 

settings-reference Changed · +847 / -822 lines

### `sshHostCommands`

This page is larger than the 256 KiB this site keeps, so one side of the diff below stops where the stored text does.

Nothing in the body moved in this read. What changed is above.

Feedback