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

# Deploy hooks for Snowflake Cortex Code

> Install Endor Labs hooks for the Snowflake Cortex Code CLI to capture session, prompt, command, file, and MCP tool events.

Endor Labs hooks for Snowflake Cortex Code send a structured event for every session, prompt, shell command, file operation, and MCP tool call. The hook also enforces your Coding Agent Governance policies locally before each tool call runs.

Cortex Code reads hooks in the Claude Code hooks format, so the configuration has the same shape as a Claude Code configuration with its own command.

## Before you begin

Confirm the following requirements before you install the hook:

* An admin has completed the [prerequisites](https://docs.endorlabs.com/agent-governance/prerequisites.md), including creating an API key with the **AI Audit User** role.
* The target machine runs macOS with the Cortex Code CLI (`cortex`) installed. Endor Labs hasn't verified Cortex Code on Linux or native Windows yet.
* [endorctl](https://docs.endorlabs.com/setup-deployment/cli.md) is on `PATH`.
* The environment that launches Cortex Code has these environment variables set:
  * `ENDOR_API` (for example, `https://api.endorlabs.com`)
  * `ENDOR_NAMESPACE`
  * `ENDOR_API_CREDENTIALS_KEY`
  * `ENDOR_API_CREDENTIALS_SECRET`

> [!WARNING]
>
> Do not export `ENDOR_TOKEN` in the environment that runs the hook. Hooks authenticate with the API key and secret only, and on older versions of endorctl the extra token triggered a mixed-authentication error that blocked `endorctl ai-audit`.

Every event runs `endorctl ai-audit cortexcode` with no flags, so endorctl reads the credentials from the environment. Use the following hook configuration.

```json expandable theme={null}
{
  "hooks": {
    "SessionStart": [
      { "matcher": "*", "hooks": [{ "type": "command", "command": "endorctl ai-audit cortexcode", "timeout": 30 }] }
    ],
    "UserPromptSubmit": [
      { "matcher": "*", "hooks": [{ "type": "command", "command": "endorctl ai-audit cortexcode", "timeout": 30 }] }
    ],
    "PreToolUse": [
      { "matcher": "*", "hooks": [{ "type": "command", "command": "endorctl ai-audit cortexcode", "timeout": 30 }] }
    ],
    "PostToolUse": [
      { "matcher": "*", "hooks": [{ "type": "command", "command": "endorctl ai-audit cortexcode", "timeout": 30 }] }
    ],
    "SessionEnd": [
      { "matcher": "*", "hooks": [{ "type": "command", "command": "endorctl ai-audit cortexcode", "timeout": 30 }] }
    ]
  }
}
```

The event cache is on by default, so you don't need to set `ENDOR_AI_AUDIT_CACHE_ENABLED`. The hook queues events locally and sends them on its next run after 30 seconds, at `SessionEnd`, and at the next `SessionStart`. The configuration registers no `Stop` hook, so events from the end of a session can arrive late, but they are not lost.

The `timeout` value is the longest Cortex Code waits for each hook. endorctl sets its own shorter time limit, 5 seconds by default. If the hook runs out of time, Cortex Code allows the tool call.

## Install for one developer

Use this for a single-machine trial or a personal install.

1. Open `~/.snowflake/cortex/hooks.json` in a text editor. Create the file if it does not exist.
2. Paste the configuration shown in [Before you begin](#before-you-begin). If the file already has hooks, add the Endor Labs entries under its existing `hooks` key.
3. Save the file and start a new Cortex Code session.

Cortex Code uses `$CORTEX_HOME` if set, else `$SNOWFLAKE_HOME/cortex`, else `~/.snowflake/cortex`. Put `hooks.json` in the first folder that applies. See [Known limitations](#known-limitations) for how a custom home affects inventory.

## Install for a repository

Add the Endor Labs entries from the configuration under the `hooks` key of `<repository>/.cortex/settings.json`, and commit the file. Every developer who runs Cortex Code in that repository then runs the hook.

## Install on a managed fleet

Cortex Code has no managed hooks path, so an administrator can't lock the hooks in place. A developer can edit or remove both the user file and the repository file. You can't guarantee coverage on every machine, so combine the following:

* Use your MDM, such as Jamf, Intune, Kandji, or JumpCloud, to deliver `~/.snowflake/cortex/hooks.json` and the four environment variables to each developer.
* Commit `.cortex/settings.json` with the hooks to the repositories your developers work in.

The [in-browser generator](https://docs.endorlabs.com/agent-governance/mdm-deployment.md#generate-a-configuration-in-your-browser) and the mdm-scripts repository don't cover Cortex Code yet, so maintain the file by hand.

> [!NOTE]
>
> On macOS, environment variables set in `~/.zshrc` reach apps launched from a terminal but not apps launched from Spotlight. Ship the variables through your MDM payload (for example, a `launchctl setenv` profile or an `~/.zprofile` snippet) for full coverage.

> [!TIP]
>
> If your MDM's deployment script rejects the inline JSON payload, encode the configuration in Base64 in the script, and decode it on the target machine. This avoids the quoting and escaping issues that some MDMs introduce when publishing JSON.

## Configuration the hook reads at session start

At `SessionStart`, the hook takes a snapshot of the Cortex Code configuration for inventory and session policies. It reads the snapshot once per session and doesn't rescan during the session.

* **User level**: `settings.json`, `hooks.json`, `mcp.json`, and `permissions.json` under `$SNOWFLAKE_HOME/cortex` (default `~/.snowflake/cortex`). The hook also reads hooks from `~/.claude/settings.json` and MCP servers from `~/.claude.json`.
* **Project level**: `.cortex/settings.json`, `.cortex/settings.local.json`, `.claude/settings.json`, and `.claude/settings.local.json`. The `.claude` files contribute hooks only. The project directory is `CORTEX_PROJECT_DIR` when set, otherwise the hook's working directory, not the Git root.
* **MCP servers**: The hook merges servers by name, and the last source wins. The order is the top level of `~/.claude.json`, then its entry for the project directory, then `$SNOWFLAKE_HOME/cortex/mcp.json`.
* **Enterprise MCP servers**: Servers from the managed `files.mcpFile` setting stay separate from the merged list. The setting must be an absolute path.

The hook reads `~/.claude.json` only when `importClaudeMcpServers` is on, which is the default. A project-level `importClaudeMcpServers` value overrides the user-level one.

## Managed settings and MDM policy

Cortex Code accepts a managed settings file and an MDM policy from your administrator. Neither one delivers hooks, but the hook reads both at `SessionStart` to report sandbox posture, permission bypass, and enterprise MCP servers.

### Managed settings file

The hook reads the managed settings file from a fixed path on each platform.


&#x20; \- Platform: macOS
&#x20; Path: \`/Library/Application Support/Cortex/managed-settings.json\`
&#x20; Trust check: Owned by root and not writable by group or others
&#x20; \- Platform: Linux
&#x20; Path: \`/etc/cortex/managed-settings.json\`
&#x20; Trust check: None
&#x20; \- Platform: Windows
&#x20; Path: \`%ProgramData%\Cortex\managed-settings.json\`
&#x20; Trust check: None. Endor Labs doesn't check the file's access control list
&#x20;&#x20;

The hook ignores the whole file and reports the sandbox posture as **Unknown** when the file fails its trust check or any of the following validations:

* `version` is exactly `"1.0"`.
* `permissions.defaultMode` is `allow` or `deny`.
* `sql.defaultMode` is `read-only` or `write`.
* `sql.lockMode` appears only together with `sql.defaultMode`.
* `settings.forceSandboxMode` is `regular` or `autoAllow`.
* `defaults.theme` is `dark`, `light`, or `pro`.
* `required.minimumVersion` is a semantic version without a `v` prefix.

### MDM policy

On macOS and Windows, the hook also reads a Cortex Code MDM policy. The policy holds the managed settings JSON as a Base64-encoded value.

* **macOS**: Preference domain `com.snowflake.coco`. The hook reads `/Library/Managed Preferences/<username>/com.snowflake.coco.plist`, then `/Library/Managed Preferences/com.snowflake.coco.plist`. For each key, the first file that sets it wins.
* **Windows**: Registry key `HKLM\SOFTWARE\Policies\Microsoft\CortexCode`.


&#x20; \- Key: \`CortexManagedSettingsBase64\`
&#x20; Type: String (\`REG\_SZ\` on Windows)
&#x20; Value: The managed settings JSON, Base64-encoded. The value can be up to 256 KiB.
&#x20; \- Key: \`CortexManagedSettingsBase64Enabled\`
&#x20; Type: Boolean (\`REG\_DWORD\` on Windows)
&#x20; Value: Turns the policy on when absent or true. Set it to false (\`0\`) to turn the policy off and apply the managed settings file instead.
&#x20;&#x20;

The MDM policy wins over the managed settings file for `settings.forceSandboxEnabled`, the whole `permissions` block, and `files.mcpFile`. Every other setting comes from the file.

Any of the following MDM policy failures reports the sandbox posture as **Unknown**:

* The value is unreadable or larger than 256 KiB.
* The value isn't valid Base64, isn't valid JSON, or fails schema validation.
* The property list is larger than 1 MiB, or `plutil` times out.

### Sandbox posture

The hook resolves each session's sandbox posture in the following order, and the first rule that applies wins:

1. A trusted managed `settings.forceSandboxEnabled: true` reports **Sandboxed**.
2. Any warning while reading the configuration, such as an ignored managed file or a failed MDM policy, reports **Unknown**.
3. The first explicit `sandbox.enabled` value in the project `.cortex/settings.json`, then in `$SNOWFLAKE_HOME/cortex/settings.json`, sets the posture.
4. Otherwise, the posture is **Not sandboxed**.

A managed `forceSandboxEnabled: false` doesn't force the sandbox off. On Windows, `forceSandboxEnabled: true` in the managed settings file alone reports **Unknown**, never **Sandboxed**, because the hook can't trust the file. An MDM policy that forces the sandbox reports **Sandboxed**.

See [Sandbox posture](https://docs.endorlabs.com/agent-governance/inventory.md#sandbox-posture) to read the posture in inventory, and [Session sandbox policies](https://docs.endorlabs.com/agent-governance/policies.md#session-sandbox-policies) to act on it.

### Permission bypass

The hook reports permission bypass (`dangerouslyAllowAll`) from the managed level only. When managed settings are present, the hook reports bypass as disabled unless they set `dangerouslyAllowAll: true`. A user-level `dangerouslyAllowAll` value never reports, and the Windows managed settings file alone never reports bypass state.

## Cortex Code hook events used by Coding Agent Governance

The following table lists every event the hook processes.


&#x20; \- Hook event: \`SessionStart\`
&#x20; Why it matters: Records the session, agent version, and user for inventory. Captures the configuration snapshot, refreshes the local policy cache, and sends queued events. The hook reads the version from \`cortex --version\` and waits up to 1 second for it.
&#x20; \- Hook event: \`UserPromptSubmit\`
&#x20; Why it matters: Records that a prompt was submitted, for session counters in inventory. The prompt text stays on the developer's machine.
&#x20; \- Hook event: \`PreToolUse\`
&#x20; Why it matters: Enforces policies before a tool runs. See \[How Cortex Code tools map to policies]\(#how-cortex-code-tools-map-to-policies).
&#x20; \- Hook event: \`PostToolUse\`
&#x20; Why it matters: Captures completed tool calls for inventory.
&#x20; \- Hook event: \`SessionEnd\`
&#x20; Why it matters: Marks the end of a session for inventory and sends remaining events.
&#x20;&#x20;

The hook accepts and ignores `Stop`, `Notification`, `PermissionRequest`, and `SubagentStop` if a configuration registers them. Those events record nothing.

### How Cortex Code tools map to policies

The hook maps each Cortex Code tool to a policy template by tool name.


&#x20; \- Cortex Code tool: \`Bash\`
&#x20; Policy template: \*\*Command Execution\*\*
&#x20; \- Cortex Code tool: \`Read\`, \`Write\`, \`Edit\`, \`NotebookEdit\`
&#x20; Policy template: \*\*File Access\*\*
&#x20; \- Cortex Code tool: \`mcp\_\_\<server>\_\_\<tool>\`
&#x20; Policy template: \*\*MCP Server Access\*\*
&#x20; \- Cortex Code tool: Any other tool, such as \`sql\_execute\`, \`Task\`, \`Glob\`, or \`agent\_output\`
&#x20; Policy template: None. Endor Labs records a generic tool call.
&#x20;&#x20;

No policy template inspects the SQL that `sql_execute` runs. Cortex Code names its sub-agent tool `Task`, so policies written for the Claude Code `Agent` tool don't match it.

### Enforcement behavior

If the hook can't run or doesn't finish in time, Cortex Code allows the tool call and keeps working, so a hook problem never blocks a developer. The usual cause of a hook that can't run is a missing binary. endorctl gives each hook run a time limit of 5 seconds by default, or 10 seconds when your policies use the AI judge. `ENDOR_AI_AUDIT_HOOK_TIMEOUT` overrides the limit.

An unreadable local policy cache does not disable enforcement, because the hook falls back to the built-in system policies. A successfully evaluated **Block** policy still stops the tool call.

On a policy match at `PreToolUse`, a **Block** policy denies the tool call, and Cortex Code shows the reason from the policy. An **Ask Permission** policy shows Cortex Code's own permission prompt, without the reason from the policy. An **Alert** policy lets the call proceed and shows the developer nothing, so only Endor Labs records the match.

### User attribution

The hook attributes Cortex Code events to the value of `ENDOR_ACTOR` when the hook environment sets it. Otherwise, it uses the operating system login.

## Tune the hook

For an audit-only rollout, set `ENDOR_AI_AUDIT_NO_BLOCKING=true` in the Snowflake Cortex Code hook environment. [endorctl ai-audit](https://docs.endorlabs.com/developers-api/cli/commands/ai-audit.md) then downgrades every `Block` action to `Alert` at evaluation time. For Cursor, set it in the shell that launches the IDE. For Claude Code, set it in the `env` block of `settings.json`. Policies still record violations under **Policy Violations**, but the hook never denies a tool call. Use audit-only mode to seed a new policy without interrupting developers, then unset the variable when you're ready to enforce.

Any value that Go parses as a `bool` (such as `true`, `1`, or `t`) turns the option on. Unset, empty, or unparseable values keep enforcement on.

## Verify hooks are firing

1. Start a session in Snowflake Cortex Code and ask the agent to run a benign shell command, such as `ls`.
2. Select **Agent Governance** from the left sidebar, then select **Inventory**. The page opens on the **Workstation Inventory** tab.
3. Confirm the developer's Snowflake Cortex Code row shows a recent value under **Last Active**.

If no row appears, see [Troubleshoot missing events](#troubleshoot-missing-events).

## What developers see when a policy fires

When a policy with the **Block** action matches an event, the hook returns a deny response to Snowflake Cortex Code. Snowflake Cortex Code stops the tool call and shows the **User Message** to the developer when the hook response can carry it. A response that includes a message also explains that a governance policy denied the action and tells the agent not to work around the block.

When a policy with the **Ask Permission** action matches, Snowflake Cortex Code asks the developer to confirm or deny, where it can pause at that event. Where it cannot, the outcome depends on the event. Endor Labs records the match under **Policy Violations** as **Ask** either way. See [Enforcement behavior](https://docs.endorlabs.com/agent-governance/how-it-works.md#enforcement-behavior) to review the per-agent behavior.

When a policy with the **Alert** action matches, the hook allows the action to proceed. Endor Labs records the event under **Policy Violations** for your security team to review.

## Troubleshoot missing events

Run these checks if a developer's actions never appear under **Workstation Inventory** or **Policy Violations**:

* Confirm the developer has started at least one session. With the event cache enabled, the hook batches events and sends them to Endor Labs within about 30 seconds of hook activity. The hook also sends every queued event at session start and end. Inventory **Sessions**, **Last Active**, and counts can therefore lag the newest actions by that interval.
* Hook errors never block the developer. If the hook cannot run or reach the Endor Labs API, Snowflake Cortex Code allows the tool call. The event is missing from inventory, and no error appears. The remaining checks rule out the common causes.
* Confirm endorctl is on `PATH` in the environment that runs the hook. If the binary is not found, the hook does nothing and shows no error. Run `endorctl version` from the same shell to verify.
* Confirm only one endorctl installation is active. If `endorctl ai-audit` fails with `Failed with non-blocking status code: No stderr output`, conflicting installations are the likely cause, such as an `npx`-provisioned endorctl alongside a Homebrew install. Keep one installation method per machine, remove the others, then run `endorctl version` to confirm.
* Confirm `ENDOR_API`, `ENDOR_NAMESPACE`, `ENDOR_API_CREDENTIALS_KEY`, and `ENDOR_API_CREDENTIALS_SECRET` are set for the process that runs the hook. Cursor reads them from the shell that launched it. Claude Code reads them from the `env` block in `settings.json`.
* Confirm `ENDOR_TOKEN` is not also exported alongside your Coding Agent Governance API key credentials in the hook environment. Hooks rely only on `ENDOR_API_CREDENTIALS_KEY` and `ENDOR_API_CREDENTIALS_SECRET`. `ENDOR_TOKEN` is redundant and, on older versions of endorctl, triggered a mixed-authentication error that blocked `endorctl ai-audit`.
* Confirm the API key has the **AI Audit User** role and has not expired.
* Confirm the developer restarted Snowflake Cortex Code after the hooks file changed. Snowflake Cortex Code reads the configuration at session start.
* Confirm the developer restarted Snowflake Cortex Code after a policy edit. The local policy cache refreshes at session start.
* Confirm the JSON file parses. A trailing comma silently disables every hook in the file.
* Confirm the Endor Labs API is reachable from the environment that runs the hook. Network failures appear as gaps in the inventory.

## Known limitations

Cortex Code support has the following limitations:

* **Session policies**: A **Block** or **Ask Permission** session policy doesn't stop a Cortex Code session, because Cortex Code ignores `continue: false` at `SessionStart`. A match on a Cortex Code session only records the violation.
* **Claude Code hooks in Cortex Code sessions**: Cortex Code also runs hooks from `~/.claude/settings.json`, `.claude/settings.json`, and `.claude/settings.local.json`. Events from an `endorctl ai-audit claudecode` hook there record as Claude Code, so one Cortex Code session can appear under both agents.
* **Custom home directory**: The hook reads configuration under `SNOWFLAKE_HOME` but ignores `CORTEX_HOME`, and AI tool discovery looks only in `~/.snowflake/cortex`.
* **Two inventory rows on one device**: A device can show both a governed **Snowflake Cortex Code** agent and a discovered `cortex-code-cli` AI tool.
* **Skills**: Endor Labs doesn't collect Cortex Code skills, so they don't appear on the **Skills** tab.
* **Project MCP servers and permissions**: The hook doesn't read MCP servers or permissions declared at the project level. Those servers can appear in inventory from their tool calls, without an Endor Score.
* **Windows managed settings file**: The hook doesn't check the file's access control list, so the file alone can't make the posture **Sandboxed**. Use the MDM policy to force the sandbox on Windows.
* **Headless runs**: Endor Labs hasn't verified the hook with headless `cortex -p` and `cortex exec` runs.

## Next steps

With hooks deployed, continue with the following pages:

* See [Triage policy violations](https://docs.endorlabs.com/agent-governance/policy-violations.md) to set your daily review workflow.
* See [Read the Coding Agent Governance overview](https://docs.endorlabs.com/agent-governance/overview.md) to track adoption and enforcement trends.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.