Enterprise

macOS standalone deployment

Install the standalone DefenseClaw enterprise LaunchDaemons on Apple silicon Macs from the pkg or the payload tarball, check the result, and remove them.

The standalone macOS profile installs DefenseClaw machine-wide under /opt/cisco/defenseclaw, with LaunchDaemons labeled com.cisco.defenseclaw.* and a hidden service account, _defenseclaw. It is separate from the Cisco Secure Client module (/opt/cisco/secureclient/defenseclaw) and refuses to install beside it. Standard users cannot stop the daemons or change the config, and the hook guardian restores DefenseClaw's hooks if a user removes them. For the terms used on this page, see Concepts.

Deploying with an MDM?

This page installs on one Mac from an administrator shell. To deploy with Jamf Pro, Kandji, Intune, Workspace ONE or another MDM, start with Install with an MDM. It uses the same pkg and the same config.

Requirements

RequirementDetail
macOSmacOS 13.0 or later. The pkg refuses older versions.
HardwareApple silicon only. The pkg is arm64-only, and releases build no Intel macOS binaries, so neither the pkg nor the tarball runs on an Intel Mac.
Privilegesroot, through sudo or an MDM agent. Only enterprise macos status works without root.
Other profilesNo Secure Client DefenseClaw module. The pkg's pre-install script refuses when /opt/cisco/secureclient/defenseclaw or a com.cisco.secureclient.defenseclaw* LaunchDaemon exists.
GatekeeperOnly the pkg can be Developer ID-signed and notarized. It is signed only when the release pipeline has Apple signing credentials, and notarized only when it also has notary credentials, so a release can be signed but not notarized. The tarball's binaries are never Developer ID-signed. On Macs that require notarized software, use the pkg and check it first with spctl --assess --type install --verbose=2 <pkg>.

Layout

All paths are under /opt/cisco/defenseclaw unless shown in full.

ItemPathOwner and mode
Binariesbin: defenseclaw-gateway, defenseclaw-hook, defenseclaw-sensor-helper, and defenseclaw-acp when presentroot, 0755
Managed OpenCode pluginshare/opencode/defenseclaw.jsroot, 0644
Configetc/config.yamlroot:_defenseclaw, 0640
Policiesetc/policiesroot:_defenseclaw, 0750
Secretsetc/secretsDirectory root:_defenseclaw 0750, files 0640
Target manifestetc/hook-guardian/targets.yaml, written by the enumeratorDirectory root:_defenseclaw, 0750
Runtime descriptoretc/managed-runtime.json, the non-secret settings that hooks readroot, 0644
Machine-policy summaryetc/machine-policy.jsonReadable by users
Lifecycle statelifecycle, including last-package-result.json, last-activation-failure.log, the last applied config and a rejected oneroot, 0700
Gateway dataruntime_defenseclaw, 0700 (the device identity key needs a private directory)
Authorization ledgerhook-guardian-state, with each enrolled user's latest AI Discovery scan in ai-discovery/root:_defenseclaw, 0750
Hook socketrun/hook.sockDirectory _defenseclaw, 0755
Logs/Library/Logs/Cisco/DefenseClaw, with the gateway's logs in gateway/root, 0755; gateway/ is _defenseclaw, 0750
LaunchDaemons/Library/LaunchDaemons/com.cisco.defenseclaw.*.plistroot, 0644

The daemons:

