One file for the fleet
One file puts the hook on every machine; the required check makes it a gate.
This file is reach. It is not enforcement. A hook that runs on Write and Edit does not see a shell that writes the same bytes, and a managed MCP server is a decision surface. The gate is still the required check, or admission. The steps that make the check required are on install. Where the gate is not is on what each path does.
What ran on one machine
Measured 2026-09-27 on Péter's machine, not on this round: Claude Code 2.1.283, macOS, file /Library/Application Support/ClaudeCode/managed-settings.json. OpenTelemetry recorded managed_settings_resolved with sources file. With allowManagedHooksOnly: true, two user hooks on the same matcher did not run and the managed hook did; its hook_execution_* events came only from policySettings. permissions.deny removed WebFetch from the tool set, and a CLI --allowedTools WebFetch did not restore it. A Bash curl to the same URL was logged as an ordinary tool_decision accept / source config, and neither OpenTelemetry nor the session transcript linked that call to the denied tool.
The documentation says a file-based policy shows Enterprise managed settings (file) on the Setting sources line of /status. That sentence is the vendor's check, not a line read back here.
The same pages say the file mechanism is not tied to one billing package. Server-managed delivery from the claude.ai console wants a Teams or Enterprise plan. A Max-account run of this file is not recorded here.
The file, without a deny list
allowManagedHooksOnly: true is documented as: only managed hooks run, plus Agent SDK hooks and hooks from plugins the managed settings force-enable. User, project, and local hooks are blocked. allowManagedMcpServersOnly: true is documented as: only allowedMcpServers from managed settings are respected, and deniedMcpServers still merges from all sources. This example sets neither a deny list nor an allow list. A header is left out: the docs say anyone who can read the file, including the user, can read a header value.
The command below is the package path from the per-repo snippet, written as an absolute path because this file is machine-wide. ${CLAUDE_PROJECT_DIR} is a checkout path, not a fleet path. The matcher here is Write|Edit. The per-repo snippet also names MultiEdit. The session on Péter's machine used one PreToolUse Write|Edit hook and a WebFetch deny. This example command was not executed on this round.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/usr/local/bin/node",
"args": [
"/absolute/path/node_modules/@coderifts/agent-hooks/claude-code/hook.mjs"
]
}
]
}
]
},
"managedMcpServers": {
"coderifts": {
"type": "http",
"url": "https://app.coderifts.com/mcp"
}
},
"allowManagedHooksOnly": true,
"allowManagedMcpServersOnly": true
}
Where the file goes
File-based, from the managed-settings page, fetched 2026-09-27:
macOS: /Library/Application Support/ClaudeCode/managed-settings.json
Linux and WSL: /etc/claude-code/managed-settings.json
Windows: C:\Program Files\ClaudeCode\managed-settings.json
Claude Code does not read the legacy Windows path C:\ProgramData\ClaudeCode\managed-settings.json.
The same keys, other shapes. macOS configuration profile: the com.anthropic.claudecode managed preferences domain, nested settings as dictionaries and lists as plist arrays. Windows HKLM: the JSON as a REG_SZ or REG_EXPAND_SZ value named Settings under HKLM\SOFTWARE\Policies\ClaudeCode.
The limit
Under allowManagedHooksOnly, the 2026-09-27 session on Péter's machine showed two user hooks on the same matcher did not run and the managed hook did. A shell that writes a file does not pass the Write matcher. The admin-setup page says it for the neighbouring control: denying WebFetch blocks Claude's fetch tool, but if Bash is allowed, curl and wget can still reach any URL. That session fetched the denied URL with Bash curl. OpenTelemetry logged an ordinary tool_decision accept / source config, and neither that log nor the session transcript linked the call to the denied tool. Reach onto the fleet is not a gate. The required check, and admission, are the gates. Install is where those steps are written.
A PreToolUse hook installed outside managed settings does not prove the call was refused: since Claude Code 2.1.287 a user-installed mod runs before it, can answer the call so the hook never runs, and can approve a call the hook blocked; only a hook in managed settings is final.
That is why the managed install is the final path for this hook.
Source, read 2026-10-03: code.claude.com/docs/en/plugins/mods/events, code.claude.com/docs/en/plugins/mods/overview. claude --version the same day printed 2.1.283.