Install with any 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.
Any MDM, configuration-management tool or administrator shell that can run a program as SYSTEM (Windows) or root (Linux, macOS) can deploy the standalone profile. This page is the contract that every recipe in this section follows: what you deliver, how you run it, how you read the result, how you detect it, and how you upgrade, roll back, reconfigure and remove it. Terms such as standalone profile, lifecycle and ensure are defined in Concepts.
The MDM kit lives in packaging/mdm
in the repository. It holds three kinds of script per OS:
| Script | Windows | Linux and macOS |
|---|---|---|
| Wrapper: verify, install and apply | windows/Invoke-DefenseClawEnterprise.ps1 (PowerShell 7) | linux/defenseclaw-enterprise.sh, macos/defenseclaw-enterprise.sh (POSIX sh) |
| Detection | windows/detect.ps1 (Windows PowerShell 5.1 or PowerShell 7, 32- or 64-bit) | linux/detect.sh, macos/detect.sh |
| Removal | windows/uninstall.ps1 (Windows PowerShell 5.1 or PowerShell 7) | linux/uninstall.sh, macos/uninstall.sh |
The Linux and macOS copies differ only in the line DC_SCRIPT_OS, and each
refuses to run on the other OS. Microsoft Intune has its own launcher and
Remediations scripts under intune/windows; see
Intune on Windows.
What you deliver
Every deployment needs three things: the release artifact for the OS, the administrator config, and, optionally, the Cisco AI Defense API key.
| OS | Artifact (release asset) | Trust anchor |
|---|---|---|
| Windows x64 | DefenseClawSetup-Enterprise-Standalone-x64.exe | Its SHA-256 from checksums.txt. When the release Setup is Authenticode-signed, also the signer certificate's SHA-256 thumbprint. |
| Linux | defenseclaw-enterprise-<version>-linux-<arch>.deb or .rpm, or the payload archive defenseclaw-enterprise-<version>-linux-<arch>.tar.gz | Its SHA-256 from checksums.txt. When the release ships GPG signatures, also the detached <file>.asc and the key defenseclaw-enterprise-release-key.asc. |
| macOS (Apple silicon) | defenseclaw-enterprise-<version>-darwin-arm64.pkg, or the payload archive | Its SHA-256 from checksums.txt. When the release pkg is signed and notarized, also the Developer ID Team ID. |
<arch> is amd64 or arm64. On Windows, use the -Standalone- Setup:
DefenseClawSetup-Enterprise-x64.exe is the Cisco Secure Client build. The
release also publishes
DefenseClawSetup-Enterprise-Standalone-x64.payload-manifest.json, a record of
the version, source commit and per-file SHA-256 of what the Setup embeds. You
do not deliver it: the Setup carries the same manifest and writes its own
trust anchor at run time.
Hash-pinned trust is the default for every wrapper and works for every
release, signed or not. The pin is the artifact's SHA-256 from the release's
checksums.txt, and checksums.txt is signed with cosign on every release.
Verify it once on an administrator workstation that has cosign and
sha256sum, then read the pin:
VERSION=1.4.0
FILE="defenseclaw-enterprise-$VERSION-linux-amd64.deb"
BASE="https://github.com/cisco-ai-defense/defenseclaw/releases/download/$VERSION"
curl -fsSLO "$BASE/checksums.txt"
curl -fsSLO "$BASE/checksums.txt.bundle"
curl -fsSLO "$BASE/$FILE"
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 --check --ignore-missing checksums.txt
PIN=$(awk -v f="$FILE" '$2 == f {print $1}' checksums.txt)
echo "$PIN"Replace FILE with the artifact you deploy. Signature-based trust is optional
and is covered in the kit's signing/README.md.
Deliver the config
The config decides which agents are protected and how. Its content is described in Configuration; start with Choose the agents to protect. The lifecycle installs it at a fixed path:
| OS | Installed config |
|---|---|
| Windows | C:\ProgramData\Cisco\DefenseClaw\etc\config.yaml |
| Linux | /etc/defenseclaw/config.yaml |
| macOS | /opt/cisco/defenseclaw/etc/config.yaml |
A first install on Windows needs a config: ensure without one exits 1639.
On Linux and macOS, an install without a config gets a built-in default that
runs the local engine in observe mode and protects no agents.
Pass the config to the wrapper in one of these ways:
| Method | Linux and macOS wrapper | Windows wrapper | Windows Setup |
|---|---|---|---|
| From a file | --config-file /abs/path | -ConfigPath C:\abs\path | CONFIG=C:\abs\path |
| From standard input | --config-stdin | -ConfigFromStdin | — |
| In the script itself | Settings block DC_CONFIG_FILE, or YAML between the DEFENSECLAW_CONFIG markers in dc_inline_config | — | — |
Rules that apply to every method:
- Only administrators may be able to change the file. On Linux and macOS
the file and every parent directory must be owned by root and not writable
by group or others (a sticky parent such as
/tmpis accepted). On Windows the file must be owned by SYSTEM, Administrators or TrustedInstaller and grant no one else write, delete or permission rights. The wrappers refuse anything else withmdm_untrusted_input; Setup run directly refuses it with its own error and exit1603. - Absolute local paths only. Setup also refuses relative paths, paths with
environment variables such as
%TEMP%, and network paths (the standalone Setup exits1639). - At most 1 MiB (1,048,576 bytes). The wrappers refuse a larger config
with
mdm_input_too_large. - No credentials in the config. The standalone profile rejects an inline
cisco_ai_defense.api_key. MDM script bodies are not secret storage either, so keep the key out of the settings block too. - Only one of the config and the key can come from standard input in the same run.
Deliver the AI Defense key
The Cisco AI Defense API key is optional. Without it, the local policy engine
decides alone. To use it, the config names the credential
(enterprise.inspection.ai_defense.credential) and turns the feature on;
see Cisco AI Defense key.
Store the key after the deployment is installed. The key never goes on a command line, in an MDM script body, or in the config.
| OS | In the same wrapper run | Directly |
|---|---|---|
| Linux, macOS | --secret-name ai-defense-api-key with --secret-file /root-only/file or --secret-stdin | enterprise secret set --name ai-defense-api-key --from-stdin, as root |
| Windows | -SecretName ai-defense-api-key with -SecretPath C:\admin-only\file or -SecretFromStdin | The installed CLI's enterprise secret set --name ai-defense-api-key --from-stdin, elevated |
The wrapper stores the key only after ensure succeeds. If storing fails, the
deployment stays applied and the run fails with mdm_secret_failed. The
wrapper deletes its own staging copy of the key (on Windows it overwrites it
first), but not your source file: delete that yourself.
Key names are 1 to 63 lowercase letters, digits and dashes, starting with a letter or digit. The value is a single line of at most 16 KiB.
On Linux and macOS, from an administrator shell:
sudo /opt/defenseclaw/bin/defenseclaw-gateway enterprise secret set \
--name ai-defense-api-key --from-file /root/ai-defense.key
sudo /opt/defenseclaw/bin/defenseclaw-gateway enterprise secret statusOn macOS the binary is /opt/cisco/defenseclaw/bin/defenseclaw-gateway.
Storing the key re-runs ensure, which applies it. On Windows the command
restarts the gateway service; see Intune on Windows
for an example that never writes the key to disk.
Run ensure
ensure is the one action an MDM runs. It installs when nothing is installed,
upgrades when the artifact's version differs, repairs when verify finds
drift, and applies a changed config (and, on Linux and macOS, a changed key). When the host already matches, it
changes nothing and reports "noop": true. So you can run it on every
check-in.
On Linux, with a package:
sudo ./defenseclaw-enterprise.sh \
--source /var/cache/mdm/defenseclaw-enterprise-1.4.0-linux-amd64.deb \
--sha256 "$PIN" \
--config-file /etc/mdm/defenseclaw/config.yamlOn macOS, the same wrapper with the pkg (--source also accepts the payload
archive, and --source-url https://... downloads it):
sudo ./defenseclaw-enterprise.sh \
--source /Library/Caches/mdm/defenseclaw-enterprise-1.4.0-darwin-arm64.pkg \
--sha256 "$PIN" \
--config-file /Library/Management/defenseclaw/config.yamlOn Windows, in PowerShell 7 as SYSTEM. The staging folder must be writable
only by SYSTEM and Administrators, or the wrapper and Setup refuse the config.
On Windows client editions a new folder under C:\ inherits write access for
authenticated users, so restrict it first:
icacls C:\Staging /inheritance:r /grant:r "*S-1-5-18:(OI)(CI)F" "*S-1-5-32-544:(OI)(CI)F"
$pin = '<SHA-256 of the Setup from checksums.txt>'
& 'C:\Program Files\PowerShell\7\pwsh.exe' -NoProfile -NonInteractive -File .\Invoke-DefenseClawEnterprise.ps1 `
-SetupPath C:\Staging\DefenseClawSetup-Enterprise-Standalone-x64.exe `
-Sha256 $pin `
-ConfigPath C:\Staging\config.yamlIf your MDM can run a native executable as SYSTEM but not PowerShell 7, run Setup directly. Setup has no pin of its own, so verify the file first:
C:\Staging\DefenseClawSetup-Enterprise-Standalone-x64.exe /ensure CONFIG=C:\Staging\config.yaml JSON=1Wrapper options
| Linux and macOS | Windows | Meaning |
|---|---|---|
--action ensure|status|verify | -Action Ensure|Status|Verify | Default ensure. status and verify are read-only and take no config or key. The Linux and macOS wrapper also refuses a source with them; the Windows wrapper accepts -SetupPath and runs Setup /status or /verify. Use the uninstall script to remove. |
--source FILE or --source-url https://... | -SetupPath FILE | The artifact. Without it, the wrapper re-applies the installed deployment. |
--sha256 HEX | -Sha256 HEX | The pin. Required with hash-pinned trust. |
--trust-mode hash_pinned|signed | -TrustMode HashPinned|Authenticode | Default hash-pinned. |
--allowed-team-id ID (macOS), --gpg-keyring FILE with --signature FILE or --signature-url URL (Linux) | -AllowedSigners THUMBPRINT[,...] | Signer pins for signed trust. |
--config-file, --config-stdin | -ConfigPath, -ConfigFromStdin | The config. |
--secret-name with --secret-file or --secret-stdin | -SecretName with -SecretPath or -SecretFromStdin | The optional key. |
--product-version X.Y.Z | -ProductVersion X.Y.Z | Linux and macOS: a deb, rpm or pkg of another version is refused with mdm_version_mismatch before the package manager runs, so nothing is installed; the lifecycle refuses a payload archive of another version before applying it. Windows: applies only without -SetupPath: it sets the version the installed CLI's ensure records and compares with the installed one, and does not filter an artifact. With -SetupPath the wrapper refuses it (mdm_invalid_arguments, 1639); a staged Setup carries its own version and is pinned to one release with -Sha256. |
--https-proxy URL | — | Proxy for the download only. The services' own proxy is set in the config (Network proxy). |
--log FILE | — | Wrapper log path. |
Script-only MDMs that cannot pass arguments set the same values in the
settings block at the top of defenseclaw-enterprise.sh (DC_SOURCE,
DC_SOURCE_URL, DC_SOURCE_SHA256, DC_TRUST_MODE, DC_ALLOWED_TEAM_IDS,
DC_GPG_KEYRING, DC_SIGNATURE, DC_SIGNATURE_URL, DC_PRODUCT_VERSION,
DC_CONFIG_FILE, DC_SECRET_NAME, DC_SECRET_FILE, DC_HTTPS_PROXY,
DC_LOG). Flags override the settings block. The wrapper refuses any argument
it does not know, including positional arguments, with exit 2
(mdm_invalid_arguments). If your MDM appends its own arguments, follow its
recipe in this section.
For Linux signed trust, gpgv needs a binary keyring. Convert the release key
once and deploy the result as a root-owned file:
gpg --dearmor < defenseclaw-enterprise-release-key.asc > defenseclaw-enterprise-release-key.gpgExecution requirements
- SYSTEM or root. On Windows an elevated administrator session also works.
The wrappers refuse anything else (
mdm_not_root,mdm_not_elevated), and so does Setup. - No terminal and no inherited environment. The Linux and macOS scripts
set their own
PATHand locale and work underenv -iwith no TTY. They read standard input only for--config-stdinor--secret-stdin. - Windows: native x64 only. The lifecycle refuses ARM64 and 32-bit
Windows. The wrapper also refuses a 32-bit or emulated PowerShell,
Constrained Language Mode, and .NET loader-injection variables such as
DOTNET_STARTUP_HOOKS. A 32-bit agent that starts Windows PowerShell must use%SystemRoot%\Sysnative\WindowsPowerShell\v1.0\powershell.exeto get the 64-bit engine. - Windows: PowerShell 7, installed from Microsoft's x64 MSI into
C:\Program Files\PowerShell\7. The lifecycle needs a stable (non-preview) x64 PowerShell 7 that the MSI registered, and refuses anything else. The kit's PowerShell 7 scripts (Invoke-DefenseClawEnterprise.ps1andNew-DefenseClawIntunePackage.ps1) need 7.4 or later. Setup,detect.ps1,uninstall.ps1and the Intune scripts do not need PowerShell 7 to start: the lifecycle they call finds and verifies it. - Time. Setup gives the lifecycle 30 minutes by default
(
TIMEOUTSECONDS=60to7200). Allow at least that long in your MDM. - One run at a time. A second run while one holds the lifecycle lock, or
while dpkg, rpm or the macOS installer is busy, exits
1618(Windows) or75(Linux, macOS). Retry it later. - Leftovers of an unmanaged install (Linux, macOS). If DefenseClaw machine
files exist that no committed deployment owns,
ensurerefuses withunmanaged_layout_present. Take them over once by runningenterprise linux ensureorenterprise macos ensureas root with--adopt-existingand your channel flag (--from-package, or--payload <dir>for an extracted archive). It backs the old files up first. The wrapper does not pass--adopt-existing. See Troubleshooting.
Setup properties
DefenseClawSetup-Enterprise-Standalone-x64.exe takes one action and
NAME=value properties. Names are case-insensitive and ignore dashes, so
TimeoutSeconds= and TIMEOUT-SECONDS= both work. /quiet and /norestart
are accepted and ignored.
| Action or property | Meaning |
|---|---|
/ensure | Install, upgrade, repair or no-op. Use this from an MDM. |
/install, /upgrade, /repair, /reconcile, /status, /verify, /uninstall | The individual actions. /install also needs MANIFEST=, so use /ensure instead. |
CONFIG= | Absolute path of the config. Required for the first install. |
MANIFEST= | Optional administrator target manifest (targets.yaml). Most deployments omit it; a first install with enterprise.enrollment.mode: manifest needs it. |
ALLOWEDSIGNERS= | Comma-separated SHA-256 thumbprints of accepted Authenticode signers, for a signed Setup. |
JSON=1 | Print the result document on standard output. Always set it from an MDM. |
NOSTART=1 | Install with the services stopped (mutating actions only). |
PURGE=1 | With /uninstall, also remove the config, credentials and state. |
TIMEOUTSECONDS= | Lifecycle timeout, 60 to 7200 seconds. Default 1800. |
ATTESTCLAUDEEFFECTIVEPOLICY=1 | With /repair: record the administrator's attestation that Claude Code runs DefenseClaw's managed hooks. See Claude Code on Windows. |
Exit codes
The wrappers, the removal scripts and every lifecycle action print exactly one
result document on standard output and exit with a code from one of two
families. (The lifecycle prints the document when it runs with --json or
JSON=1; the kit's scripts always ask for it. The detection and Remediations
scripts print one line instead.) The document's exit_code field matches the
process exit code. Its format is published as
packaging/mdm/contract/lifecycle-result.schema.json
(schema version 2).
| Result | Windows | Linux, macOS | MDM action |
|---|---|---|---|
Success, or nothing to do ("noop": true) | 0 | 0 | Success |
| Failed. A mutating action has already rolled back. | 1603 | 1 | Failure; read errors[].code |
| Another run holds the lock, or the package manager is busy | 1618 | 75 | Retry later |
| Invalid arguments, for example a first Windows install without a config. Retrying does not help. | 1639 | 2 | Failure; fix the assignment |
| Reboot required (reserved; nothing returns it today) | 3010 | — | Soft reboot |
An invalid config is a plain failure: on Linux and macOS it exits 1 with
config_invalid, and on Windows it exits 1603. The installed config stays
as it was. Read errors[].code and errors[].message.
The Windows codes are Windows Installer codes, so MDMs classify them with
their default tables. Intune's default Win32 return codes (0, 1707, 3010,
1641, 1618) already treat 1603 and 1639 as failures and retry 1618.
The standalone Setup can also refuse before the lifecycle starts. A
malformed command line exits 1639: an unknown property, a CONFIG= or
MANIFEST= path that is missing, relative or environment-expanded,
/install without CONFIG= and MANIFEST=, or TIMEOUTSECONDS out of
range. A token that is not elevated, and a CONFIG= or MANIFEST= file that
a non-administrator can change, exit 1603. The Secure Client Setup returns
only 0 and 1603.
With JSON=1, Setup then prints a short document with schema_version: 1 and
an error field. Setup stops reading its arguments at the first unknown one,
so put JSON=1 before the other properties. The wrappers turn it into a schema-version-2 document with
the code mdm_lifecycle_no_result.
Error codes that start with mdm_ come from the kit's scripts. Most mean the
wrapper stopped before the lifecycle ran; mdm_secret_failed, and
mdm_not_installed after an apply, are reported after the lifecycle
succeeded. In every mdm_ document, "installed": false means "not
evaluated". The Windows wrapper's parameters are checked by PowerShell itself:
an unknown parameter, or starting the wrapper in Windows PowerShell 5.1, stops
it with exit 1 before it prints a result document. Run the status action to inspect the host. Every other code
comes from the lifecycle; see Troubleshooting.
| Code | Exit (Windows / Linux, macOS) | Meaning |
|---|---|---|
mdm_invalid_arguments | 1639 / 2 | A flag or setting is missing, malformed, contradictory or unknown. |
mdm_wrong_platform | — / 2 | The Linux copy ran on macOS, or the reverse. |
mdm_not_root, mdm_not_elevated | 1603 / 1 | Not running as root, SYSTEM or an elevated administrator. |
mdm_hash_mismatch | 1603 / 1 | The staged artifact does not match the pinned SHA-256. |
mdm_signature_invalid, mdm_signer_not_allowed, mdm_signature_unsupported | 1603 / 1 | The signature is invalid, the signer is not allowed, or signed trust does not apply to this artifact type. |
mdm_untrusted_input | 1603 / 1 | A config, key or keyring file can be changed by a non-administrator, or (uninstall.sh) the installed gateway is not root-owned. |
mdm_untrusted_install | 1603 / — | uninstall.ps1 only: the installed CLI or one of its folders is not administrator-only. The wrapper reports the same condition as mdm_lifecycle_launch_failed, and detect.ps1 as not detected. |
mdm_input_too_large | 1639 / 2 | Config over 1 MiB or key over 16 KiB. |
mdm_payload_invalid, mdm_package_invalid, mdm_wrong_package, mdm_wrong_architecture, mdm_version_mismatch | — / 1 | The archive or package is not a DefenseClaw enterprise artifact for this host, or not the version you pinned. |
mdm_package_manager_busy | — / 75 | dpkg, rpm or the macOS installer held its lock. Retry. |
mdm_package_install_failed, mdm_package_remove_failed, mdm_package_manager_missing | — / 1 | The package manager failed or is absent. |
mdm_download_failed, mdm_download_unavailable | — / 1 | The HTTPS download failed, or neither curl nor wget is installed. |
mdm_not_installed | 1603 / 1 | A read-only action, or ensure without an artifact, found no installed deployment. |
mdm_lifecycle_no_result, mdm_lifecycle_launch_failed | Setup's code or 1603 / the lifecycle's code or 1 | The lifecycle did not start or printed no result document. |
mdm_secret_failed | 1603 / 1, 2 or 75 | The deployment applied, but storing the key failed. On Linux and macOS the exit code is the one secret set returned. |
mdm_staging_untrusted, mdm_package_incomplete | 1603 / 1 | The private staging folder, or the Intune Win32 app content (Windows), is not as expected. |
unsupported_architecture, powershell_constrained_language, loader_environment_present, powershell7_untrusted | 1603 / — | The PowerShell 7 wrapper refused its host. The lifecycle applies the same checks. |
Detection
MDMs ask two different questions. Answer each with the mechanism that fits.
- Inventory: is the version I assigned installed? Install rules, supersedence and upgrade detection need a cheap, stable answer. Use the registry marker, the package database or the pkg receipt.
- Health: is it installed, intact and enforcing? Compliance scripts and
remediations need the lifecycle's own verdict. Use the detection script with
its health option, or the
verifyaction.
The Windows marker is written only after a successful lifecycle run. The
Linux package database records the package even when its post-install
ensure failed, so use detect.sh or verify for the real state. Standard
users cannot write any of the markers. Only verify proves that the services, protected
files, hooks and machine policy are intact.
| OS | Inventory | Health |
|---|---|---|
| Windows | Registry value HKLM\SOFTWARE\Cisco\DefenseClaw\Enterprise ProductVersion in the 64-bit view, compared as a version, greater than or equal to the assigned version. Or detect.ps1 -MinimumVersion X.Y.Z. | detect.ps1 -RequireHealthy, or the installed CLI's enterprise windows verify --profile standalone --json exits 0. |
| Linux | dpkg-query -W -f='${Status} ${Version}' defenseclaw-enterprise or rpm -q defenseclaw-enterprise (package channel only). Or detect.sh --min-version X.Y.Z, which covers every channel. | detect.sh --require-healthy, or the wrapper's --action verify exits 0. |
| macOS | pkgutil --pkg-info com.cisco.defenseclaw.enterprise reports version: X.Y.Z (pkg channel). Or detect.sh --min-version X.Y.Z. | detect.sh --require-healthy, or the wrapper's --action verify exits 0. |
detect.ps1 [-MinimumVersion X.Y.Z] [-RequireHealthy] follows Intune's
custom-detection rules: "detected" is exit 0 with one line on standard
output (DefenseClaw Enterprise 1.4.0) and nothing on standard error; "not
detected" is exit 1 with the reason on standard error. It reads the marker
through the 64-bit registry view, so it works in 32-bit Windows PowerShell.
detect.sh [--min-version X.Y.Z] [--require-healthy] [--format exit|value|jamf]
asks the installed, root-owned gateway for its status instead of trusting
files, so run it as root. The formats are:
| Format | Exit code | Output |
|---|---|---|
exit (default) | 0 when detected, 1 otherwise | DefenseClaw Enterprise <version> on standard output, or the reason on standard error |
value | Always 0 | The installed version, or not-installed, outdated (with --min-version) or unhealthy (with --require-healthy). For inventory attributes. |
jamf | Always 0 | The same values inside <result>...</result>, for Jamf Pro extension attributes |
In every format, an unknown argument or a bad --format exits 2 and
prints nothing on standard output.
Versions are dotted releases. A leading v and build metadata (+...) are
ignored, and a prerelease sorts before its release (1.4.0-rc1 is older than
1.4.0).
When an installed agent runs without DefenseClaw hooks for one account
(agent_unprotected, hook_contract_unverified), verify, and so the
health checks above, reports it as a warning for that account and passes,
with security_complete: false.
It does not require the Windows Claude Code attestation: a host without it
passes verify and reports security_complete: false. To collect
security_complete across a fleet, read it from the last lifecycle result
(%WINDIR%\Logs\DefenseClaw\last-result.json on Windows; the --json
output of status on Linux and macOS) with a custom compliance script or
inventory attribute of your MDM.
Windows also keeps an Add/Remove Programs entry,
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\CiscoDefenseClawEnterprise
(DisplayName "Cisco DefenseClaw Enterprise", DisplayVersion), and the
marker's other values: Profile, InstallRoot, StateRoot, TrustMode,
UpdatedAt and DisableSelfUpdate.
Claude Code on Windows
With Claude Code enabled, a Windows host reports security_complete: false
until an administrator attests that Claude Code runs DefenseClaw's managed
hooks there: check it with enterprise policy verify --live in an enrolled
user's session, then run Setup /repair ATTESTCLAUDEEFFECTIVEPOLICY=1 JSON=1
(see Verify the install). The
record is bound to the hashes of the installed Claude Code policy and hook
binary, so every DefenseClaw upgrade makes it stale. Computers that run the
same release and Claude Code hook contract have the same binding, so you can
check one reference computer and push the /repair command to the others
from your MDM after each upgrade; the attestation then stands for each of
those computers.
Logs
| OS | Where |
|---|---|
| Windows | %WINDIR%\Logs\DefenseClaw\enterprise-lifecycle.log: one JSON result per lifecycle run, five generations of 5 MiB. last-result.json in the same folder: the latest full result. mdm-wrapper.log in the same folder: the wrapper's steps. DefenseClaw event log, source "DefenseClaw Lifecycle": 100 installed, 101 upgraded, 102 repaired, 110 uninstalled (Application log only), 111 ensure no-op, 112 ensure applied, 120 unhealthy, 130 failed, 140 busy, 150 refused. A legacy copy of each event goes to the Application log, source "DefenseClaw Enterprise". Users can read the files and both event logs. Only SYSTEM and Administrators can write the files and the DefenseClaw log; any account can write Application-log entries under any source name, so detect from the DefenseClaw log. |
| Linux | /var/log/defenseclaw-enterprise-mdm.log (root, 0600): the wrapper's steps and results. /var/lib/defenseclaw-enterprise/last-package-result.json and last-package-result.log: the package's own ensure. journalctl -u defenseclaw-enterprise-apply.service and -u defenseclaw-enterprise-verify.service: config-change and daily verify runs. |
| macOS | /Library/Logs/Cisco/DefenseClaw/mdm-wrapper.log (root, 0600): the wrapper. lifecycle.log and verify.log in the same folder: config-change runs and the daily verify. /opt/cisco/defenseclaw/lifecycle/last-package-result.json: the pkg's own ensure. |
The services' own logs are covered in Operations.
Upgrades, rollback and config changes
Upgrade. Deliver the new artifact and its pin, and run ensure. It
upgrades in place and keeps the config, key and state. In an MDM that models
upgrades as supersedence, make the new version supersede the old one
without uninstalling it first. On Linux, a package manager upgrade works
too: the package's post-install step runs ensure. Keep each host on one
channel: a host installed from the deb or rpm refuses a payload-archive
upgrade (package_owned_binaries).
Roll back. Every lifecycle refuses an older release than the installed one
unless you ask for the rollback (downgrade_refused; exit 1603 on Windows).
The wrappers do not ask for it: rpm -U refuses an older rpm, and on Linux the
lifecycle refuses an older deb after dpkg installs it. On macOS, create the
root-owned rollback marker /opt/cisco/defenseclaw/lifecycle/allow-downgrade
before you deliver the older pkg; the package consumes it. See
Roll back for the supported
procedure.
Change the config.
| OS | How |
|---|---|
| Linux, macOS | Run the wrapper again with the new config and no artifact. The lifecycle validates the new file before it replaces the installed one, then applies it. With a script-only MDM, edit the config in the script and let the MDM run it again. Replacing the installed file directly also works: the apply job (defenseclaw-enterprise-apply.path on Linux, com.cisco.defenseclaw.apply on macOS) runs ensure when the file changes. |
| Windows | Run Setup again with the new file: /ensure CONFIG=<new file> JSON=1, or the wrapper with -SetupPath and -ConfigPath. Use a Setup of the installed version or newer. ensure sees that the config differs and re-applies the deployment with it. The installed CLI cannot apply a new config on its own: its enterprise windows ensure --config (and the wrapper without -SetupPath) exits 1639, because it has no payload to re-apply. |
An MDM that detects the Windows deployment by version does not re-run it for a config-only change. Intune on Windows shows a detection rule that also checks the config. More detail is in Change the config.
Uninstall
The removal scripts are idempotent: on a host with nothing installed they print
a no-op result and exit 0.
| OS | Command | Removes |
|---|---|---|
| Linux | uninstall.sh [--purge] [--keep-package] [--log FILE], or DC_PURGE=1 and DC_KEEP_PACKAGE=1 in its settings block | The services, DefenseClaw's hooks and machine-policy entries (administrator entries stay), then the deb or rpm unless --keep-package. --purge also removes /etc/defenseclaw (config, policies, keys), the state under /var/lib and the logs. The defenseclaw service account stays. |
| macOS | uninstall.sh [--purge] [--log FILE] | The same, and the lifecycle forgets the pkg receipt. --purge also removes /opt/cisco/defenseclaw and the logs. |
| Windows | uninstall.ps1 [-Purge] (runs in Windows PowerShell 5.1 or PowerShell 7), or DefenseClawSetup-Enterprise-Standalone-x64.exe /uninstall JSON=1 with optional PURGE=1 | The services, hooks, machine-policy entries, the Add/Remove Programs entry and the marker. Purge also removes the managed state. |
See Uninstall for what stays behind and how to remove the service account.
Simulate your MDM
You can validate a recipe without a tenant by reproducing the context the MDM agent runs in, then checking four things: the exit code, standard output (one result document, or the detection line), standard error, and the result document itself against the schema.
Linux and macOS. MDM agents run scripts as root, with a minimal environment, no terminal and nothing on standard input:
sudo env -i /bin/sh ./defenseclaw-enterprise.sh --action status < /dev/null > result.json 2> stderr.txt
echo "exit=$?"
sudo env -i /bin/sh ./detect.sh --format value < /dev/nullFor a script-only MDM, run your edited copy of the wrapper the same way with no arguments.
Windows. Intune and most Windows MDMs run as SYSTEM, often from a 32-bit
process. Reproduce that with a scheduled task that runs the 32-bit Windows
PowerShell (SysWOW64), then again with the 64-bit one (System32). Run this
from an elevated PowerShell; C:\DcSim holds your copy of the script:
$sim = 'C:\DcSim'
$engine = Join-Path $env:WINDIR 'SysWOW64\WindowsPowerShell\v1.0\powershell.exe'
$command = "/c `"`"$engine`" -NoProfile -NonInteractive -ExecutionPolicy Bypass -File $sim\detect.ps1 -MinimumVersion 1.4.0 1>$sim\stdout.txt 2>$sim\stderr.txt`""
$action = New-ScheduledTaskAction -Execute (Join-Path $env:WINDIR 'System32\cmd.exe') -Argument $command
$principal = New-ScheduledTaskPrincipal -UserId 'SYSTEM' -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName 'DefenseClaw MDM simulation' -Action $action -Principal $principal -Force | Out-Null
Start-ScheduledTask -TaskName 'DefenseClaw MDM simulation'
Start-Sleep -Seconds 3
while ((Get-ScheduledTask -TaskName 'DefenseClaw MDM simulation').State -eq 'Running') { Start-Sleep -Seconds 2 }
"exit=$((Get-ScheduledTaskInfo -TaskName 'DefenseClaw MDM simulation').LastTaskResult)"
Get-Content "$sim\stdout.txt", "$sim\stderr.txt"
Unregister-ScheduledTask -TaskName 'DefenseClaw MDM simulation' -Confirm:$falseAlso try the failure paths your MDM must handle: a wrong pin, a config that
other users can write, two runs at once (expect 1618 or 75 from one), and a
run with no user signed in.
The project's own CI installs every standalone package this way on each pull
request: the .deb on Ubuntu 24.04, the .rpm in RHEL 9 and RHEL 8
containers that boot systemd, the macOS .pkg on a macOS runner, and the
unsigned Windows Setup on a Windows runner. Each lane runs install, a second
ensure that must change nothing, verify, status, the detection script
and uninstall, and checks every lifecycle result. The scripts it uses,
scripts/test-enterprise-unix-install.sh,
scripts/test-enterprise-linux-container.sh,
scripts/test-enterprise-windows-install.ps1 and
scripts/check_enterprise_lifecycle_result.py, also work on a disposable
test host of your own; see
docs/TESTING.md.
Template
Recipes in this section that carry the label above were built from the vendor's documentation and checked with the simulations on this page. Nobody has yet run them end to end in that vendor's live service against this release. Pilot them on a small device group first.
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.
Intune on Windows
Deploy the standalone DefenseClaw enterprise profile to Windows x64 with a Microsoft Intune Win32 app, keep it healthy with Remediations, deliver the AI Defense key, change the config and remove it.