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, Iru (formerly Kandji), Intune, Workspace ONE or another MDM, start with Install with an MDM. It uses the same pkg and the same config.
Requirements
| Requirement | Detail |
|---|---|
| macOS | macOS 13.0 or later. The pkg refuses older versions. |
| Hardware | Apple 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. |
| Privileges | root, through sudo or an MDM agent. Every enterprise macos action, status included, needs root: a standard user gets not_root. Per-user commands such as defenseclaw-gateway status and audit export answer that the computer is managed and name the administrator command. |
| Other profiles | No Secure Client DefenseClaw module. The pkg's pre-install script refuses when /opt/cisco/secureclient/defenseclaw or a com.cisco.secureclient.defenseclaw* LaunchDaemon exists. |
| Gatekeeper | Only 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. Releases currently ship it unsigned and hash-pinned by checksums.txt (see step 1 under Install). 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.
| Item | Path | Owner and mode |
|---|---|---|
| Binaries | bin: defenseclaw-gateway, defenseclaw-hook, defenseclaw-sensor-helper, and defenseclaw-acp when present | root, 0755 |
| Managed OpenCode plugin | share/opencode/defenseclaw.js | root, 0644 |
| Config | etc/config.yaml | root:_defenseclaw, 0640 |
| Policies | etc/policies | root:_defenseclaw, 0750 |
| Secrets | etc/secrets | Directory root:_defenseclaw 0750, files 0640 |
| Target manifest | etc/hook-guardian/targets.yaml, written by the enumerator | Directory root:_defenseclaw, 0750 |
| Runtime descriptor | etc/managed-runtime.json, the non-secret settings that hooks read | root, 0644 |
| Machine-policy summary | etc/machine-policy.json | Readable by users |
| Lifecycle state | lifecycle, including last-package-result.json, last-activation-failure.log, the last applied config and a rejected one | root, 0700 |
| Gateway data | runtime | _defenseclaw, 0700 (the device identity key needs a private directory) |
| Authorization ledger | hook-guardian-state, with each enrolled user's latest AI Discovery scan in ai-discovery/ | root:_defenseclaw, 0750 |
| Hook socket | run/hook.sock | Directory _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.*.plist | root, 0644 |
The daemons:
| Label | Runs as | What it does |
|---|---|---|
com.cisco.defenseclaw.gateway | _defenseclaw | Policy engine and hook API. Binds 127.0.0.1:18970 and run/hook.sock. |
com.cisco.defenseclaw.hook-guardian | root | Always 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-enumerator | root | Rewrites the target manifest every five minutes (in the default auto enrollment mode) |
com.cisco.defenseclaw.sensor-helper | root | Answers the gateway's fixed AI Discovery requests |
com.cisco.defenseclaw.apply | root | Runs ensure --reason path when etc/config.yaml, etc/secrets or etc/policies changes |
com.cisco.defenseclaw.verify | root | Runs verify every day at 03:17 local time |
How a hook reaches the gateway:
List view for small screens. Use the expand button to open the drawing.
TrustedZ0 · Admin and OS (root)
- launchdstarts the daemons
- starts as _defenseclawGatewayZ2
UntrustedZ4 · User session (the user)
- AI agentruns as the user
- runs per tool calldefenseclaw-hook
- defenseclaw-hookchecks the socket owner
- asks over hook.sockGatewayZ2
RestrictedZ2 · Gateway (_defenseclaw)
- Gatewayallows or blocks
Unlike systemd on Linux, launchd does not hold the sockets for the gateway.
launchd hands sockets over only through launch_activate_socket(3), which
needs cgo, and release builds are built without cgo. The protected socket
directory and the hook's owner check do the same job.
How the hooks stay in place:
List view for small screens. Use the expand button to open the drawing.
PrivilegedZ1 · Services (root)
- Enumeratorlists the users
- writesTarget manifestZ3
- Guardianchecks every minute
- readsTarget manifestZ3
- starts, as the userPer-user workerZ4
ProtectedZ3 · Admin-owned files
- Target manifesttargets.yaml
UntrustedZ4 · User session (the user)
- Per-user workerruns as the user
- repairs hooksAgent config
- Agent configin the user's home
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 tarballdefenseclaw-enterprise-<version>-darwin-arm64.tar.gz;checksums.txtandchecksums.txt.bundle.
Check the signature on checksums.txt with
cosign, then
the file against it, then the pkg's own signature:
VERSION=1.0.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"Releases currently ship the pkg unsigned: it is not Developer ID-signed or
notarized, and pkgutil reports "no signature". The release notes say so
when that is the case. An unsigned pkg is protected by its SHA-256 in
checksums.txt, whose cosign signature the first command checks, so run both
checks before every deployment and pin that SHA-256 in your MDM. An unsigned
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_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.
AI Discovery (each enrolled user's inventory scan in ai-discovery/, and the
skill, MCP and AI component records it feeds) is off unless the config sets
it. A config you write is read as written, so add this block to turn it on:
ai_discovery:
enabled: trueSee Per-user scans.
To read the inventory on the host, run the read-only discovery view as root:
sudo /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise macos discovery
sudo /opt/cisco/defenseclaw/bin/defenseclaw-gateway enterprise macos discovery --user <account> --jsonSkills and MCP servers are inventoried, not scanned: a managed deployment
does not run the skill or MCP scanners. The inventory names each skill and
MCP server per user, and its ai_component and per-entry skill and MCP
records go to your observability destinations. The gateway's skill, MCP and
tool-catalog API routes are not available on a managed host (they answer
501); use the discovery view above instead.
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.yamlThe lifecycle re-owns the file to root:_defenseclaw 0640 when it installs.
3a. Install the pkg
VERSION=1.0.0 # the release you deploy
sudo installer -pkg "defenseclaw-enterprise-${VERSION}-darwin-arm64.pkg" -target /
sudo cat /opt/cisco/defenseclaw/lifecycle/last-package-result.jsonThe 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, and /var/log/install.log
names its first error. Fix it, then install the pkg again (or let your MDM
redeploy it). The pkg receipt is com.cisco.defenseclaw.enterprise, and a
failed install records none. ensure --from-package also finishes the
install, but it records no receipt either, so receipt-based inventory
reports the Mac as not installed until the pkg installs successfully.
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" --json3b. 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.0.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" --jsonensure 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:
| Code | Meaning |
|---|---|
0 | Success, or nothing to do |
1 | Failure. 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. |
2 | Invalid arguments |
75 | Another 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:
enterprise:
profile: standalone
inspection:
ai_defense:
enabled: true
credential: ai-defense-api-keyThen store the key, before or after the install. After the install, run:
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.keyBefore the install, run the same command with the defenseclaw-gateway from
the extracted tarball ($PAYLOAD/defenseclaw-gateway, step 3b). The key
stays readable by root only, the command reports not_installed as a
warning and exits 0, and the install gives the gateway its access, so a
config that references the key installs at once.
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 onlystatus reports the state. verify also checks every file, permission,
service and readiness check, and exits 1 when anything fails. Look for:
| Field | Healthy value |
|---|---|
ok | true |
installed, installed_version | true and the version you deployed |
readiness | gateway, guardian, enumerator and sensor_helper all true |
coverage_complete | true: the gateway, guardian and enumerator are ready |
security_complete | true: coverage is complete, the sensor helper is ready, and there are no errors |
enrollment.targets | The 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. |
errors | Empty |
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 action | What happens |
|---|---|
install.sh | Refuses and changes nothing |
defenseclaw upgrade | Refuses |
| Per-user gateway | Refuses 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.
A per-user gateway that was already running keeps running until it is
stopped. The install, status and verify name each such account with the
per_user_gateway_running warning and the kill command that stops it.
Remove
| Goal | Command |
|---|---|
Stop and remove the deployment, its binaries, config, secrets, data, logs, lifecycle state and the _defenseclaw account. Each user keeps ~/.defenseclaw. | defenseclaw-gateway enterprise macos uninstall --json |
Also remove each enrolled user's ~/.defenseclaw and per-user binaries | defenseclaw-gateway enterprise macos uninstall --purge --json |
| Keep the config, secrets, data and logs (and the account) for a reinstall | defenseclaw-gateway enterprise macos uninstall --keep-state --json |
Keep only the _defenseclaw account | Add --keep-service-account |
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.
Each user keeps ~/.defenseclaw unless you purge; the result's kept: line
names the accounts that still have ~/.defenseclaw or per-user binaries in
~/.local/bin. With --purge it also
stops each enrolled user's per-user gateway and removes all of that user's
~/.defenseclaw, the empty agent folders DefenseClaw created in that home,
the per-user binaries and launcher links in ~/.local/bin, and the
DefenseClaw wheels earlier per-user installs left in ~/.cache/uv, and the
result names every user it purged or left alone.
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.
launchd keeps a record for each removed label. After uninstall,
launchctl print system lists the DefenseClaw labels as enabled, which is
launchd's default, and no job stays loaded. macOS also keeps any privacy (TCC)
decision about the gateway binary, such as a Full Disk Access entry, and
uninstall cannot remove it. Remove the entry in System Settings > Privacy &
Security > Full Disk Access. If an MDM PPPC profile granted the access, remove
that profile.
Upgrades and changes
- Upgrade. Install the newer pkg, or run
ensurewith the newer tarball. An older pkg is refused before any file changes unless the root-owned rollback marker/opt/cisco/defenseclaw/lifecycle/allow-downgradeexists; 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 runsensure, 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.
AI Discovery Plane C (Endpoint Security)
AI Discovery's runtime detection has three planes: A watches for
inference compute, B attributes network traffic to processes, and C records
agent actions from kernel events (see
Runtime planes).
Plane C (ai_discovery.runtime.enable_host_plane: true with c in
ai_discovery.runtime.planes) reads process and file events through Apple's eslogger. On a
managed Mac the root sensor helper,
/opt/cisco/defenseclaw/bin/defenseclaw-sensor-helper
(com.cisco.defenseclaw.sensor-helper), runs eslogger. launchd starts the
helper directly, so macOS treats the helper as the responsible process, and
the helper is the binary that needs Full Disk Access. Without it, Endpoint
Security refuses the client (ES_NEW_CLIENT_RESULT_ERR_NOT_PERMITTED), Plane C
reports running: false with that reason, and the runtime snapshot stays
degraded. Planes A and B keep working.
Grant the access with a PPPC payload (com.apple.TCC.configuration-profile-policy)
under SystemPolicyAllFiles:
<key>Services</key>
<dict>
<key>SystemPolicyAllFiles</key>
<array>
<dict>
<key>Identifier</key>
<string>/opt/cisco/defenseclaw/bin/defenseclaw-sensor-helper</string>
<key>IdentifierType</key>
<string>path</string>
<key>CodeRequirement</key>
<string>(the designated requirement printed by codesign -dr - on that path)</string>
<key>Allowed</key>
<true/>
</dict>
</array>
</dict>The code requirement needs a signed build. On an unsigned test build, grant
Full Disk Access to that path by hand in System Settings > Privacy & Security >
Full Disk Access. Then restart the helper with
sudo launchctl kickstart -k system/com.cisco.defenseclaw.sensor-helper.
Machine policy
For Codex, Claude Code, Cursor, GitHub Copilot and OpenCode, DefenseClaw adds
its hooks to the vendor's machine policy files under /etc and
/Library/Application Support. The files are listed in
Machine policy files.
An MDM configuration profile for com.openai.codex or
com.anthropic.claudecode outranks those files; export DefenseClaw's
entries in plist form and add them to your profile, as
Higher-precedence sources
and Inspect, export and verify
show.
Linux standalone deployment
Install the standalone DefenseClaw enterprise services on Linux from the deb or rpm package or the payload tarball, check the result, and remove them.
Install with an MDM
The contract every MDM recipe follows to deliver, run, detect, upgrade, reconfigure and remove the standalone DefenseClaw enterprise deployment on Windows, Linux and macOS.