LabelRuns asWhat it does
com.cisco.defenseclaw.gateway_defenseclawPolicy engine and hook API. Binds 127.0.0.1:18970 and run/hook.sock.
com.cisco.defenseclaw.hook-guardianrootAlways running. Reconciles each user's hooks every minute (enterprise hooks watch --interval 1m), through a worker that runs as each user. The same worker runs each enrolled user's AI Discovery scan (Per-user scans). It does not repair machine policy, including the Claude Code version floor: ensure, repair and reconcile do.
com.cisco.defenseclaw.hook-enumeratorrootRewrites the target manifest every five minutes (in the default auto enrollment mode)
com.cisco.defenseclaw.sensor-helperrootAnswers the gateway's fixed AI Discovery requests
com.cisco.defenseclaw.applyrootRuns ensure --reason path when etc/config.yaml, etc/secrets or etc/policies changes
com.cisco.defenseclaw.verifyrootRuns verify every day at 03:17 local time
starts as _defenseclaw
starts as root
spawns
user config
hook.sock, peer uid
TrustedZ0 · Admin and MDM (root)
RestrictedZ2 · Gateway (_defenseclaw)
PrivilegedZ1 · Services (root)
UntrustedZ4 · User session (user)
SystemlaunchdLaunchDaemons
Control planegateway
Systemhook-guardian
Systemper-user workeras the user
Agent runtimeAI agent
Connectordefenseclaw-hook
Standalone macOS. launchd hands over sockets only through launch_activate_socket(3), which needs cgo, and release builds do not use cgo. So the gateway binds its own sockets, in a directory only root or its service account can write. The hook checks the listener's owner before it sends anything.

See Threat model for what each zone may do.

Install

Stage the config first, then install the pkg or the tarball. The lifecycle picks its config in this order: --config, then /opt/cisco/defenseclaw/etc/config.yaml, then a built-in default. The default runs observe mode with no connectors, so it protects nothing.

1. Get and check the release

Download from the same GitHub release:

  • defenseclaw-enterprise-<version>-darwin-arm64.pkg, or the tarball defenseclaw-enterprise-<version>-darwin-arm64.tar.gz;
  • checksums.txt and checksums.txt.bundle.

Check the signature on checksums.txt with cosign, then the file against it, then the pkg's own signature:

VERSION=1.4.0  # the release you deploy
cosign verify-blob --bundle checksums.txt.bundle \
  --certificate-identity "https://github.com/cisco-ai-defense/defenseclaw/.github/workflows/release.yaml@refs/heads/main" \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  checksums.txt
grep " defenseclaw-enterprise-${VERSION}-darwin-arm64.pkg\$" checksums.txt | shasum -a 256 -c
pkgutil --check-signature "defenseclaw-enterprise-${VERSION}-darwin-arm64.pkg"

pkgutil reports "no signature" for a pkg built without Apple credentials. Such a pkg does not satisfy Gatekeeper on Macs that require notarization.

2. Stage the config

This example protects Codex and Claude Code in observe mode. The lifecycle requires config_version: 8, deployment_mode: managed_enterprise, enterprise.profile: standalone and data_dir: /opt/cisco/defenseclaw/runtime. If you set gateway.api_bind or gateway.api_port, they must be 127.0.0.1 and 18970.

config.yaml
config_version: 8
deployment_mode: managed_enterprise
data_dir: /opt/cisco/defenseclaw/runtime
policy_dir: /opt/cisco/defenseclaw/etc/policies
enterprise:
  profile: standalone
gateway:
  api_bind: 127.0.0.1
  api_port: 18970
guardrail:
  enabled: true
  mode: observe
  connectors:
    codex: {}
    claudecode: {}

Each key under guardrail.connectors turns on protection for that agent. See Choose the agents to protect and the settings reference.

Put it where the lifecycle reads it. A config staged before the install is used as is; it does not count as an unmanaged layout:

sudo install -d -o root -g wheel -m 0755 /opt/cisco/defenseclaw/etc
sudo install -o root -g wheel -m 0640 config.yaml /opt/cisco/defenseclaw/etc/config.yaml

The lifecycle re-owns the file to root:_defenseclaw 0640 when it installs.

3a. Install the pkg

VERSION=1.4.0  # the release you deploy
sudo installer -pkg "defenseclaw-enterprise-${VERSION}-darwin-arm64.pkg" -target /
sudo cat /opt/cisco/defenseclaw/lifecycle/last-package-result.json

The pkg installs the binaries in /opt/cisco/defenseclaw/bin, and its post-install script runs enterprise macos ensure --from-package --reason package --json. That creates _defenseclaw, validates the config, lays out the files, writes the LaunchDaemons, starts them in order, verifies, and rolls back on any failure. A failed apply fails the pkg install, so installer and your MDM report it. The result is in last-package-result.json. The pkg receipt is com.cisco.defenseclaw.enterprise.

