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.
On Linux, DefenseClaw runs machine-wide in the standalone profile only. The lifecycle installs a fixed set of systemd units. systemd (PID 1) owns the gateway's sockets, so no user process can take them over. Standard users cannot stop the services 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 or configuration management?
This page installs on one computer from a root shell. To deploy with Intune, Ansible, Puppet, Chef, Salt or another tool, start with Install with an MDM. It uses the same packages and the same config.
Requirements
| Requirement | Detail |
|---|---|
| systemd | systemd 239 or later running as PID 1. The lifecycle refuses containers and WSL without systemd. Check with systemctl --version. |
| Architecture | x86_64 (amd64) or arm64. Each release has a package and a tarball for both. |
| Package format | .deb (Debian, Ubuntu) or .rpm (RHEL and derivatives). Hosts without a package manager use the payload tarball. |
| SELinux | Supported. After each change the lifecycle runs restorecon -R on /opt/defenseclaw and /etc/defenseclaw, and reports a selinux_relabel warning if that fails. |
| Privileges | root. Only enterprise linux status works without root. |
| Other profiles | No Secure Client DefenseClaw layout at /opt/cisco/secureclient/defenseclaw. The lifecycle refuses with profile_conflict. |
The systemd version decides how the gateway reads secrets:
| systemd | Secrets |
|---|---|
| 247 or later | /etc/defenseclaw/secrets is root:root 0700, and each file is 0600. The gateway receives them through LoadCredential=. |
| 239 to 246 | /etc/defenseclaw/secrets is root:defenseclaw 0750, and each file is 0640, readable by the gateway's group |
It also decides how much of the units' sandboxing applies. systemd ignores
directives it does not know, so hosts older than systemd 250 run the units
without some of them; RHEL 8 (systemd 239) runs without ProtectHostname,
ProtectKernelLogs, ProtectClock, ProtectProc and ProcSubset. The
Linux threat model
lists each directive and the version it needs.
Layout
| Item | Path | Owner and mode |
|---|---|---|
| Binaries | /opt/defenseclaw/bin: defenseclaw-gateway, defenseclaw-hook, defenseclaw-sensor-helper, and defenseclaw-acp when present | root, 0755 |
| Managed OpenCode plugin | /opt/defenseclaw/share/opencode/defenseclaw.js | root, 0644 |
| Config | /etc/defenseclaw/config.yaml | root:defenseclaw, 0640 |
| Policies | /etc/defenseclaw/policies | root:defenseclaw, 0750 |
| Secrets | /etc/defenseclaw/secrets | See the table above |
| Target manifest | /etc/defenseclaw/hook-guardian/targets.yaml, written by the enumerator | Directory root:defenseclaw, 0750 |
| Runtime descriptor | /etc/defenseclaw/managed-runtime.json, the non-secret settings that hooks read | root, 0644 |
| Machine-policy summary | /etc/defenseclaw/machine-policy.json | Readable by users |
| Lifecycle state | /var/lib/defenseclaw-enterprise: lock, snapshots, deployment record, last-package-result.json, last-activation-failure.log, the last applied config and a rejected one | root, 0700 |
| Transaction snapshots on other filesystems | /opt/defenseclaw/.lifecycle-snapshots, /etc/defenseclaw/.lifecycle-snapshots: only during a lifecycle transaction (or while a rollback waits for a retry) when /var is on its own filesystem. They hold the snapshot's hard links to the files there. | root, 0700 |
| Gateway data | /var/lib/defenseclaw | defenseclaw, 0700 (the device identity key needs a private directory) |
| Authorization ledger | /var/lib/defenseclaw-hook-guardian, with each enrolled user's latest AI Discovery scan in ai-discovery/ | root:defenseclaw, 0750 |
| Logs | /var/log/defenseclaw: empty unless a jsonl destination writes there; the services log to the journal (Logs and events) | defenseclaw, 0750 |
| Hook socket | /run/defenseclaw-hook/hook.sock | Directory defenseclaw, 0755. Socket 0666: every user may connect, and the gateway authorizes each caller by uid. |
The gateway runs as the defenseclaw system account. The lifecycle creates
it with systemd-sysusers, or useradd as a fallback, and reuses an
existing account.
The units:
| Unit | Runs as | What it does |
|---|---|---|
defenseclaw-gateway.service | defenseclaw, no capabilities | Policy engine and hook API. Type=notify with a 60 s watchdog and Restart=always. Config, binaries and ledger are read-only to it. |
defenseclaw-gateway-api.socket | PID 1 | Listens on 127.0.0.1:18970 |
defenseclaw-gateway-hook.socket | PID 1 | Listens on /run/defenseclaw-hook/hook.sock |
defenseclaw-hook-guardian.service | root, bounded capabilities | 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. |
defenseclaw-hook-guardian-reconcile.service | root | One immediate reconcile, on demand |
defenseclaw-hook-enumerator.service | root, homes read-only | Rewrites the target manifest every five minutes (in the default auto enrollment mode) |
defenseclaw-sensor-helper.service | root, acquisition capabilities | Answers the gateway's fixed AI Discovery requests |
defenseclaw-enterprise-apply.path | PID 1 | Runs ensure --reason path when /etc/defenseclaw/config.yaml, /etc/defenseclaw/secrets or /etc/defenseclaw/policies changes |
defenseclaw-enterprise-verify.timer | PID 1 | Runs verify daily, with up to one hour of random delay |
The package installs the units in /usr/lib/systemd/system. A tarball install
writes them to /etc/systemd/system. Either way, host-specific drop-ins go
in /etc/systemd/system/<unit>.d/.
See Threat model for what each zone may do.
Install
Stage the config first, then install the package or the tarball. The
lifecycle picks its config in this order: --config, then
/etc/defenseclaw/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:
- the package,
defenseclaw-enterprise-<version>-linux-<arch>.debor.rpm, or the tarball,defenseclaw-enterprise-<version>-linux-<arch>.tar.gz; checksums.txtandchecksums.txt.bundle.
Check the signature on checksums.txt with
cosign, then
the files against it:
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
sha256sum --ignore-missing -c checksums.txtWhen the release pipeline has a GPG key, each Linux package and tarball
also has a detached signature (<file>.asc), and the release includes the
public key, defenseclaw-enterprise-release-key.asc.
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: /var/lib/defenseclaw. 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: /var/lib/defenseclaw
policy_dir: /etc/defenseclaw/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 root -m 0755 /etc/defenseclaw
sudo install -o root -g root -m 0640 config.yaml /etc/defenseclaw/config.yamlThe lifecycle re-owns the file to root:defenseclaw 0640 when it installs.
3a. Install the package
VERSION=1.4.0 # the release you deploy
ARCH=amd64 # or arm64
sudo apt install "./defenseclaw-enterprise-${VERSION}-linux-${ARCH}.deb"
# RHEL and derivatives:
# sudo dnf install "./defenseclaw-enterprise-${VERSION}-linux-${ARCH}.rpm"
sudo cat /var/lib/defenseclaw-enterprise/last-package-result.jsonThe package's post-install script runs
enterprise linux ensure --from-package --reason package --json. That creates
the service account, validates the config, lays out the files, starts the
units in order, verifies, and rolls back on any failure. The script never
fails the package transaction, so the package manager reports success even
when the lifecycle did not apply. Always check the result: "ok": true in
last-package-result.json, or detect.sh --require-healthy from the MDM kit.
3b. Or install from the tarball
Use the tarball on hosts without dpkg or rpm. 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
ARCH=amd64 # or arm64
PAYLOAD="/root/defenseclaw-enterprise-${VERSION}"
sudo install -d -o root -g root -m 0700 "$PAYLOAD"
sudo tar -xzf "defenseclaw-enterprise-${VERSION}-linux-${ARCH}.tar.gz" \
-C "$PAYLOAD" --no-same-owner --no-same-permissions
sudo chmod -R go-w "$PAYLOAD"
sudo "$PAYLOAD/defenseclaw-gateway" enterprise linux 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 services stopped.
A host installed from the package refuses a tarball upgrade
(package_owned_binaries). Upgrade it with the package manager.
If the host already has a hand-built DefenseClaw layout, such as older
units or a gateway binary that no deployment record owns, the install stops
with unmanaged_layout_present. Add --adopt-existing to back up that
layout to /var/lib/defenseclaw-enterprise 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. The deployment must be installed first, because the
command needs the service account and runs ensure right after it writes
the key:
sudo /opt/defenseclaw/bin/defenseclaw-gateway enterprise secret set \
--name ai-defense-api-key --from-file /root/ai-defense.key --json
sudo rm /root/ai-defense.keySee AI Defense key for rotation and removal.
Users who are enrolled
The enumerator finds accounts through NSS, so directory users (LDAP, SSSD, Active Directory) count as well as local ones. It skips:
- accounts below
UID_MINfrom/etc/login.defs(1000 when the file does not set it), and local accounts aboveUID_MAX(60000 when unset).enterprise.enrollment.uid_minchanges the minimum, andenterprise.enrollment.uid_maxsets a maximum for local and directory accounts alike. Withoutuid_max, directory users with id-mapped uids aboveUID_MAX, common with SSSD and Active Directory, are enrolled; - accounts with a nologin shell, unless listed in
include_users; - accounts whose home is outside
/homeand/var/home. Add roots withenterprise.enrollment.home_roots;include_usersdoes not override this; - accounts in
exclude_usersorexempt_users, and accounts that theinclude_groupsandexclude_groupsfilters leave out; root, uid 65534 andnobody, always.
See Enrollment for the filters and for how removed users are handled.
Verify the install
sudo /opt/defenseclaw/bin/defenseclaw-gateway enterprise linux status --json
sudo /opt/defenseclaw/bin/defenseclaw-gateway enterprise linux verify --json
systemctl list-timers defenseclaw-enterprise-verify.timerstatus 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. For per-user connectors, a newly added
user shows up after the next enumerator cycle (up to five minutes), then the
guardian reconciles.
The guardian is always running, so systemctl start on it does nothing. To
reconcile at once, run:
sudo /opt/defenseclaw/bin/defenseclaw-gateway enterprise linux reconcile --jsonSee Status and verify and Health fields for monitoring.
User namespaces
On most distributions as shipped, Ubuntu 24.04 and RHEL 9 included,
verify reports ok: true with one warning, unprivileged_user_namespaces.
Where standard users can create a private user and mount namespace, an
agent started inside one can be given its own view of
/etc/codex/requirements.toml and the other machine-policy files, the
runtime descriptor and /run/defenseclaw-hook, outside your enforcement.
DefenseClaw cannot turn this off from user space
(Residual risks). The
warning names the host setting that closes it:
| Host | Setting |
|---|---|
Ubuntu 24.04 and other AppArmor hosts where kernel.apparmor_restrict_unprivileged_userns is 1 | kernel.apparmor_restrict_unprivileged_unconfined=1, so an unconfined process cannot switch to an AppArmor profile that allows user namespaces (for example with aa-exec) |
| Any host | user.max_user_namespaces=0, which turns user namespaces off for everyone but root |
# Ubuntu 24.04: keep the setting across reboots, then load it.
echo 'kernel.apparmor_restrict_unprivileged_unconfined = 1' |
sudo tee /etc/sysctl.d/60-defenseclaw-userns.conf
sudo sysctl --systemWeigh the trade-off first. The command sandboxes of Codex and Claude Code use bubblewrap, which needs user namespaces:
- With
user.max_user_namespaces=0those sandboxes cannot start, and neither can other software that uses user namespaces, such as rootless containers. - Under the AppArmor restriction a sandbox runs only where an AppArmor
profile allows user namespaces. On stock Ubuntu 24.04, where
kernel.apparmor_restrict_unprivileged_userns=1is already the default, Codex's bundled bubblewrap already fails (bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted), so its shell calls fail until the user chooses--sandbox danger-full-accessor approves running outside the sandbox. Settingkernel.apparmor_restrict_unprivileged_unconfined=1as well does not change that.
DefenseClaw's hooks inspect each tool call whether or not the agent's own sandbox runs.
Per-user installs
On a computer with /etc/defenseclaw/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.
Remove
| Goal | Command |
|---|---|
| Stop and remove the deployment. Keep the config, secrets, data and logs. | defenseclaw-gateway enterprise linux uninstall --json |
| Also remove the config, secrets, data, logs and lifecycle state | defenseclaw-gateway enterprise linux uninstall --purge --json |
Also delete the defenseclaw account | defenseclaw-gateway enterprise linux uninstall --purge --remove-service-account --json |
Run these as root from /opt/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.
On a host installed from the package, uninstall leaves the package's
binaries and units in place, so dpkg or rpm still lists
defenseclaw-enterprise. Removing the package runs uninstall for you:
| Package command | Effect |
|---|---|
apt remove defenseclaw-enterprise | Runs uninstall, then removes the package files. The config and state stay. |
apt purge defenseclaw-enterprise | Also deletes /etc/defenseclaw, /var/lib/defenseclaw, /var/lib/defenseclaw-hook-guardian, /var/lib/defenseclaw-enterprise and /var/log/defenseclaw. The defenseclaw account stays. |
dnf remove defenseclaw-enterprise | Runs uninstall, then removes the package files. RPM has no purge, so the config and state stay. |
To remove everything from a package host, purge with the lifecycle first, while the binary still exists, then remove the package:
sudo /opt/defenseclaw/bin/defenseclaw-gateway enterprise linux uninstall \
--purge --remove-service-account --json
sudo apt purge defenseclaw-enterprise
# RHEL and derivatives:
# sudo dnf remove defenseclaw-enterprise
# sudo rm -rf /var/lib/defenseclaw-enterpriseOn RHEL, the package's removal script writes its result to
/var/lib/defenseclaw-enterprise again, and nothing removes it, so delete
that folder last. On Debian and Ubuntu, apt purge removes it.
On a tarball host, uninstall --purge --remove-service-account removes
everything, including /opt/defenseclaw.
Upgrades and changes
- Upgrade. Install the newer package with apt or dnf, or run
ensurewith the newer tarball. The lifecycle refuses an older release unless you pass--allow-downgrade, andrpm -Urefuses an older package. See Upgrade and Roll back. - Change the config. Replace
/etc/defenseclaw/config.yaml. The apply path unit runsensure, which validates the new file and applies it, or rolls back. See Change the config. - Failures. See Lifecycle error codes.
Windows standalone deployment
Install the standalone DefenseClaw enterprise services on Windows x64 with the standalone Setup or the lifecycle CLI, check the result, and remove them.
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.