> For the complete documentation index, see [llms.txt](https://help.nightfall.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.nightfall.ai/data-exfiltration-prevention/ai-agent-security/ai-governance/auditability-and-control/setup-and-installation/nightfall-hooks-for-claude-code.md).

# Nightfall Hooks for Claude Code

Step-by-step instructions for deploying Nightfall's Claude Code hooks to corporate-managed devices via MDM. The `README.md` in the deployment zip matches this page. This page is Claude Code only. Cursor and VS Code Copilot ship as separate packages.

### Prerequisites

1. **Nightfall endpoint agent** installed and running (v1.2.12.11 or later). The agent installs and updates `nightfall-hook-relay` and adds it to the system PATH. The hooks call that binary by name. The agent installer does not write the hook payload.
2. **Nightfall agent configuration profile v3+**, deployed with the agent (Jamf, Mosyle, Kandji, and the other agent-deployment guides). That profile grants the agent its OS permissions. It is not `payloads/nightfall-hooks.mobileconfig`. Without the agent profile, the agent reports missing permissions and MCP server inventory stays empty.
3. **MDM scope selected** — the device group, Blueprint, Smart Group, or assignment that will receive this deployment.
4. **Claude Code is current** on the managed devices.
5. **This package**, including both payloads: `payloads/nightfall-hooks.json` (macOS) and `payloads/nightfall-hooks.windows.json` (Windows), plus the scripts for the platform you are deploying.

### Choose the carrier first

Claude Code loads enterprise settings from **one** source. A higher-priority source ignores a file drop completely, and the MDM can still report the install as successful.

On one device in the target group:

```bash
# macOS — a managed-preferences profile for Claude Code?
ls -l "/Library/Managed Preferences/com.anthropic.claudecode.plist"
```

```powershell
# Windows — an HKLM Claude Code policy?
reg query "HKLM\SOFTWARE\Policies\ClaudeCode" /v Settings
```

Then run `/status` inside Claude Code. The managed source is named in parentheses.

| `/status` shows                                                       | What to deploy                                                                               |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| No managed source, or `(drop-ins)`, or `(file)` / `(file + drop-ins)` | [File drop](#file-drop-the-default)                                                          |
| `(plist)` on macOS, or the plist file exists                          | [macOS managed-preferences profile](#macos-managed-preferences-profile). Skip the file drop. |
| `(HKLM)` on Windows, or the registry value exists                     | [Windows HKLM policy](#windows-hklm-policy). Skip the file drop.                             |
| `(remote)`                                                            | [claude.ai admin console](#server-managed-claudeai-admin-console). Skip the file drop.       |

#### Why the check exists

| Priority    | Source                | macOS                                                                                   | Windows                                                                     |
| ----------- | --------------------- | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| 1 (highest) | Server-managed        | claude.ai admin console                                                                 | claude.ai admin console                                                     |
| 2           | MDM / OS-level policy | managed preferences domain `com.anthropic.claudecode`                                   | `HKLM\SOFTWARE\Policies\ClaudeCode` → `Settings`                            |
| 3           | File-based            | `/Library/Application Support/ClaudeCode/managed-settings.json` + `managed-settings.d/` | `C:\Program Files\ClaudeCode\managed-settings.json` + `managed-settings.d\` |
| 4 (lowest)  | Per-user registry     | —                                                                                       | `HKCU\SOFTWARE\Policies\ClaudeCode`                                         |

The highest-priority source present wins, and the others are ignored. Merging happens only inside the file-based tier: `managed-settings.json` is the base, then `managed-settings.d/*.json` apply in filename order.

`/status` can also name `(parent process)` or `(helper)`. An MDM does not deploy either. `(HKCU)` is the per-user registry.

The file drop in this package is priority 3. On a device that already has a `com.anthropic.claudecode` profile, an HKLM policy, or any keys from the claude.ai admin console, the drop-in is ignored. No hooks load, and no events reach the console.

`managed-settings.d/` accepts multiple vendor files, so `nightfall-hooks.json` coexists with other files **in that directory**. A managed-preferences profile or admin-console settings still shadows the whole directory.

{% hint style="info" %}
Anthropic's public settings documentation describes merge behavior inside the file-based tier and does not document precedence between these sources. Precedence and the no-merge behavior were verified on Claude Code 2.1.205 / macOS 26.5.2, and confirmed by a customer reproduction. Re-verify after major Claude Code upgrades.
{% endhint %}

### What gets deployed

| Event              | When it fires                       |
| ------------------ | ----------------------------------- |
| `PreToolUse`       | Before Claude Code runs a tool      |
| `PostToolUse`      | After a tool runs                   |
| `UserPromptSubmit` | When the developer submits a prompt |
| `Stop`             | When the agent finishes responding  |

`allowManagedHooksOnly: true` stops users from adding their own hooks next to the managed ones.

A `disableAllHooks: true` in a user, project, or project-local settings file does not turn managed hooks off. It only stops that user's own hooks. Verified on Claude Code 2.1.220: managed hooks still fired with `disableAllHooks: true` in both `.claude/settings.json` and `.claude/settings.local.json`. Managed hooks turn off only when `disableAllHooks` is set inside managed settings, which only an administrator can do.

{% hint style="warning" %}
**Windows must use `payloads/nightfall-hooks.windows.json`.** Stage it under the name `nightfall-hooks.json`. The macOS payload uses a POSIX shell command (`command -v`, `exec`, `/dev/null`, `&&`, `||`). On Windows, Claude Code runs hooks in Git Bash when Git Bash is installed, and in Windows PowerShell 5.1 when it is not. Under PowerShell 5.1 that command does not parse, the relay never runs, and the console shows hooks **Not configured** — while the file on disk still matches its hash. `install.ps1` refuses the macOS payload.

The Windows payload uses exec form (`command` plus `args`) and spawns the relay directly, so it behaves the same whether or not Git Bash is installed. Exec form also keeps the relay's exit code. Shell form on Windows does not, and some hook events are enforced by exit code, so a swallowed exit code turns a block into an allow.

When the agent is not installed, the macOS payload does nothing. The Windows payload reports a non-blocking error on each event until the agent is present. It does not block the developer. Deploy the agent and the payload to the same device group.
{% endhint %}

For a mixed macOS/Windows fleet that must use one document (the claude.ai admin console), use the Windows payload. Its command runs on macOS as well. The macOS payload does not run on a Windows device without Git Bash.

### File drop (the default)

Use this when `/status` shows no higher-priority source. Two steps, in order. Then developers fully quit Claude Code and reopen it. Managed settings load at startup only.

#### Step 1 — stage the payload

Your MDM drops one file at a fixed path. The install script only reads this path. It does not create the file.

|                 | macOS                                                   | Windows                                               |
| --------------- | ------------------------------------------------------- | ----------------------------------------------------- |
| File in the zip | `payloads/nightfall-hooks.json`                         | `payloads/nightfall-hooks.windows.json`               |
| Staged as       | `/opt/nightfall/hooks/claude-code/nightfall-hooks.json` | `C:\Nightfall\Hooks\claude-code\nightfall-hooks.json` |

The staged name is `nightfall-hooks.json` on both platforms. The file you copy from the zip is not the same file. If the MDM cannot target that path, change `STAGING` at the top of both `install.sh` and `audit.sh` (or `$staging` in both PowerShell scripts) to the same new path.

Wrap the file in a `.pkg` (macOS), an `.msi` or `.intunewin` (Windows), or use a raw file feature such as Workspace ONE Files. The `README.md` in the zip has copy-paste recipes for each.

#### Step 2 — run the install script

Run it at System / SYSTEM scope, on enrollment and on a recurring schedule. It copies the staged file into Claude Code's `managed-settings.d/` and locks permissions. Re-running re-copies the file.

|        | macOS                                                                             | Windows                                                               |
| ------ | --------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| Script | `scripts/macos/install.sh`                                                        | `scripts/windows/install.ps1`                                         |
| Writes | `/Library/Application Support/ClaudeCode/managed-settings.d/nightfall-hooks.json` | `C:\Program Files\ClaudeCode\managed-settings.d\nightfall-hooks.json` |

`install.sh` exits **1** if the staging file is missing, or if a `com.anthropic.claudecode` profile is already on the Mac. `install.ps1` exits **1** if staging is missing, if the macOS payload was staged, or if an HKLM `Settings` value already exists. That failure means switch carriers. Set `NIGHTFALL_ALLOW_SHADOWED_INSTALL=1` only if you intend to write a file that Claude Code will ignore.

`audit.sh` and `detect.ps1` report no drift (exit 0) when a higher-priority source is present, and they log why, so Kandji and Intune do not loop. A device stuck reporting `shadowed-by-hklm-policy`, or logging that a managed-preferences profile is present, needs its hooks moved to the matching carrier.

#### Step 3 — developers quit and reopen Claude Code

A session that was already open does not pick up the file.

* macOS: Quit with Cmd+Q, confirm no `claude` process remains, then relaunch.
* Windows: Close every window, confirm no `claude` process in Task Manager, then relaunch.

#### Step 4 — validate

On the Devices page in the Nightfall console, confirm the **Last-event** timestamp moved after someone used Claude Code in a session started after the install. See [Validation](#validation).

### MDM mapping

| MDM              | Platform | Wire into                                         | Script                        |
| ---------------- | -------- | ------------------------------------------------- | ----------------------------- |
| Rippling         | macOS    | Custom Script (on enrollment + daily)             | `scripts/macos/install.sh`    |
| Rippling         | Windows  | PowerShell Script                                 | `scripts/windows/install.ps1` |
| Jamf Pro         | macOS    | Script + Policy (Recurring Check-in + Enrollment) | `scripts/macos/install.sh`    |
| Kandji           | macOS    | Custom Script — Audit                             | `scripts/macos/audit.sh`      |
| Kandji           | macOS    | Custom Script — Remediation                       | `scripts/macos/install.sh`    |
| Microsoft Intune | Windows  | Win32 Remediation — Detection                     | `scripts/windows/detect.ps1`  |
| Microsoft Intune | Windows  | Win32 Remediation — Remediation                   | `scripts/windows/install.ps1` |
| Workspace ONE    | macOS    | Script (System context, Periodic + Enrollment)    | `scripts/macos/install.sh`    |
| Workspace ONE    | Windows  | Script (SYSTEM context, Periodic + Enrollment)    | `scripts/windows/install.ps1` |

Rippling, Jamf, and Workspace ONE re-run the install script. Kandji and Intune run the install script only when the audit or detect script reports drift.

{% hint style="warning" %}
**Audit and detect exit codes mean drift. That polarity is the opposite of a Rippling pre-install check.** Exit 0 from `audit.sh` or `detect.ps1` means in sync, staging not deployed yet, or a higher-priority source is already winning — do nothing. Exit 1 means drift — run the install script. A Rippling pre-install check proceeds when the check exits 0. Wire `install.sh` / `install.ps1` directly on Rippling, Jamf, and Workspace ONE. Do not put `audit.sh` in front of them.
{% endhint %}

### Alternative carriers

Use the carrier that matches the source already winning. A `managed-settings.d/` drop-in deployed beside any of these does not load. Use the macOS payload on macOS carriers and the Windows payload on Windows carriers.

#### macOS managed-preferences profile

`payloads/nightfall-hooks.mobileconfig` is the ready-to-deploy form.

The plist carrier is proven: a customer deployed this payload's hooks through a `com.anthropic.claudecode` profile on Claude Code 2.1.205 and confirmed hooks firing. An automated test asserts the profile's keys match `nightfall-hooks.json`. Nightfall has not installed this exact `.mobileconfig` through an MDM. Pilot it on one machine, run `/status`, and confirm the Last-event timestamp advances before scoping it to the fleet.

**No `com.anthropic.claudecode` profile yet:** upload the file as a configuration profile scoped to the same device group — Addigy, Jamf (Configuration Profile → Application & Custom Settings), Kandji (Custom Profile), Rippling, or Workspace ONE (Custom Settings). On the device, `/status` should show `Enterprise managed settings (plist)`.

**A profile in that domain already exists:** copy `allowManagedHooksOnly` and `hooks` from this profile into the existing one, and keep deploying that single profile. Two profiles claiming `com.anthropic.claudecode` have no defined winner.

`install.sh` and `audit.sh` are unused on this path. The profile is the deployment. `install.sh` detects the profile and exits 1.

`payloads/nightfall-hooks.json` stays the source of truth. Regenerate keys from that file after a payload change. The relay command contains `>` and `&&`, which must be XML-escaped.

```bash
plutil -convert xml1 -o - payloads/nightfall-hooks.json
```

The shipped profile uses fixed `PayloadUUID` values so a redeploy updates in place. Regenerate them with `uuidgen` only if the MDM requires org-unique UUIDs.

#### Windows HKLM policy

Set `Settings` under `HKLM\SOFTWARE\Policies\ClaudeCode` to the JSON in `payloads/nightfall-hooks.windows.json`. The macOS payload fails here for the same PowerShell reason as the file drop.

**No other Claude Code policy keys:** write the payload verbatim.

```powershell
$key = 'HKLM:\SOFTWARE\Policies\ClaudeCode'
New-Item -Path $key -Force | Out-Null

# Absolute path: an MDM script's working directory is not where the payload landed.
$json = Get-Content 'C:\Nightfall\Hooks\claude-code\nightfall-hooks.json' -Raw
Set-ItemProperty -Path $key -Name Settings -Value $json -Type String
```

The staged file must be the Windows payload, under the name `nightfall-hooks.json`. To avoid staging a file, paste the JSON into the script.

**The value already has other keys:** combine the two JSON documents into one. If the org already sets its own `hooks` key, merge the hook events.

{% hint style="danger" %}
Do not rebuild the value with `ConvertFrom-Json` / `ConvertTo-Json` on Windows PowerShell 5.1. Every hook event is a single-element array, and 5.1 unwraps those arrays into bare objects. Claude Code rejects the shape and the hooks never load, while the registry value still looks populated. Assemble the merged document by hand, or with tooling you can verify, and check that each of `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, and `Stop` is still a JSON array afterward.
{% endhint %}

Deploy as an MDM script at SYSTEM scope, or set the value through the MDM's registry feature. `/status` should show `Enterprise managed settings (HKLM)`. Confirm the Last-event timestamp advances.

Save the script as ASCII or UTF-8 with a BOM. Windows PowerShell 5.1 reads a no-BOM `.ps1` in the machine's ANSI codepage. One non-ASCII character makes the script fail to parse, and nothing is installed.

#### Server-managed (claude.ai admin console)

If the organization sets Claude Code keys in the claude.ai admin console, that source outranks the carriers above. Paste the payload into the managed settings JSON there, merged with the existing keys. The JSON is used verbatim.

This carrier delivers one document to every device. For a mixed macOS/Windows fleet, use `payloads/nightfall-hooks.windows.json`. It runs on macOS.

### Validation

On the Devices page, a healthy Claude Code status means some hook event arrived in the **last 72 hours**. There is no alert when a client that was healthy goes silent. After a new deployment, a carrier change, or a payload update, confirm the Last-event timestamp advanced after a developer used Claude Code in a session started after the install.

On the device, the deployed file should exist at the target path, and `nightfall-hook-relay` should resolve on PATH. `claude --debug` should log `Hook PreToolUse … success`.

{% hint style="warning" %}
On Claude Code 2.1.x the in-app `/hooks` menu can show **"0 hooks configured"** while managed hooks are loaded and firing (confirmed on 2.1.159). Use the console timestamp and `claude --debug`. There is no user-consent dialog. Managed hooks apply silently (confirmed on 2.1.205 for the drop-in and for managed preferences). A missing dialog is the expected result.
{% endhint %}

### Rollback

Remove the MDM assignment first when the install is on a recurring trigger. The next check-in would put the file back. The relay binary stays installed. The Nightfall agent owns it.

| Carrier             | Remove                                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| File drop, macOS    | `/Library/Application Support/ClaudeCode/managed-settings.d/nightfall-hooks.json`                                                     |
| File drop, Windows  | `C:\Program Files\ClaudeCode\managed-settings.d\nightfall-hooks.json`                                                                 |
| Managed preferences | Nightfall's `hooks` and `allowManagedHooksOnly` keys from the profile, then re-push. Remove the profile if it contained nothing else. |
| HKLM                | The Nightfall keys from the `Settings` value, or the value if it contained nothing else.                                              |
| Server-managed      | The same keys from the admin-console settings JSON.                                                                                   |

### Payload versioning

`nightfall-hooks.json` is the source of truth for macOS carriers (file drop and the configuration profile). `nightfall-hooks.windows.json` is the source of truth for Windows carriers and for a mixed-fleet admin-console document. A profile or admin-console copy does not change when the agent updates. Checksum the file you embedded against each new zip and rebuild the embedded copy when it differs:

```bash
shasum -a 256 payloads/nightfall-hooks.json
shasum -a 256 payloads/nightfall-hooks.windows.json
```

### Notes

* **Anthropic Windows path migration (March 2026).** Claude Code on Windows reads managed settings from `C:\Program Files\ClaudeCode\`. The scripts target that path. Pin managed devices to a current Claude Code version.
* **Claude Code in VS Code** bundles its own CLI, which can be a different version from the standalone `claude` binary. Update both, and confirm events from the surface the developer actually uses.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.nightfall.ai/data-exfiltration-prevention/ai-agent-security/ai-governance/auditability-and-control/setup-and-installation/nightfall-hooks-for-claude-code.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