If you did not stage a config, the pkg installs the default, which protects nothing. Apply your config afterward. Either copy it to /opt/cisco/defenseclaw/etc/config.yaml, and the apply daemon runs ensure for you, or run ensure yourself and read the result:

sudo /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise macos ensure \
  --from-package --config "$PWD/config.yaml" --json

3b. Or install from the tarball

The payload folder and its binaries must be owned by root and not writable by group or others:

VERSION=1.4.0  # the release you deploy
PAYLOAD="/var/root/defenseclaw-enterprise-${VERSION}"
sudo install -d -o root -g wheel -m 0700 "$PAYLOAD"
sudo tar -xzf "defenseclaw-enterprise-${VERSION}-darwin-arm64.tar.gz" \
  -C "$PAYLOAD" --no-same-owner --no-same-permissions
sudo chmod -R go-w "$PAYLOAD"
sudo "$PAYLOAD/defenseclaw-gateway" enterprise macos ensure --payload "$PAYLOAD" --json

ensure installs when nothing is installed, applies a payload that differs from the installed one, re-applies a changed config, repairs a deployment that fails verification, and otherwise does nothing. Add --config <absolute path> to install a config from another location, and --no-start to stage the deployment with the daemons stopped. If the Mac already has a hand-built DefenseClaw layout, the install stops with unmanaged_layout_present. Add --adopt-existing to back it up and take it over.

Exit codes:

CodeMeaning
0Success, or nothing to do
1Failure. A failed install, upgrade or repair is rolled back, and a refusal before any change leaves the computer as it was. For status and verify, 1 means the deployment is unhealthy.
2Invalid arguments
75Another lifecycle run holds the lock. Retry later.

4. Add the AI Defense key

Without a key, the local policy engine decides alone. To add Cisco AI Defense, set these keys in the config:

config.yaml (excerpt)
enterprise:
  profile: standalone
  inspection:
    ai_defense:
      enabled: true
      credential: ai-defense-api-key

Then store the key. Install first: the command needs the _defenseclaw account, and it runs ensure right after it writes the key, so it fails on a Mac with nothing installed.

sudo /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise secret set \
  --name ai-defense-api-key --from-file /var/root/ai-defense.key --json
sudo rm /var/root/ai-defense.key

See AI Defense key for rotation and removal.

Verify the install

sudo /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise macos status --json
sudo /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise macos verify --json
pkgutil --pkg-info com.cisco.defenseclaw.enterprise   # pkg installs only

status reports the state. verify also checks every file, permission, service and readiness check, and exits 1 when anything fails. Look for:

FieldHealthy value
oktrue
installed, installed_versiontrue and the version you deployed
readinessgateway, guardian, enumerator and sensor_helper all true
coverage_completetrue: the gateway, guardian and enumerator are ready
security_completetrue: coverage is complete, the sensor helper is ready, and there are no errors
enrollment.targetsThe number of per-user targets in the manifest. Connectors published through machine policy (Codex, Claude Code, Cursor, GitHub Copilot) have no per-user targets unless enterprise.enrollment.unenrolled_users is deny, so 0 is normal when you enable only those. For per-user connectors, 0 means nobody is enrolled yet.
errorsEmpty

When the config enables no agent under guardrail.connectors while there are users to enroll, status and verify warn no_connectors_enabled and security_complete is false. Otherwise neither field checks that the users you expect are enrolled, so check enrollment too. The enumerator enrolls accounts with a uid of 501 or higher by default. See Enrollment. To reconcile at once instead of waiting for the next minute, run sudo /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise macos reconcile --json.

See Status and verify and Logs and events for monitoring.

Per-user installs

On a Mac with /opt/cisco/defenseclaw/etc/managed-runtime.json, the per-user product refuses to run:

Per-user actionWhat happens
install.shRefuses and changes nothing
defenseclaw upgradeRefuses
Per-user gatewayRefuses to start

Nothing migrates a per-user install automatically. Before you deploy, have each user remove their per-user install with defenseclaw uninstall --binaries --yes. Add --all to also delete ~/.defenseclaw. See Uninstall.

Remove

GoalCommand
Stop and remove the deployment and its binaries. Keep the config, secrets, data and logs.defenseclaw-gateway enterprise macos uninstall --json
Also remove the config, secrets, data, logs and lifecycle statedefenseclaw-gateway enterprise macos uninstall --purge --json
Also delete the _defenseclaw accountdefenseclaw-gateway enterprise macos uninstall --purge --remove-service-account --json

Run these as root from /opt/cisco/defenseclaw/bin. Uninstall removes each user's DefenseClaw hook registrations and DefenseClaw's own machine-policy entries first. Administrator entries in those files stay as they were. With --purge it also removes each enrolled user's ~/.defenseclaw, except inert hook stubs. See Uninstall.

A pkg has no uninstaller, so the lifecycle removes the binaries itself. For a pkg install it also runs pkgutil --forget com.cisco.defenseclaw.enterprise, so the pkg receipt is gone afterward.

Upgrades and changes

  • Upgrade. Install the newer pkg, or run ensure with the newer tarball. An older pkg is refused before any file changes unless the root-owned rollback marker /opt/cisco/defenseclaw/lifecycle/allow-downgrade exists; the lifecycle refuses an older tarball unless you pass --allow-downgrade. See Upgrade and Roll back.
  • Change the config. Replace /opt/cisco/defenseclaw/etc/config.yaml. The apply daemon runs ensure, which validates the new file and applies it, or rolls back. See Change the config.
  • Failures. See Lifecycle error codes.

The service account

_defenseclaw is a hidden account and group. The lifecycle creates it with dscl, using the highest free id from 300 to 499 (the range macOS reserves for daemons), with shell /usr/bin/false and home /var/empty. The gateway runs as _defenseclaw. Everything else runs as root.

Privacy prompts

The packaging ships no PPPC (Privacy Preferences Policy Control) profile. The guardian works only in hidden dot-directories inside each user's home, such as ~/.claude, ~/.codex and ~/.config. macOS privacy controls (TCC) do not protect those folders, so no profile is needed.

The per-user AI Discovery scan also walks the home for package manifests. Without a profile, macOS keeps it out of TCC-protected folders, and it skips them.

If your organization moves agent configuration into a TCC-protected folder, such as Desktop, Documents or iCloud Drive, deploy a PPPC profile that grants Full Disk Access (SystemPolicyAllFiles) to /opt/cisco/defenseclaw/bin/defenseclaw-gateway. That one binary runs the gateway, the guardian (enterprise hooks watch) and the enumerator. Identify it by its Developer ID code requirement, which needs a signed build.

Machine policy

For Codex, Claude Code, Cursor, GitHub Copilot and OpenCode, DefenseClaw adds its hooks to the vendor's machine policy files:

ConnectorFile
Codex/etc/codex/requirements.toml
Claude Code/Library/Application Support/ClaudeCode/managed-settings.d/
Cursor/Library/Application Support/Cursor/hooks.json
GitHub Copilot/etc/github-copilot/policy.d/
OpenCode/Library/Application Support/opencode/opencode.json, which loads the managed plugin

An MDM configuration profile outranks these files: a Codex profile for com.openai.codex with requirements_toml_base64, or a Claude Code profile for com.anthropic.claudecode. enterprise policy show reports such a profile as a higher-precedence source. Export DefenseClaw's entries in plist form and add them to your profile:

sudo DEFENSECLAW_CONFIG=/opt/cisco/defenseclaw/etc/config.yaml \
  /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise policy show
sudo DEFENSECLAW_CONFIG=/opt/cisco/defenseclaw/etc/config.yaml \
  /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise policy export --connector codex --format plist

Use --connector claudecode --format plist for Claude Code. The policy commands only read. See Machine policy for ownership, conflicts and the foreign-hook guard.