Setup

Enterprise hardening and deployment

Provision DefenseClaw as a managed operating-system service, understand its trust boundaries, and continuously repair per-user AI-agent hooks.

Enterprise deployment is for centrally managed endpoints where DefenseClaw policy must remain under administrator control instead of the interactive user running Codex, Claude Code, Cursor, Antigravity, or another AI agent. Start with the rollout sequence below, then follow the platform section for Linux, Windows, or macOS.

Managed mode boundary

Set deployment_mode: managed_enterprise when DefenseClaw is installed as an operating-system service. In this mode, config.yaml is treated as an administrator-owned policy file and runtime HTTP PATCH changes are rejected.

Exact tamper-resistance guarantee

An AI agent running as a standard, non-privileged user cannot stop the system service, replace the administrator-owned DefenseClaw binary, edit administrator policy, read service-side scoped credentials, or forge the administrator-owned guardian authorization record. The same user still owns the AI application's native config and per-user hook files, so it can create a short local bypass window by editing or deleting those files. The guardian detects and repairs that drift; prevention requires MDM, EDR, application control, or filesystem policy that denies the user write access.

Normal mode does not lose auto-heal

Enterprise service enforcement is opt-in. If the effective deployment mode is not managed_enterprise, DefenseClaw keeps its existing per-user startup, connector setup, application-protection repair, and hook self-heal behavior. Installing a release that contains Windows enterprise support does not create a machine service, move state into ProgramData, harden user files, or transfer hook ownership to the enterprise guardian. Those changes occur only when an administrator runs the enterprise installer with a managed config. On Windows, ordinary process startup also returns before SCM-host detection unless the protected service environment contains the installer-owned exact service-name marker.

Security model at a glance

Enterprise hardening separates policy authority, inspection runtime, and user-agent integration:

Linux / macOS targets
Windows connector scope
publish eligible rows
install + repair
authorize
OperatorAdministrator / MDM
PolicyRoot-ownedconfig + services
Agent runtimeWindows eligibleinteractive profiles
OperatorDefenseClawHookEnumeratorfiltered enrollment
PolicyProtected targetsusers · connectors
OperatorHook guardianprivileged repair
Agent runtimeUser AI agentCodex · Claude · …
ConnectorNative hooksscoped token
ConnectorLoopback hook API
Control planeDefenseClaw gateway
Evidence storeAuthorization ledgerroot-owned
SystemManaged readinessenforcement health
Administrator policy and target authorization remain outside the agent user's authority. Administrators author Linux/macOS targets; on Windows the enumerator maintains eligible profile rows in the protected manifest. The gateway evaluates hook traffic, and the guardian repairs only enrolled integrations.

The gateway is intentionally unable to write broadly into user homes. The guardian is the only privileged component that repairs enrolled per-user integrations. Linux and macOS use administrator-authored target manifests. Managed Windows adds a separate LocalSystem enumerator that discovers eligible interactive profiles and maintains the protected manifest as described under Windows enrollment and discovery; the gateway itself still does not enumerate or write profiles.

Deployment-mode behavior matrix

Effective modeLinuxWindowsmacOSHook repair owner
Empty/default or any non-managed mode, including unmanaged_byod, ci_cd, sandboxed, server, and saasExisting user/process deployment behaviorExisting per-user startup/watchdog and hook auto-heal behaviorExisting user/process deployment behaviorThe ordinary gateway/application-protection hook guard. No added enterprise service enforcement is activated.
managed_enterpriseAdministrator-owned systemd gateway plus privileged manifest guardian; event watcher and periodic timerFour administrator-owned SCM services: gateway, credential broker, manifest guardian, and profile enumerator; guardian watch plus periodic reconciliationAdministrator-owned LaunchDaemons; periodic guardian reconcileThe privileged enterprise guardian repairs only the protected target manifest. The gateway does not write interactive-user homes.

The matrix is an ownership switch, not an auto-heal switch. Both rows repair hook drift: normal mode uses the existing per-user repair loop, while managed mode transfers repair to an administrator-owned guardian that a standard user cannot unregister or reconfigure.

Threat model

ActorAssumed capabilityExpected result
AI agent or shell running as the protected userRead/write files owned by that user; start and kill that user's processesCannot modify the system service, binaries, managed config, service token store, or guardian authorization. Can tamper with user-owned hook/config files until the guardian repairs them.
Another unprivileged local userAccess permitted by normal UNIX permissions, macOS ACLs, or Windows DACLsCannot read protected runtime state or scoped service credentials; cannot repair or authorize another user's hooks.
Endpoint administrator, root, MDM, or a process with equivalent privilegeFull machine administrationCan install, upgrade, disable, or remove DefenseClaw. DefenseClaw does not attempt to defend the machine from its trusted administrator.
Compromised gateway processService identity plus the platform service boundaryCan write only its declared runtime/log paths; cannot write user homes, managed config, binaries, service definitions, or arbitrary host paths. The packaged Linux unit adds a systemd sandbox; Windows uses a dedicated virtual service account and protected DACLs.
Compromised guardian processControl of a process running as root on Linux/macOS or LocalSystem on WindowsInherits authority equivalent to the trusted administrator and can bypass the manifest, path, and impersonation safeguards to modify host state allowed by that token. Those safeguards bound normal guardian behavior and malformed-input handling; they do not sandbox an attacker who controls the process. This collapses to the trusted-administrator non-goal above.
Compromised enumerator processControl of a process running as LocalSystem on WindowsInherits authority equivalent to the trusted administrator and can bypass the managed-hook filter, manifest publication contract, and fixed-DACL targets to modify host state allowed by that token. Those controls bound normal enumerator behavior; they do not contain an attacker who controls the process. This collapses to the trusted-administrator non-goal above.

Security goals and non-goals

The managed deployment is designed to:

  • keep configuration, policy, binaries, service definitions, and authorization under administrator control;
  • prevent runtime API clients from downgrading managed_enterprise or changing security-critical topology;
  • give each connector a credential accepted only by that connector's hook/notify routes;
  • fail managed readiness until every configured hook connector has trusted guardian coverage;
  • reject unsafe owners, modes, ACLs, symlinks, reparse points, path escapes, malformed manifests, and unsupported hook contracts;
  • repair authorized user-owned hook drift without continuously rewriting unchanged files;
  • report guardian failures and connector enforcement truthfully instead of claiming protection after partial setup.

It is not designed to:

  • protect the host from root, an MDM administrator, or an AI agent that has been given unrestricted sudo/admin credentials;
  • make user-owned native agent configuration immutable without an external endpoint-control policy;
  • guarantee zero-time repair after a user edits or deletes a hook;
  • convert hook-native connectors into an inline network proxy when the vendor integration is lifecycle-hook based;
  • silently create application profiles for users who have never initialized the native agent.

Why a standard AI agent cannot permanently remove DefenseClaw

The durable controls live outside the agent user's authority:

  1. The gateway is a system service. It runs as the dedicated defenseclaw identity, not as the interactive Codex or Claude Code user. Killing the agent process does not kill the gateway.
  2. The executable and service definition are administrator-owned. A standard user cannot replace /opt/defenseclaw/bin/defenseclaw-gateway, edit the systemd unit, replace /opt/cisco/secureclient/defenseclaw/bin/defenseclaw-gateway, unload a system LaunchDaemon, replace C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\bin\defenseclaw-gateway.exe, or change/delete the Windows SCM services.
  3. Policy is administrator-owned and mode-pinned. The service sets DEFENSECLAW_DEPLOYMENT_MODE=managed_enterprise; changing a user environment variable or user config cannot downgrade the running service.
  4. Runtime mutation is denied. Authenticated non-admin clients receive HTTP 403 for managed-enterprise config changes. Security-critical changes require an administrator edit and service restart.
  5. Guardian authorization is root-owned. Service-writable status is not sufficient to authorize repair or claim readiness. The gateway trusts the separate administrator-owned authorization record.
  6. Per-user credentials are narrow. Reading a user's .hook-<connector>.token grants only that connector's hook route, not status, config, policy, scan, or cross-connector API access.
  7. Per-user drift is repaired. The event-driven Linux and Windows watchers plus their periodic backstops restore canonical hooks, helper files, scoped tokens, permissions, and supported native config entries after an authorized target is changed. The packaged macOS guardian performs periodic reconcile.

The important qualifier is permanently. A standard user—or an AI agent executing tools as that user—can delete a user-owned hook and invoke the agent before repair completes. On Linux with the event-driven watcher enabled, repairs were observed in hundreds of milliseconds during testing, but that is an observed implementation result, not a hard real-time guarantee. The Linux periodic backstop and the packaged macOS periodic reconcile have longer repair windows. If a threat model requires the hook to be impossible for the user to modify, enforce the native agent config and hook directory with MDM/EDR/application-control policy and ensure the AI agent has no path to sudo, root shells, privileged package managers, service control, or policy-exemption tools.

Administrative controls required around the agent

  • Do not grant passwordless sudo or local administrator rights to the account used by the AI agent.
  • Do not expose root-capable MCP servers, terminal wrappers, deployment keys, or remote-management credentials to the agent.
  • Restrict systemctl, launchctl, Windows service control, package installation, kernel/debug tooling, and endpoint-security exclusions through normal OS policy.
  • Use a separate administrator or MDM channel for DefenseClaw upgrades and guardian manifest changes.
  • Treat a host-level root compromise as an endpoint incident; reinstall or attest the endpoint rather than relying on in-process repair.

Hardening controls

ControlWhat it protectsEnforcement behavior
Managed mode pinDeployment mode and administrator authorityThe service environment pins managed_enterprise; user config cannot downgrade it.
Trusted config pathManaged config and policy inputsRejects unsafe ownership, writable ancestors, symlinks, and platform-specific ACL/DACL or reparse-point hazards.
Administrator-owned binaries and service definitionsExecutable integrity and startupStandard users cannot replace the gateway or guardian, edit systemd units or LaunchDaemons, or change/delete Windows SCM services.
Gateway service sandboxHost filesystem, devices, kernel, namespaces, and process visibilityLinux gateway runs with NoNewPrivileges=true, an empty capability set, strict filesystem protection, private devices/tmp, filtered syscalls, and hidden non-service processes.
Protected guardian manifestPrivileged repair scopeLinux/macOS administrators choose exact user/connector/version targets. Managed Windows maintains the manifest from filtered HKLM ProfileList discovery and configured supported hook connectors; it does not scan arbitrary filesystem roots.
Administrator-owned authorization ledgerManaged readiness claimsA target is enforcement-covered only after a privileged successful reconcile writes trusted authorization.
Connector-scoped hook tokenAPI least privilege and connector isolationToken is accepted only for the matching hook/notify route and cannot authorize management APIs or another connector.
Raw scoped-token formatService/hook format consistencyThe service validator and generated hook consume the same raw file; inherited generic gateway credentials cannot shadow it.
Hook contract pinningVendor lifecycle compatibilityAction-mode installation rejects unknown or drifted agent hook contracts unless an administrator explicitly opts into exploratory drift.
Filesystem footprint validationHook/config path integrityRejects path escapes, unsafe parents, foreign owners, special files, and first-install writable/symlinked targets.
Authorized repairPost-install user tamperAfter trusted authorization, target-owned symlinks can be removed and unsafe target-owned modes—including SUID/SGID/sticky bits—are normalized before canonical reinstall.
Atomic, idempotent writesPartial updates and watcher loopsSame-directory replacement prevents partial files; unchanged bytes, modes, ownership, backups, and contracts preserve mtimes and do not retrigger the watcher.
Truthful startup and healthPartial-success false assuranceScoped-token, setup, contract, and verification failures publish error state and prevent managed enforcement from being reported as ready.

What moves to system scope

SurfaceLinux pathLinux ownermacOS pathmacOS owner
Binaries/opt/defenseclaw/binroot:root/opt/cisco/secureclient/defenseclaw/binroot:wheel
Config/etc/defenseclaw/config.yamlroot:defenseclaw/opt/cisco/secureclient/defenseclaw/etc/config.yamlroot:wheel
Runtime state/var/lib/defenseclawdefenseclaw:defenseclaw/opt/cisco/secureclient/defenseclaw/runtimeroot:wheel
Logs/var/log/defenseclawdefenseclaw:defenseclaw/Library/Logs/Cisco/SecureClient/DefenseClawroot:wheel
Service definition/etc/systemd/system/defenseclaw-gateway.serviceroot:root/Library/LaunchDaemons/com.cisco.secureclient.defenseclaw.plistroot:wheel

User-owned agent configuration remains in user space. Examples include ~/.codex/config.toml, ~/.claude/settings.json, and ~/.gemini/settings.json. DefenseClaw can only harden those files by installing and repairing hooks through a privileged enterprise controller; users still own the agent's native configuration file.

Use a staged deployment rather than enabling action mode everywhere at once:

  1. Inventory endpoints and agent versions. Record OS, interactive user, home directory, connector, native config path, and exact agent version.
  2. Deploy the system service in observe mode. Validate config ownership, loopback listeners, service sandboxing, audit output, and operational monitoring.
  3. Initialize each agent profile as the user. The native agent must create its config before the privileged guardian performs a first installation.
  4. Establish protected guardian targets. On Linux/macOS, add only approved users and connectors to the administrator-owned manifest. On Windows, configure the approved connector families and review the enumerator-produced SID/version rows before acceptance.
  5. Run one manual reconcile. Review JSON/status output and resolve every failed target before enabling the watcher.
  6. Enable event-driven and periodic repair. On Linux, run the watcher plus timer. On macOS, the packaged LaunchDaemon performs periodic reconcile; use MDM/EDR for stronger native-config prevention.
  7. Move selected connectors to action mode. Confirm allow and deterministic block events, then monitor error/block counters and guardian health.
  8. Record deployment acceptance. On a dedicated test endpoint, confirm the evidence under Verify, exercise one allowed and one blocked request, and record the recovery window for an authorized user-hook repair. Keep destructive tamper testing in your controlled endpoint-validation pipeline rather than on a production user workstation.

Prerequisites

  • A supported DefenseClaw build and its matching packaging files.
  • Root or MDM access through a channel unavailable to the protected AI agent.
  • A daemon identity for the gateway: on Linux, a dedicated non-login defenseclaw service user; on macOS, the LaunchDaemon runs as root (the managed cloud auth provider requires root to read its on-disk credential store).
  • An initialized native config for every protected user/connector.
  • A policy decision for response failure mode and availability behavior.
  • Central collection for service health, guardian failures, and audit events.
  • No passwordless sudo or root-capable tool integration exposed to the AI agent account.

Linux systemd layout

The Linux design uses three privilege domains:

ComponentIdentityWritable scopePurpose
Gatewaydefenseclaw:defenseclaw/var/lib/defenseclaw, /var/log/defenseclaw, /run/defenseclawInspection, policy, audit, hook API, and health.
Guardian reconcile/watchroot with bounded unit restrictionsAllow-listed user homes plus guardian/runtime stateInstall and repair native user hooks after trusted path validation.
AI agentInteractive standard userIts normal user profileInvoke native hooks; cannot manage the system service or administrator assets.

Create a service user and the managed directories:

sudo useradd --system --home /var/lib/defenseclaw --shell /usr/sbin/nologin defenseclaw
sudo install -d -o root -g defenseclaw -m 0750 /etc/defenseclaw
sudo install -d -o root -g root -m 0750 /etc/defenseclaw/hook-guardian
sudo install -d -o root -g root -m 0755 /opt/defenseclaw/bin
sudo install -d -o defenseclaw -g defenseclaw -m 0750 /var/lib/defenseclaw /var/log/defenseclaw
sudo install -d -o root -g defenseclaw -m 0750 /var/lib/defenseclaw-hook-guardian

Install the binaries and service files:

sudo install -o root -g root -m 0755 defenseclaw-gateway /opt/defenseclaw/bin/defenseclaw-gateway
sudo install -o root -g root -m 0755 defenseclaw /opt/defenseclaw/bin/defenseclaw
sudo install -D -o root -g root -m 0644 packaging/systemd/defenseclaw-gateway.service /etc/systemd/system/defenseclaw-gateway.service
sudo install -D -o root -g root -m 0644 packaging/systemd/defenseclaw-hook-guardian@.service /etc/systemd/system/defenseclaw-hook-guardian@.service
sudo install -D -o root -g root -m 0644 packaging/systemd/defenseclaw-hook-guardian.service /etc/systemd/system/defenseclaw-hook-guardian.service
sudo install -D -o root -g root -m 0644 packaging/systemd/defenseclaw-hook-guardian-watch.service /etc/systemd/system/defenseclaw-hook-guardian-watch.service
sudo install -D -o root -g root -m 0644 packaging/systemd/defenseclaw-hook-guardian.timer /etc/systemd/system/defenseclaw-hook-guardian.timer

Before starting anything, verify that no path in the binary/config/unit chain is writable by the protected user:

namei -l /opt/defenseclaw/bin/defenseclaw-gateway
namei -l /etc/defenseclaw
stat -c '%U:%G %a %n' \
  /opt/defenseclaw/bin/defenseclaw-gateway \
  /etc/systemd/system/defenseclaw-gateway.service \
  /etc/systemd/system/defenseclaw-hook-guardian-watch.service

Create /etc/defenseclaw/config.yaml:

config_version: 8
deployment_mode: managed_enterprise
data_dir: /var/lib/defenseclaw
plugin_dir: /var/lib/defenseclaw/plugins
policy_dir: /var/lib/defenseclaw/policies
observability:
  local:
    path: /var/lib/defenseclaw/audit.db
    judge_bodies_path: /var/lib/defenseclaw/judge_bodies.db
    retention_days: 7
  defaults:
    redaction_profile: sensitive
gateway:
  device_key_file: /var/lib/defenseclaw/device.key
  api_bind: 127.0.0.1
  api_port: 18970
  config_reload:
    mode: hot
guardrail:
  enabled: true
  mode: observe
  scanner_mode: both
application_protection:
  enabled: false

Seven days is the runtime default. Increase retention_days only as an intentional, capacity-planned override backed by storage sizing and your audit retention requirements.

The packaged macOS installer (packaging/macos/install.sh) defaults guardrail.mode to action for managed_enterprise deployments so hooks enforce their verdict from the first tick. Pass --mode observe on the initial rollout if you want a logging-only pilot; validate telemetry and guardian coverage, then change back to action through the administrator-owned file and restart the service. Do not use a user-writable overlay config to stage enterprise policy.

The AI Defense endpoint (cisco_ai_defense.endpoint in the emitted config.yaml) is sourced from a static JSON file that the Cisco Secure Client (AVC) module drops at /opt/cisco/secureclient/defenseclaw/env_config.json:

{
  "cisco_ai_defense_endpoint": "https://us.api.inspect.aidefense.security.cisco.com"
}

install.sh reads only the cisco_ai_defense_endpoint field; extra keys are ignored for forward compatibility. The file must be root-owned with no group/other write bits and no write-capable macOS ACL — the installer walks every ancestor and applies the same checks before reading. Override with --config-file PATH (fleet-specific staging) or --override-endpoint URL (adhoc / preview testing). If the default env_config.json is missing or malformed, the installer emits a warning and falls back to the US-production AI Defense endpoint so the enrollment is not blocked; only failed ownership, permission, or ACL trust checks on a present file still fail the installer closed.

Set ownership and start the service:

sudo chown root:defenseclaw /etc/defenseclaw/config.yaml
sudo chmod 0640 /etc/defenseclaw/config.yaml
namei -l /etc/defenseclaw/config.yaml
stat -c '%U:%G %a %n' /etc/defenseclaw/config.yaml
sudo systemctl daemon-reload
sudo systemctl enable --now defenseclaw-gateway.service

Confirm the packaged environment pin and sandbox are active:

sudo systemctl show defenseclaw-gateway.service \
  -p User -p Group -p Environment -p NoNewPrivileges \
  -p CapabilityBoundingSet -p ProtectSystem -p ProtectHome \
  -p ReadOnlyPaths -p ReadWritePaths
sudo systemd-analyze security defenseclaw-gateway.service
sudo ss -ltnp | grep 18970

The API listener must be loopback-only. A security exposure score is a comparative systemd signal, not an attestation; review the actual properties and your distribution's systemd version.

The packaged unit sets:

DEFENSECLAW_HOME=/var/lib/defenseclaw
DEFENSECLAW_CONFIG=/etc/defenseclaw/config.yaml

DEFENSECLAW_CONFIG lets the service keep administrator policy in /etc while runtime state remains in /var/lib.

The unit also pins StateDirectoryMode=0750, RuntimeDirectoryMode=0750, and LogsDirectoryMode=0750; do not rely on systemd's default directory modes for secret-bearing runtime state. It enables a restrictive service sandbox (ProtectSystem=strict, ProtectHome=true, private devices/tmp, kernel and control-group protections, hidden non-service /proc entries, the @system-service syscall allow-list, no realtime scheduling, no SUID/namespace escalation, and an empty capability bounding set). If your deployment needs host-wide user discovery or hook repair, run that as the enterprise guardian component rather than widening the gateway service sandbox.

Windows system-service layout

Windows managed deployment uses four Service Control Manager (SCM) services and three privilege domains:

ComponentIdentityWritable scopePurpose
DefenseClawGatewayNT SERVICE\DefenseClawGateway virtual service accountManaged runtime and logs onlyInspection, policy, audit, hook API, health, and least-privilege service-token access.
DefenseClawCMIDBrokerLocalSystem with only SeChangeNotifyPrivilegeBroker authentication key, named pipe, broker log, and the pinned Cisco provider DLLIsolate access to the machine credential provider behind an authenticated gateway-only IPC boundary.
DefenseClawHookGuardianLocalSystemEnrolled user hook footprints plus guardian stateImpersonate each manifest SID, install or repair that user's files with the user's ownership, and return to LocalSystem for protected state.
DefenseClawHookEnumeratorLocalSystemHKLM ProfileList discovery, protected target manifest, and bounded inventory-directory DACL grantsRefresh eligible connector enrollment without granting the gateway access to an entire user profile.
AI agentInteractive standard user at medium integrityIts normal profileInvoke native hooks; cannot stop or reconfigure any managed service or modify administrator assets.

The default managed layout is:

SurfaceDefault pathAuthority
Broker, gateway/guardian/enumerator host, native hook, and optional CLI binariesC:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\binAdministrators/LocalSystem own and write; service identities receive only the access they need.
Managed configC:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\etc\config.yamlAdministrators/LocalSystem write; the gateway service can read.
Runtime state and service-side scoped credentialsC:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\runtimeGateway virtual account and LocalSystem; standard users have no read or write grant.
Cisco Secure Client UI IPC socketC:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\ipc\defenseclaw_ipc.sockFixed under trusted Program Files on managed Windows. The installer grants the gateway service identity the required directory access; DEFENSECLAW_IPC_SOCKET cannot redirect it.
LogsC:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\logsService identities write; grant support-reader access separately if required.
Guardian manifestC:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\hook-guardian\targets.yamlAdministrators/LocalSystem write; guardian reads.
Trusted authorization ledgerC:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\hook-guardian-state\protected_targets.jsonLocalSystem/Administrators only; gateway receives the narrow read access required for readiness.
Deployment metadataC:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\install\deployment.jsonInstaller-owned transaction and servicing state.
Shared Codex machine-policy parentC:\ProgramData\OpenAI\CodexExplicit install/upgrade/repair creates missing OpenAI and Codex directories atomically with Administrators/System full control and Users read/traverse only. Existing directories are validation-only and are never taken over.
Codex managed requirementsC:\ProgramData\OpenAI\Codex\requirements.toml, .defenseclaw-managed-hooks.state, and .defenseclaw-managed-hooks.lockDefenseClaw-owned canonical ten-event policy, enrollment state, and protected bounded transaction lock; users can read policy but cannot write it. Ownership and ACL-preimage records are private under the install-state directory.
Claude managed hook policyC:\Program Files\ClaudeCode\managed-settings.d\90-defenseclaw.json and adjacent ownership stateAdministrators/LocalSystem write; registered users read and invoke; unrelated Claude settings remain outside DefenseClaw ownership.
Agent-control evidenceC:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\install\agent-application-control-attestation.jsonProtected schema-v2 application-control and Claude-precedence evidence.
Service definitionsSCM registry and security descriptorsAdministrators/LocalSystem only.

The installer rejects install/state roots outside exact mount-manager drive roots on fixed local NTFS volumes, path escapes, unsafe owners or DACLs, and reparse points in managed path chains. It requires the effective drive, global drive, and volume-GUID DOS-device names to resolve to one identical kernel device and requires the drive root to appear in the volume's bounded mount-manager path list. This also rejects an unused per-logon drive letter created with raw DefineDosDevice: that alias can misleadingly report Fixed/NTFS and remain absent from subst.exe. Do not redirect these paths to a user profile, network share, substituted or raw-aliased drive, OneDrive folder, volume-folder mount point, or junction.

Default roots come from the Windows ProgramFiles and CommonApplicationData known-folder APIs, not the caller's ProgramFiles or ProgramData environment variables. Environment poisoning therefore cannot redirect a default deployment. Explicit root overrides remain constrained to the same fixed local known-folder trees and exist for isolated certification, not ordinary fleet layout changes.

Prepare Windows policy and targets

Create an administrator-approved config.yaml. Use forward slashes or quoted backslashes in YAML:

config_version: 8
deployment_mode: managed_enterprise
data_dir: 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\runtime'
observability:
  local:
    path: 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\runtime\audit.db'
    judge_bodies_path: 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\runtime\judge_bodies.db'
  defaults:
    redaction_profile: sensitive
plugin_dir: 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\runtime\plugins'
policy_dir: 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\runtime\policies'
gateway:
  device_key_file: 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\runtime\device.key'
  api_bind: 127.0.0.1
  api_port: 18970
  config_reload:
    mode: restart
guardrail:
  enabled: true
  mode: observe
  scanner_mode: both
application_protection:
  enabled: false

Keep application_protection.enabled: false in the system-service config. The normal per-user application-protection controller is not removed from the product; managed mode deliberately transfers that work to the guardian so the gateway virtual account never scans or writes arbitrary profiles.

Stage a schema-valid targets.yaml for the first transaction. The enumerator subsequently maintains this protected file from eligible Windows profiles. A target user must already have a supported CLI installation that the enumerator can version, and any required native application config must exist before the guardian can complete enrollment:

version: 1
targets:
  - user: alice
    connector: codex
    agent_version: "codex-cli 0.144.3"
  - user: alice
    connector: claudecode
    agent_version: "2.1.207 (Claude Code)"

For domain, directory-backed, or renamed accounts, record and deploy the exact SID in the target. The guardian binds the resolved profile owner to that SID and refuses service identities, mismatches, and profile paths outside the declared user's trusted path:

  - user: CONTOSO\alice
    sid: S-1-5-21-111111111-222222222-333333333-1001
    user_home: 'C:\Users\alice'
    connector: codex
    agent_version: "codex-cli 0.144.3"

Windows enrollment and discovery

DefenseClawHookEnumerator does not treat the staged manifest as a permanent manual allow-list. On service start it waits 30 seconds for gateway and guardian health to settle, then walks HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList. It repeats every five minutes, with a 60-second ceiling on each cycle, and atomically publishes targets.yaml only when its bytes change.

The enumerator considers only syntactically valid interactive-user SIDs shaped like S-1-5-21-… with a final user RID. It rejects well-known, service, and bare-domain SIDs; non-absolute or missing profile paths; profiles whose path or ancestor is a reparse point; explicitly excluded SIDs; and duplicate SID rows except for the newest validated profile. Connector families come from the managed config and are filtered to the Windows managed-hook implementation. Recognition by this discovery layer is not a certification claim; deploy only connectors explicitly qualified by the managed-enterprise acceptance guidance.

For a previously known (SID, connector) row, the enumerator preserves the administrator's enabled, deferred, and agent_version state. For a new row, it enables and publishes the target automatically only when a supported per-user CLI and acceptable version can be discovered. Profiles without a discoverable supported CLI are omitted and logged as skipped. Consequently, adding a supported CLI to an eligible profile can enroll that user on the next five-minute cycle without a separate administrator edit to targets.yaml. Control which connector families are eligible in the protected managed config, and monitor the enumerator log and manifest as security-sensitive enrollment state.

After publishing a manifest, the enumerator grants the gateway virtual service SID read/execute/traverse access on existing inventory-relevant profile directories such as .claude, .codex, .cursor, .agents, and .config. The inheritable grant lets managed AI discovery read the enrolled user's connector inventory; it is not a grant across the whole profile. Missing directories are skipped and retried on a later cycle. A per-directory DACL failure is logged without blocking enrollment for other users, so operators must monitor these warnings to avoid silent inventory gaps.

Install the Windows services

First copy the installer, its adjacent DefenseClawEnterprise.psm1, approved binaries, and both policy files byte-stably into a release-specific local NTFS staging directory protected against standard-user writes. The installer validates the adjacent module before import: the complete path chain must be reparse-free on a fixed non-substituted NTFS volume, owners and replacement rights must be administrator-controlled, the module leaf must have no untrusted write-like ACE, and Authenticode must be valid unless a disposable test run explicitly uses -AllowUnsigned.

The unsigned exception is itself fail-closed before module import. It is valid only for Install, Upgrade, or Repair, and only when the service names are the exact case-sensitive DefenseClawCertGateway_<10-lowercase-hex> and DefenseClawCertGuardian_<same-id>, the install and state roots are the exact same-id leaves under the dedicated Program Files and ProgramData Cisco\Cisco Secure Client\DefenseClaw-Cert roots, and a required certification CODEX_HOME basename carries that same identifier. Production defaults, mismatched identifiers, case near misses, descendant/nested roots, and all other actions reject the switch. Omit it for every production deployment.

The signed defenseclaw enterprise windows ... CLI is the supported interactive-administrator entry point. It validates the installer and module, creates a unique protected bootstrap temp/cache/home directory, constructs a strict environment allowlist, and only then starts the fixed in-box PowerShell engine. Direct invocation of install-enterprise.ps1 is reserved for trusted LocalSystem or endpoint-management startup whose loader environment was protected before PowerShell began. The script creates and validates its own one-shot bootstrap directory before native helper compilation, but no script can retroactively undo COR_*, CORECLR_*, COMPlus_*, DOTNET_*, __COMPAT_LAYER, or similar loader influence that acted before its first line.

For that trusted non-interactive endpoint-management path, resolve the Windows directory through the known-folder API and use the in-box engine with -NoProfile; do not bootstrap from a user-writable checkout:

$Windows = [Environment]::GetFolderPath([Environment+SpecialFolder]::Windows)
$PowerShell = Join-Path $Windows 'System32\WindowsPowerShell\v1.0\powershell.exe'
$Stage = 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw-Staging\<release-id>'
$Installer = Join-Path $Stage 'install-enterprise.ps1'

& $PowerShell -NoLogo -NoProfile -NonInteractive -File $Installer `
  -Action Install `
  -BrokerBinary (Join-Path $Stage 'defenseclaw-cmid-broker.exe') `
  -ProviderLibrary 'C:\Program Files\Cisco\Cisco Secure Client\CM\<cm-version>\CMID\<cmid-version>\<arch>\cmidapi.dll' `
  -GatewayBinary (Join-Path $Stage 'defenseclaw-gateway.exe') `
  -HookBinary (Join-Path $Stage 'defenseclaw-hook.exe') `
  -CLIBinary (Join-Path $Stage 'defenseclaw.exe') `
  -Config (Join-Path $Stage 'config.yaml') `
  -Manifest (Join-Path $Stage 'targets.yaml') `
  -AttestAgentApplicationControl

For an interactive administrator, use the signed CLI. It exposes the same transaction and does not bypass the PowerShell installer or its trust preflight:

$DefenseClaw = Join-Path $Stage 'defenseclaw.exe'
& $DefenseClaw enterprise windows install `
  --installer $Installer `
  --broker-binary (Join-Path $Stage 'defenseclaw-cmid-broker.exe') `
  --gateway-binary (Join-Path $Stage 'defenseclaw-gateway.exe') `
  --hook-binary (Join-Path $Stage 'defenseclaw-hook.exe') `
  --cli-binary $DefenseClaw `
  --config (Join-Path $Stage 'config.yaml') `
  --manifest (Join-Path $Stage 'targets.yaml') `
  --json

WDAC or AppLocker is an optional defense-in-depth layer. Pass -AttestAgentApplicationControl (or --attest-agent-application-control) only after it allows approved signed clients and blocks explicitly tested old and unsigned clients. Omitting the attestation does not block either connector's managed hooks. The current minimum Claude version is 2.1.152. An enabled Codex target is complete when its protected managed hook policy is installed and verified.

Install is intentionally only phase one. Even when the protected services, files, ACLs, and any optional application-control evidence are structurally healthy, the initial result must keep claude_effective_policy_verified=false and security_complete=false. Exercise the real approved Claude client with hostile user and project disableAllHooks settings. Only after observing both DefenseClaw managed hooks or a causal client block may the administrator run:

& $PowerShell -NoLogo -NoProfile -NonInteractive -File $Installer `
  -Action Repair `
  -BrokerBinary (Join-Path $Stage 'defenseclaw-cmid-broker.exe') `
  -ProviderLibrary 'C:\Program Files\Cisco\Cisco Secure Client\CM\<cm-version>\CMID\<cmid-version>\<arch>\cmidapi.dll' `
  -GatewayBinary (Join-Path $Stage 'defenseclaw-gateway.exe') `
  -HookBinary (Join-Path $Stage 'defenseclaw-hook.exe') `
  -CLIBinary (Join-Path $Stage 'defenseclaw.exe') `
  -Config (Join-Path $Stage 'config.yaml') `
  -Manifest (Join-Path $Stage 'targets.yaml') `
  -AttestClaudeEffectivePolicy

The equivalent post-proof CLI transaction is:

& $DefenseClaw enterprise windows repair `
  --installer $Installer `
  --broker-binary (Join-Path $Stage 'defenseclaw-cmid-broker.exe') `
  --gateway-binary (Join-Path $Stage 'defenseclaw-gateway.exe') `
  --hook-binary (Join-Path $Stage 'defenseclaw-hook.exe') `
  --cli-binary $DefenseClaw `
  --config (Join-Path $Stage 'config.yaml') `
  --manifest (Join-Path $Stage 'targets.yaml') `
  --attest-agent-application-control `
  --attest-claude-effective-policy `
  --json

& $DefenseClaw enterprise windows status --installer $Installer --json
& $DefenseClaw enterprise windows verify --installer $Installer --json

That Repair revalidates the structural attestations, then binds the live Claude proof to the protected manifest hash. A manifest change invalidates it. Application-control evidence, a healthy Program Files policy, or a prior run against another manifest is never substituted for this gate.

-NoStart stages and verifies the transaction with all managed services disabled and stopped. Use it when endpoint management has a separate activation phase, then activate only by running the same complete public Repair transaction without -NoStart. Do not call Start-Service directly: that bypasses the queue-drain, fresh-guardian, and readiness gates and must fail closed. The public installer defaults to the service and path names shown above; the name/root override parameters are for isolated certification fixtures and must not be used to make parallel production installations. Both the installer and certification harness resolve their default roots through Windows known-folder APIs; changing process environment variables cannot redirect them.

-CertificationCodexHome is a harness-only unsigned-scope marker, not a fleet layout option or an implicit core-mode switch. The module accepts it only when both service names carry the same exact DefenseClawCert..._<10-lowercase-hex> identifier and the existing path has the exact .codex-defenseclaw-cert-<same-id> basename on a reparse-free, fixed NTFS volume. The certification harness imposes the tighter fixture precondition: it must be an initially absent direct child of the exact WTS-active profile and, after creation by that medium token, be owned by the manifest SID. Every unsigned certification lifecycle receives the switch as the exact non-production scope marker, including both the full and -ClaudeOnly profiles. The -ClaudeOnly profile still forbids application-control attestation. Only the disposable actual-Codex child may additionally receive the path as CODEX_HOME; it is never written to the machine environment or any managed service. Signed production lifecycle calls omit it. Production and certification service environments both omit CODEX_HOME; install, upgrade, repair, status, verify, rollback, and uninstall verify that absence.

-CoreHardeningCertification is the separate explicit core-only mode. It requires -AllowUnsigned plus the exact certification home, rejects production attestations and any Codex-enabled manifest, and is used only by the harness's -ClaudeOnly profile. Full unsigned certification supplies the same exact home but omits the core flag. Signed production and read-only lifecycle calls supply neither certification flag.

Before SCM starts the services, the installer:

  • copies binaries and policy through same-directory transactional replacement;
  • rejects a gateway, hook binary, config, or manifest reached through a reparse point or an untrusted writable path;
  • protects the binary, config, manifest, runtime, log, ledger, and deployment metadata DACLs according to their identities;
  • validates or securely creates C:\ProgramData\OpenAI\Codex as a shared machine-policy prerequisite. Secure creation supplies the protected DACL to CreateDirectoryW; an unsafe owner, DACL, file obstruction, or reparse point fails without ownership or ACL takeover;
  • creates DefenseClawGateway under its virtual service account and the credential broker, hook guardian, and hook enumerator under LocalSystem;
  • pins DEFENSECLAW_DEPLOYMENT_MODE=managed_enterprise, DEFENSECLAW_CONFIG, DEFENSECLAW_HOME, DEFENSECLAW_HOOK_GUARDIAN_AUTH_DIR, and the service role in the administrator-owned service environment;
  • pins optional approved-client evidence and required Claude-policy evidence when those claims have been established; it never emits CODEX_HOME;
  • configures automatic start and gateway recovery without granting standard users service stop, change-config, or delete rights;
  • launches elevated gateway validation children with an explicit environment allowlist and System32 working directory, dropping caller-controlled loader/profiler, compatibility, execution-policy preference, and unrelated process variables. Each public elevated CLI call atomically creates a 128-bit-random child under Windows Temp with a protected LocalSystem/Administrators-only owner and DACL, pins TEMP/TMP to that child rather than the shared writable parent, and removes it after the child exits;
  • verifies the persisted image paths, arguments, identities, start policy, environment, owners, DACLs, and hashes before reporting success.

The shared Codex parent is created only by explicit Install, Upgrade, or Repair. Installer Status, the gateway's normal per-user modes, and an enterprise-disabled status check do not create it. A failed lifecycle transaction removes only an exact, empty OpenAI or Codex directory that that transaction created. Pre-existing legitimate shared directories survive rollback and Uninstall -Purge; uninstall never claims shared vendor state.

The deployment-mode environment pin is part of the security boundary. A user setting DEFENSECLAW_DEPLOYMENT_MODE=unmanaged_byod, replacing a user config, or launching another copy cannot downgrade the managed services.

All managed services use exactly three SCM failure actions: restart after 5 seconds, 15 seconds, then 60 seconds, with a one-day failure-count reset period and non-crash failure handling enabled. Windows repeats the last configured action for subsequent failures, so the 60-second restart is the indefinite backstop; there is no terminal NONE action. The current broker-focused certification force-terminates the exact gateway, broker, and guardian PIDs four times and observes all four replacements, then issues a normal administrator Stop and requires each of those services to remain stopped for longer than 60 seconds. Because an already queued SCM restart cannot be canceled, servicing first persists its intent, disables all managed services, stops them, and holds the disabled state through a bounded 65-second drain. Activation then makes only the guardian demand-startable, requires a fresh successful reconcile while the gateway remains disabled, starts the gateway, proves live readiness, and finally restores automatic start. Any interrupted activation re-enters a fresh disable/stop/drain cycle. Planned maintenance stop must not race recovery.

Reconcile and verify Windows targets

Generic Windows user-footprint mutation has a stricter boundary than service lifecycle management. An elevated administrator can install, repair, upgrade, inspect, or remove the SCM deployment, but must not run enterprise hooks install, reconcile, watch, or generic user-footprint uninstall directly. Those operations run inside DefenseClawHookGuardian as LocalSystem. The guardian obtains the exact declared user's active WTS token, impersonates that user for profile writes, then reverts to LocalSystem before updating protected state. It fails closed when the SID has no matching active token; it never writes a target user's files under the LocalSystem identity merely by trusting a path string.

Run one explicit service-mediated reconcile before accepting the endpoint:

$Gateway = 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\bin\defenseclaw-gateway.exe'
$Manifest = 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\hook-guardian\targets.yaml'

& $Gateway enterprise windows reconcile --json
& $Gateway enterprise hooks status --manifest $Manifest --json
& $Gateway enterprise hooks verify --manifest $Manifest --json

enterprise windows reconcile restarts only the LocalSystem guardian and waits for its startup reconcile and coverage result. The equivalent installer operation is install-enterprise.ps1 -Action Reconcile. An elevated administrator may run the read-only enterprise hooks status and verify commands shown above. Their --json output contains an ok boolean and identifies the target or manifest. A partial guardian reconcile records every target result, makes status and verify return ok: false, and produces a non-zero operator result. status reports observed state without repairing it. verify revalidates service-owned inputs, authorization coverage, target runtime, hook command, native config, helpers, and tokens; unhealthy or incomplete coverage is a non-zero result, not a warning-only success.

Managed hook requests authenticate both content and server identity. The connector-scoped token cannot be used for management or another connector, and the connected loopback peer PID must equal the exact current SCM gateway PID. As a release gate, stop or crash the gateway, bind the exact API port from a standard-user process that returns syntactically valid allow JSON, and invoke the managed hook. Repeat while starting the gateway to exercise the bind race. Both calls must deny/fail closed, and the fake listener must observe zero authenticated requests. Release the fake listener before restoring service readiness.

The guardian service hosts the same hardened watch loop. It watches only the manifest and declared connector footprints, debounces filesystem activity, and also performs a periodic reconcile. After the first authorized success it repairs deleted or modified native hook entries, generated scripts/helpers, per-user scoped token sidecars, contract metadata, and supported native config. It does not follow a link target: a target-owned reparse point in a previously authorized footprint is removed and replaced with the canonical object, while foreign-owned objects, unsafe parents, path escapes, and untrusted DACLs remain hard failures. Every managed regular file must also have exactly one hard-link name. For a previously authorized target, a multi-link managed leaf is moved into its one bounded profile-side quarantine and recreated as a new single-link file; the outside hard-link name's bytes, owner, and DACL are not changed.

Managed target-owned leaves and directories have a protected DACL with exactly four allow principals: OWNER RIGHTS receives read-control; the exact target receives generic read/write/execute/delete (plus child delete on directories); LocalSystem and Administrators receive generic all. Files have four direct ACEs. Directories have seven ACEs: the target, LocalSystem, and Administrators each have one direct ACE and one object/container-inherit, inherit-only ACE, while OWNER RIGHTS remains direct-only. The profile root is never canonicalized. If an already-authorized, exact target-owned object has a legacy deny ACE for the target, LocalSystem, Administrators, or OWNER RIGHTS, the guardian repairs it by handle without following reparse points. Only its locked, short-lived repair thread enables SeBackupPrivilege and SeRestorePrivilege; it reverts immediately and never uses SeTakeOwnershipPrivilege or takes over a foreign owner.

First enrollment may canonicalize an ordinary exact-target-owned ACL under the target token, but it does not recreate a missing required native config and does not repair a multi-link obstruction. Those cases remain non-zero until an operator supplies a valid native config or resolves the unapproved obstruction.

No repair loop provides a zero-time guarantee. Measure and record the observed Windows recovery interval for every approved connector/version. Use AppLocker, WDAC, MDM, EDR, or filesystem policy when native config must be immutable rather than repair-enforced.

Verify Windows service hardening

Run these checks from an elevated support shell:

$GatewayService = 'DefenseClawGateway'
$BrokerService = 'DefenseClawCMIDBroker'
$GuardianService = 'DefenseClawHookGuardian'
$EnumeratorService = 'DefenseClawHookEnumerator'

Get-CimInstance Win32_Service -Filter "Name='$GatewayService' OR Name='$BrokerService' OR Name='$GuardianService' OR Name='$EnumeratorService'" |
  Select-Object Name, State, StartMode, StartName, PathName

sc.exe qc $GatewayService
sc.exe qc $BrokerService
sc.exe qc $GuardianService
sc.exe qc $EnumeratorService
sc.exe qfailure $GatewayService
sc.exe qfailure $BrokerService
sc.exe qfailure $GuardianService
sc.exe qfailure $EnumeratorService
sc.exe sdshow $GatewayService
sc.exe sdshow $BrokerService
sc.exe sdshow $GuardianService
sc.exe sdshow $EnumeratorService

Get-Acl 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\bin\defenseclaw-cmid-broker.exe' |
  Format-List Owner, Sddl
Get-Acl 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\bin\defenseclaw-gateway.exe' |
  Format-List Owner, Sddl
Get-Acl 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\etc\config.yaml' |
  Format-List Owner, Sddl
Get-Acl 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\hook-guardian\targets.yaml' |
  Format-List Owner, Sddl
Get-Acl 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\hook-guardian-state\protected_targets.json' |
  Format-List Owner, Sddl

Acceptance requires the exact virtual-account/LocalSystem identities, automatic start, approved absolute image paths and arguments, the managed-mode environment pin, protected DACLs with no standard-user write ACE, and a healthy JSON verify. Do not stop at the configured SCM RequiredPrivileges values: the current broker-focused certification opens the gateway, broker, and guardian live process tokens read-only and records TokenUser, integrity, privileges, groups, and restricted SIDs. The gateway must have the exact NT SERVICE\<gateway> TokenUser, high integrity, a restricted token containing its service SID, World, service-logon, and write-restricted SIDs, and only SeChangeNotifyPrivilege. The broker must have the LocalSystem TokenUser, system integrity, an unrestricted token with its service SID group, and only SeChangeNotifyPrivilege. The guardian must have the LocalSystem TokenUser, system integrity, an unrestricted token with its service SID group, and exactly SeTcbPrivilege, SeImpersonatePrivilege, SeChangeNotifyPrivilege, SeBackupPrivilege, and SeRestorePrivilege. Backup and Restore must be present-but-disabled in the idle guardian process token and enabled only on the dedicated DACL-repair thread. Neither token may retain Debug, TakeOwnership, AssignPrimaryToken, or CreateToken; the gateway must not retain Backup or Restore.

Certification also requires a standard user to receive access denied for process terminate, suspend/resume, VM operation/write, remote-thread creation, handle duplication, quota/information changes, process DACL/owner changes, and all-access handles. Dangerous service-token duplicate, impersonate, assign-primary, privilege/group/default adjustment, DACL, and owner handles must also be denied. Direct taskkill, protected-file write/DELETE handles, SCM mutation, and service-registry write attempts must fail; the original gateway, broker, and guardian PIDs must remain Running and installer verification must show those three services responsive. The Enumerator is still subject to exact-name collision refusal and uninstall/ fallback-cleanup absence checks, but its full token and recovery matrix is not claimed by this broker-focused run. Query-limited process access may remain available for diagnostics. The same user must be able to read/traverse the shared OpenAI\Codex parent but must be denied child creation, directory DELETE handles, and ACL changes. Do not infer safety from a service merely being Running.

Use the repository certification harness from 64-bit PowerShell 7 only, and only on a disposable Windows validation endpoint:

.\scripts\test-windows-enterprise-hardening.ps1 `
  -BrokerBinary .\defenseclaw-cmid-broker.exe `
  -ProviderLibrary 'C:\Program Files\Cisco\Cisco Secure Client\CM\<cm-version>\CMID\<cmid-version>\<arch>\cmidapi.dll' `
  -GatewayBinary .\defenseclaw-gateway.exe `
  -HookBinary .\defenseclaw-hook.exe `
  -CLIBinary .\defenseclaw.exe `
  -NormalModeCLILauncher C:\cert\python-cli\defenseclaw.exe `
  -NormalModeCLIWheel C:\cert\defenseclaw-0.8.0-py3-none-any.whl `
  -CodexBinary C:\cert\codex-0.144.3.exe `
  -ClaudeBinary C:\cert\claude-2.1.207.exe `
  -RejectedCodexBinary C:\cert\codex-0.130.0.exe `
  -RejectedClaudeBinary C:\cert\claude-2.1.151.exe `
  -UpgradeBrokerBinary .\v2\defenseclaw-cmid-broker.exe `
  -UpgradeGatewayBinary .\v2\defenseclaw-gateway.exe `
  -UpgradeHookBinary .\v2\defenseclaw-hook.exe `
  -UpgradeCLIBinary .\v2\defenseclaw.exe `
  -AllowUnsigned `
  -AttestAgentApplicationControl `
  -Execute `
  -DisposableHost

Without -Execute -DisposableHost, the harness is non-mutating and prints its unique service/user/path plan. In execution mode it creates uniquely named temporary services, one local non-admin denial user, short-lived interactive-token scheduled tasks, and guarded roots. The protected target must already own a WTS-active desktop session. The harness never asks for that user's password: it runs tamper probes in the existing token at limited run level and verifies the exact SID, medium integrity, and effective non-admin state. It snapshots and restores the user's canonical .defenseclaw tree. It never enumerates, snapshots, or mutates the potentially large live .codex. After the normal-mode no-op gate it creates an initially absent, target-owned <profile>\.codex-defenseclaw-cert-<id> direct child on the same fixed NTFS volume. A black-box, read-only candidate-binary probe uses hostile USERPROFILE config/hooks decoys and exact before/after inventories to prove that Codex honors CODEX_HOME without writes. The harness proves the machine, coordinator, and both service environments never receive that value. It is passed only to the disposable actual-Codex child. The guardian reports the protected machine requirements.toml, keeps per-SID DefenseClaw runtime under exact .defenseclaw, and never reads, snapshots, or patches live .codex. Cleanup removes only the absent-baseline alternate child. Before machine installation, the harness runs the clean committed Python CLI with the staged candidate gateway against an explicit unmanaged_byod config/data path, requires enterprise enabled: false, and proves no service or data/machine root was created and neither user tree changed. It later runs a disposable normal-mode setup, removes its owned hook registration, and requires the existing normal-mode auto-heal to restore the exact event/trusted-hash contract without changing unrelated settings. It cleans that fixture and proves enterprise-protected machine state did not change. All DefenseClaw-owned installer, module, and executable inputs are copied byte-stably into the administrator/System-only staging tree. The signed Cisco cmidapi.dll remains at its trusted Secure Client path; the harness requires that exact path to match the public CLI resolver and pins its signer and digest before lifecycle mutation. -AllowUnsigned does not relax either source-path boundary. It proves the standard-user denial and repair scenarios, including whole-root deletion, a previously authorized target-owned junction that must be repaired without touching its outside target, and a foreign-owned canonical junction that must fail closed. Outside trees are compared byte/attribute/timestamp/owner/DACL-exactly. It also runs a four-principal DACL test: medium-user deny-ACE attempts must be blocked, while preexisting target/SYSTEM/Administrators/OWNER RIGHTS deny ACEs must make status/verify unhealthy and then auto-heal to exact bytes, owner, and canonical DACL with Backup/Restore disabled again at idle. A separate hard-link case requires link count two to make status/verify unhealthy, then proves the managed name is recreated with a different one-link file identity while the outside identity, bytes, owner, and DACL remain exact and the bounded quarantine disappears. It also runs a poisoned-environment plan probe; securely creates and validates the shared Codex parent; proves transaction rollback, purge preservation, and owner/DACL/reparse refusal for that shared path; tests exact Codex machine policy deletion/event/DACL/state auto-heal; rejects an unregistered SID; binds loopback hook trust to the live SCM gateway PID during a fake-listener restart race; runs approved/old/unsigned client application-control probes and real Claude/Codex effective-policy invocations; records JSON evidence; and attempts bounded cleanup in finally. -AllowUnsigned is only for locally built certification binaries: the harness passes it only to install/upgrade/repair with its exact same-id names, roots, and CODEX_HOME and proves that production defaults and near misses are rejected. -ClaudeOnly additionally passes the explicit core-hardening flag; the full unsigned profile does not. Omit both certification flags when certifying signed release artifacts. Never aim the harness at the default production service names or roots, and never enable unsigned binaries for a production deployment.

The full certification profile can exercise both connectors through the shared defenseclaw-hook.exe without application control. Use -AttestAgentApplicationControl only when the enterprise has separately deployed and tested WDAC or AppLocker. For an intentionally Claude-only core run, -ClaudeOnly -AllowUnsigned still performs the real hostile Claude invocation, but deliberately leaves claude_effective_policy_verified=false, security_complete=false, and production_certified=false; its evidence cannot be promoted into a fleet attestation.

Windows repair, upgrade, and removal

For every lifecycle action, prepare a fresh release-specific protected staging directory and launch the staged installer through the fixed, clean Windows PowerShell engine described in the install section. Reassert the installed transaction and DACL/service contract:

$Windows = [Environment]::GetFolderPath([Environment+SpecialFolder]::Windows)
$PowerShell = Join-Path $Windows 'System32\WindowsPowerShell\v1.0\powershell.exe'
$Stage = 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw-Staging\<release-id>'
$Installer = Join-Path $Stage 'install-enterprise.ps1'
$ProviderLibrary = 'C:\Program Files\Cisco\Cisco Secure Client\CM\<cm-version>\CMID\<cmid-version>\<arch>\cmidapi.dll'

& $PowerShell -NoLogo -NoProfile -NonInteractive -File $Installer `
  -Action Repair `
  -BrokerBinary (Join-Path $Stage 'defenseclaw-cmid-broker.exe') `
  -ProviderLibrary $ProviderLibrary `
  -GatewayBinary (Join-Path $Stage 'defenseclaw-gateway.exe') `
  -HookBinary (Join-Path $Stage 'defenseclaw-hook.exe') `
  -CLIBinary (Join-Path $Stage 'defenseclaw.exe') `
  -Config (Join-Path $Stage 'config.yaml') `
  -Manifest (Join-Path $Stage 'targets.yaml')

Use -Action Upgrade with the complete approved binary/config/manifest set. Upgrade is transactional: validation or staging failures leave the previously verified deployment running; a failed activation reports failure and rolls back rather than publishing a mixed version as healthy. The disposable-host certification includes a protected non-loopback managed config that fails after the transaction snapshot and requires exact restoration of deployment hashes, SCM configuration/recovery/SDDL/environment, Running state, and guardian readiness. Always run installer Status, installer Verify, guardian status --json, guardian verify --json, and one allow/block contract check after upgrade.

The public CLI supports the same upgrade, but a CLI-replacing transaction must be launched by the new release's protected staged CLI, not by the executable currently mapped from the installed bin directory. This keeps the destination CLI image unlocked and makes the release being activated the transaction driver. The command rejects an installed-CLI self-replacement before mutation:

$ReleaseCLI = Join-Path $Stage 'defenseclaw.exe'
& $ReleaseCLI enterprise windows upgrade `
  --installer $Installer `
  --broker-binary (Join-Path $Stage 'defenseclaw-cmid-broker.exe') `
  --gateway-binary (Join-Path $Stage 'defenseclaw-gateway.exe') `
  --hook-binary (Join-Path $Stage 'defenseclaw-hook.exe') `
  --cli-binary $ReleaseCLI `
  --config (Join-Path $Stage 'config.yaml') `
  --manifest (Join-Path $Stage 'targets.yaml') `
  --json

Running the installed CLI is still valid for a broker/gateway/hook-only upgrade when --cli-binary is omitted.

Only an administrator can remove the service registration:

& $PowerShell -NoLogo -NoProfile -NonInteractive -File $Installer `
  -Action Uninstall

The preferred public CLI drives the same authenticated transaction. It may be run from the protected installed CLI; the uninstall result then reports a protected asynchronous retirement receipt while a fixed-System32 finalizer removes the running CLI's renamed install tree:

$InstalledCLI = 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\bin\defenseclaw.exe'
& $InstalledCLI enterprise windows uninstall `
  --installer 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\libexec\install-enterprise.ps1' `
  --json

# Approved decommission only:
& $InstalledCLI enterprise windows uninstall `
  --installer 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\libexec\install-enterprise.ps1' `
  --purge `
  --json

Default uninstall revokes targets, removes the services and installed binaries, and surgically removes DefenseClaw-owned Codex requirements/enrollment and Claude managed-hook wiring. It retains runtime, logs, guardian evidence, and deployment diagnostics for recovery. Use -Purge only through an approved decommission procedure:

& $PowerShell -NoLogo -NoProfile -NonInteractive -File $Installer `
  -Action Uninstall `
  -Purge

Neither removal form deletes C:\ProgramData\OpenAI or C:\ProgramData\OpenAI\Codex, even when DefenseClaw originally created the empty directories. They are shared vendor state. Remove them, if appropriate, only through a separate inventory-aware endpoint-management procedure. Likewise, removal never recursively deletes C:\Program Files\ClaudeCode or managed-settings.d. Ownership records and captured preimages permit only exact DefenseClaw leaves or values to be removed/restored. A current value that no longer matches the owned postimage is preserved and removal fails truthfully rather than overwriting a concurrent administrator edit.

Uninstall verifies that machine policy and every DefenseClaw-owned command reference are absent before it retires the Program Files tree. Close or restart Codex and Claude sessions as part of decommissioning. A process that was already running may have cached the old absolute Program Files hook command and can report a launch failure after that binary is removed; only a fresh process is guaranteed to reload the now-absent machine policy. This is deliberately different from ordinary per-user mode, whose stable launcher retains its existing disabled-tombstone no-op behavior. The structured uninstall result reports cached_enterprise_clients_require_reload: true and, for installed-CLI teardown, identifies the protected receipt, helper, isolated helper-environment root, and retired install root until finalization completes.

A protected user cannot make removal permanent by calling enterprise hooks uninstall, editing the manifest, forging protected_targets.json, changing the service environment, or deleting a user-owned hook: the LocalSystem-only mutation preflight and DACLs deny control-plane mutation, and the running guardian repairs the declared target. To decommission a generic user-footprint target, first remove or disable it in the protected manifest and run the service-mediated reconcile. Then remove the owned native entry through the connector's supported teardown or an endpoint-management action running with that user's token, and verify the remaining target set. Do not run generic teardown from an elevated administrator process and do not substitute LocalSystem path-string writes for target-user impersonation. The only direct elevated exception documented below is removal of an already-owned Claude Code machine-policy registration; direct Claude installation still requires the LocalSystem guardian because it also writes per-user runtime under exact target impersonation.

Native Windows Codex managed hooks

Windows enterprise Codex registration is machine policy, not a patch to <profile>\.codex\config.toml. The lifecycle owns:

C:\ProgramData\OpenAI\Codex\requirements.toml
C:\ProgramData\OpenAI\Codex\.defenseclaw-managed-hooks.state
C:\ProgramData\OpenAI\Codex\.defenseclaw-managed-hooks.lock
C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\install\codex-requirements-ownership.json
C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\install\codex-requirements-acl-backup.json

The canonical TOML sets allow_managed_hooks_only = true, enables hooks, pins the protected installed hook directory, and contains exactly these ten groups: SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, SubagentStart, SubagentStop, PreCompact, PostCompact, and Stop. Each command uses the fixed System32 Windows PowerShell path and the protected defenseclaw-hook.exe; user PATH, COMSPEC, user config, and project hooks do not select the managed command.

The hidden installed-binary commands are lifecycle internals and read-only operator diagnostics except for service-mediated reconcile/removal:

$Gateway = 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\bin\defenseclaw-gateway.exe'
& $Gateway enterprise windows codex-requirements verify --json
& $Gateway enterprise windows reconcile --json
& $Gateway enterprise hooks verify `
  --manifest 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\hook-guardian\targets.yaml' `
  --json

Do not call codex-requirements reconcile or remove from a standard-user binary or use these hidden commands as a substitute for the public lifecycle. The exact installed path, managed mode, protected metadata, administrator identity, and service environment are all revalidated.

Approved Codex must be at least 0.133.0. Application control restricts which client executable may run, while the protected machine policy points Codex to the shared defenseclaw-hook.exe and is verified by the guardian.

The guardian verifies and repairs exact policy bytes, event coverage, ownership, DACL, enrollment state, and preimage records. A missing event, deleted policy/state, or DACL drift makes status/verify non-zero until repair. An unregistered SID invoking the installed managed hook fails closed; normal unmanaged Codex setup and its existing per-user auto-heal are unaffected.

Codex policy transactions serialize through the protected .defenseclaw-managed-hooks.lock file. The implementation validates its administrator ownership, canonical DACL, no-reparse identity, and single link, then uses a bounded exclusive byte-range lock. It never opens the retired predictable Global\DefenseClaw-CodexRequirements-* mutex, so a standard user pre-creating that kernel object has no influence. A user can hold a read handle and temporarily contend for the real lock; verification fails closed after the bounded deadline and succeeds after release without publishing partial state.

Native Windows Claude Code managed hooks

Native Windows supports administrator-managed Claude Code hooks without writing ~/.claude/settings.json. Enrollment is service-mediated. Add the approved claudecode target to the protected guardian manifest, then run:

$Gateway = 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\bin\defenseclaw-gateway.exe'
$Manifest = 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\hook-guardian\targets.yaml'

& $Gateway enterprise windows reconcile --json
& $Gateway enterprise hooks verify --manifest $Manifest --json

Do not run enterprise hooks install --connector claudecode from an elevated administrator process. Managed Windows mutation preflight requires the LocalSystem guardian; that guardian impersonates the manifest's exact active target token for per-user runtime, reverts, and then publishes the administrator-owned machine policy.

The gateway and sibling defenseclaw-hook.exe must first be deployed in an Administrator-, LocalSystem-, or TrustedInstaller-owned machine path (normally under C:\Program Files) with no standard-user write ACEs. The per-user public installer under %LOCALAPPDATA% is deliberately rejected as an enterprise policy command source; elevating a user-writable executable would make the machine policy unsafe.

The guardian validates the target profile's exact SID, rejects service/system identities, unsafe DACLs, foreign owners, symlinks, junctions, and other reparse points, then writes the user's scoped runtime beneath <profile>\.defenseclaw. The native hook resolves that runtime from the invoking process SID; it does not use the administrator account's install data root or project-controlled environment. The administrator policy is published atomically as:

C:\Program Files\ClaudeCode\managed-settings.d\90-defenseclaw.json

The adjacent protected .defenseclaw-managed-hooks.state binds the exact policy digest, native hook executable, and allow-listed target SIDs. Claude loads hooks from this tier when allowManagedHooksOnly: true; no manual hook trust prompt is required. A managed invocation from a user not present in the protected SID allow-list fails closed with an enrollment diagnostic before it can use another target's credential. This is distinct from normal/unmanaged mode, whose existing per-user hook and auto-heal behavior is unchanged. Invalid or tampered ownership metadata also fails closed.

The Program Files Claude policy is machine-global whenever at least one managed Claude target is enabled. It can therefore be loaded by other interactive users on that endpoint; those users are not silently treated as unmanaged. Their hook invocation is rejected before credential lookup unless their exact SID is enrolled in protected state. Validate this negative case with a separate unregistered standard account during rollout.

Native Windows managed hooks deliberately pin their per-user runtime to <profile>\.defenseclaw. --data-dir may only repeat that exact path; custom DefenseClaw data directories are rejected because a standard-user hook cannot securely discover an administrator-selected redirect. The managed hook also ignores inherited DEFENSECLAW_HOME and CLAUDE_CONFIG_DIR. The latter may relocate Claude's user configuration, but it does not relocate the machine policy under C:\Program Files\ClaudeCode, so no user or project environment can redirect this managed registration.

DefenseClaw refuses to overwrite a foreign drop-in, an administrator-edited policy whose digest no longer matches its sidecar, disableAllHooks: true, or a file policy superseded by policyHelper. It also refuses installation when an HKLM Settings policy has higher precedence. In those environments, merge the DefenseClaw hook matrix through the active GPO/MDM/policy-helper source; local file installation cannot override it. Remotely delivered policy is an external management boundary and must likewise include the hook matrix at its authoritative source.

Claude managed-source precedence is server-managed, then HKLM/MDM, then the Program Files policy, then HKCU; the first authoritative source wins. Certification must run the real approved Claude client with hostile user and project disableAllHooks settings and observe a DefenseClaw managed hook or a blocked client operation. The existence, digest, and DACL of 90-defenseclaw.json are necessary but cannot by themselves prove effective runtime precedence.

After removing or disabling the row in the protected manifest and completing a service-mediated reconcile, an elevated administrator may remove one already-owned Claude machine-policy registration with the installed, administrator-protected gateway. This is narrowly allowed because the removal updates only authenticated machine-policy ownership state and leaves per-user runtime inert:

defenseclaw-gateway enterprise hooks uninstall `
  --user alice `
  --connector claudecode `
  --json

If the account and profile have already been removed, pass the recorded --sid S-1-5-21-... without --user or --user-home to remove the stale allow-list entry.

Cleanup verifies the protected digest and ownership metadata before mutation. It removes the machine drop-in after the last target and otherwise updates the SID allow-list atomically. Per-user runtime files remain as inert repair and forensic evidence; a removed SID is no longer activated by the shared policy. Re-running install repairs those files and re-registers the SID.

This endpoint hook-policy support does not turn the public Windows package into the private managed-enterprise distribution flavor or provide private CMID credentials; those release/provider boundaries remain unchanged.

Per-user hook guardian

The managed gateway service must not write directly to /home. To install or repair hook-native connectors for real interactive users, run the short-lived guardian command as an administrator:

Prepare the user profile

First installation is intentionally conservative. The guardian requires the native application config to exist and refuses to invent a profile for a user who has never run the agent.

ConnectorTypical native configPreparation check
Codex~/.codex/config.tomlLaunch Codex once as the user, then verify the file is user-owned and not group/other writable.
Claude Code~/.claude/settings.jsonLaunch Claude Code once as the user, then verify the file is user-owned and not group/other writable.
Other hook-native connectorsConnector-specific native hook configConsult the connector page and confirm the declared config surface exists.

Collect the exact version using the same binary the user invokes. In managed action mode, version information is part of the hook-contract decision:

sudo -u ubuntu -H /path/to/codex --version
sudo -u alice -H /path/to/claude --version

sudo -u ubuntu -H stat -c '%U:%G %a %n' /home/ubuntu/.codex/config.toml
sudo -u alice -H stat -c '%U:%G %a %n' /home/alice/.claude/settings.json

Do not copy a root-owned placeholder into the user's profile. The native config should be owned by the target user; the guardian validates that ownership and then patches only the connector-owned entries.

Bootstrap one target

sudo DEFENSECLAW_CONFIG=/etc/defenseclaw/config.yaml \
  /opt/defenseclaw/bin/defenseclaw-gateway enterprise hooks install \
  --user ubuntu \
  --connector codex \
  --agent-version "codex-cli 0.142.0" \
  --json

Use --user-home, --uid, and --gid only for directory-backed or nonstandard identity systems where normal user lookup is unavailable. Keep --data-dir, --api-addr, and --proxy-addr at their managed-config defaults unless the packaged deployment intentionally uses different paths or ports.

The guardian command:

  • targets an explicit user via --user or --user-home;
  • rejects proxy/plugin connectors;
  • refuses first-time install when the agent hook config file is missing;
  • rejects hook config paths outside the target home, including unsafe symlink escapes;
  • writes a raw connector-scoped hook token, not the gateway administrator token, into the user's hook runtime;
  • preserves the user's ownership on patched hook files and per-user hook scripts;
  • applies the same action-mode hook-contract validation as gateway startup.

The corresponding service-side token remains in the managed runtime and is validated for owner, mode, path, ACL/DACL, and symlink/reparse safety before authentication. The per-user sidecar uses the same raw token format. Generated hooks prefer the scoped sidecar over an inherited generic gateway token, so a stale shell environment cannot silently cross the connector boundary.

Authorization and readiness

A successful privileged reconcile writes two different records:

RecordTrustPurpose
<data_dir>/hook_guardian_state.jsonService/runtime statusLast run, manifest, target counts, and per-target errors displayed by defenseclaw status.
DEFENSECLAW_HOOK_GUARDIAN_AUTH_DIR/protected_targets.jsonRoot-owned authorizationDurable list of successfully protected targets trusted by managed readiness.

The gateway does not treat the service-writable status file as authorization. Managed hook enforcement is reported ready only when the trusted authorization record covers every configured target. Partial coverage remains starting/non-enforcing and includes an actionable health hint.

The guardian units set NoNewPrivileges=false deliberately. Each reconcile temporarily adopts the target user's effective UID/GID so connector files are created with the user's ownership, then returns to root to process the next allow-listed target. Linux otherwise prevents the long-lived guardian from regaining its bounded CAP_SETUID after that credential drop. The units still limit capabilities to CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_FOWNER, CAP_SETGID, and CAP_SETUID, retain the @system-service syscall filter, deny SUID/SGID creation, and restrict writable paths to user homes and the two guardian state directories.

For scheduled repair, create /etc/defenseclaw/hook-guardian/<user>.env:

DEFENSECLAW_GUARDIAN_CONNECTOR=codex
DEFENSECLAW_GUARDIAN_AGENT_VERSION="codex-cli 0.142.0"

Then run the template unit:

sudo systemctl start defenseclaw-hook-guardian@ubuntu.service

The template unit is useful for a single user/connector bootstrap. For ongoing fleet management, prefer the manifest-driven reconcile and watcher because they produce one aggregate state and authorization record.

Run one guardian invocation per user and connector. The template intentionally has narrower scope than a full root shell: it runs as root so it can repair user-owned files, sets UMask=0077, and limits its capability bounding set to ownership, DAC file access, and the credential drop used for per-user setup (CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_FOWNER, CAP_SETGID, CAP_SETUID). It needs write access to user homes so it can repair hook files and to /var/lib/defenseclaw so the shared CLI startup can open the audit DB and write guardian state. Do not remove ProtectHome=true from the always-on gateway service to solve per-user hooks.

For periodic multi-user repair, create an explicit allow-list at /etc/defenseclaw/hook-guardian/targets.yaml:

version: 1
targets:
  - user: ubuntu
    connector: codex
    agent_version: "codex-cli 0.142.0"
  - user: ubuntu
    connector: claudecode
    agent_version: "2.1.187 (Claude Code)"
  - user: alice
    connector: claudecode
    agent_version: "Claude Code v2.1.154"
  - user: disabled-user
    connector: codex
    enabled: false

The manifest parser rejects unknown fields, unsupported versions, duplicate YAML documents, and incomplete enabled targets. user_home, uid, gid, and data_dir are available for controlled nonstandard layouts. enabled: false keeps a target in source control without reconciling it; removing or disabling a target stops future repair but does not automatically uninstall existing native hook entries.

Then run reconcile manually, enable the event-driven watcher, and keep the timer as a periodic backstop:

sudo DEFENSECLAW_CONFIG=/etc/defenseclaw/config.yaml \
  /opt/defenseclaw/bin/defenseclaw-gateway enterprise hooks reconcile \
  --manifest /etc/defenseclaw/hook-guardian/targets.yaml \
  --json

sudo systemctl enable --now defenseclaw-hook-guardian-watch.service
sudo systemctl enable --now defenseclaw-hook-guardian.timer

The manifest is an administrator-owned allow-list. The guardian does not enumerate every local user by default, so it will not write into service accounts, stale profiles, or users outside the enterprise policy.

defenseclaw-hook-guardian-watch.service watches only directories derived from the manifest and each connector's declared hook/runtime footprint. When a user edits or deletes a watched hook config, hook script, token sidecar, or generated helper, the watcher debounces the event and re-runs the same hardened reconcile path. First-time installs still refuse symlinks, bad owners, group/other-writable files, and missing app configs. After a target has a root-owned authorization record, the watcher may remove target-owned symlinks and normalize target-owned modes before reinstalling the canonical footprint; foreign-owned paths and unsafe parent directories remain hard failures. The timer keeps a periodic repair backstop for missed filesystem events, reboot catch-up, and deployments that choose not to run the watcher.

The watcher ignores its own lock-file housekeeping, and the installer preserves unchanged bytes, ownership, modes, backups, contract timestamps, and mtimes. A settled target should therefore remain quiet instead of entering a self-generated fsnotify loop.

Repair semantics

ConditionFirst installPreviously authorized repair
Native config missingRefuseRefuse unless the connector's declared repair contract permits recreating a previously managed surface.
Path escapes the target homeRefuseRefuse.
Parent is unsafe or foreign-ownedRefuseRefuse.
Target-owned symlink in managed footprintRefuseRemove the symlink and install the canonical file; do not modify the symlink target.
Target-owned group/other-writable modeRefuseNormalize to the expected mode, then reinstall.
Target-owned SUID/SGID/sticky bitsRefuse or fail validationRemove special bits through exact mode normalization.
Foreign-owned hook/config fileRefuseRefuse; authorization never grants permission to replace another principal's object.
Unchanged canonical fileInstall onceNo-op; preserve mtime and avoid watcher churn.

Failure and availability behavior

  • A malformed, unreadable, or untrusted service-side scoped token makes that connector's guardrail health error and rejects hook authentication.
  • Connector-scoped token failure is isolated: a Codex token problem does not authorize or invalidate Claude Code's route.
  • Delivery, missing-token/authentication, and invalid-response failures follow the connector's effective hook fail mode. Fresh configuration defaults to fail closed; a migrated installation can retain an explicit fail-open setting. DEFENSECLAW_STRICT_AVAILABILITY=1 additionally forces the transport and missing-token subset closed, but is not a durable anti-tamper control if the user can change its own environment.
  • A user-owned token or hook may be changed before the guardian repairs it. Use endpoint controls when the required property is prevention rather than bounded recovery.

Each reconcile run writes /var/lib/defenseclaw/hook_guardian_state.json. defenseclaw status reads that file and reports the last manifest path, run time, target counts, and per-user repair failures under Hook guardian.

macOS LaunchDaemon layout

The macOS design uses two privilege domains — mirroring the Linux table above but collapsed to root for the daemon side, because the managed cloud auth provider requires root to read and re-perm its on-disk credential store. There is no dedicated defenseclaw service user on macOS.

ComponentIdentityWritable scopePurpose
Gatewayroot (uid 0)/opt/cisco/secureclient/defenseclaw/runtime, /Library/Logs/Cisco/SecureClient/DefenseClawInspection, policy, audit, hook API, and health.
Hook guardianroot (uid 0), same daemon-level authorityAllow-listed user homes plus guardian/runtime stateInstall and repair native user hooks after trusted path validation.
AI agentInteractive standard userIts normal user profileInvoke native hooks; cannot manage the LaunchDaemon or administrator assets.

macOS launchd does not offer the fine-grained sandbox directives systemd exposes (ProtectSystem, ProtectHome, capability bounding set, syscall filters, etc.), so isolation is enforced through the surrounding endpoint boundary — code-signing/notarization policy, MDM file ownership and ACL enforcement, launchd system-domain control, Endpoint Security/EDR, and restricted local-admin membership — instead of by narrowing the daemon's process privileges. Both packaged plists deliberately omit UserName and GroupName so launchd runs the daemons as root by default; the installer refuses to load a plist that pins them to any other identity.

Install the binary under /opt/cisco/secureclient/defenseclaw/bin, create the managed config and guardian manifest under /opt/cisco/secureclient/defenseclaw/etc, then install both packaged LaunchDaemons. The managed config must set data_dir: "/opt/cisco/secureclient/defenseclaw/runtime" so mutable service state stays separate from the administrator-owned config.

Prepare the administrator-approved config and guardian manifest outside the managed destination. From the extracted DefenseClaw release archive, run:

sudo ./packaging/launchd/install-enterprise.sh \
  --binary ./defenseclaw \
  --config /path/to/approved-config.yaml \
  --manifest /path/to/approved-targets.yaml

The installer is idempotent: running it twice on the same host reconciles the machine-wide layout in place rather than refusing on existing state. It refuses symlinked sources or destinations, verifies the root-owned macOS system path chain, unloads any existing DefenseClaw jobs (including pre-Cisco-path legacy labels), relocates legacy directories under /Library/Logs/Cisco/SecureClient/DefenseClaw/ with a timestamped .pre-<version>-<timestamp> suffix (rather than deleting them), and installs through same-directory temporary files. Runtime state (audit.db, judge_bodies.db, device.key, and the hook-guardian authorization directory) is preserved across reinstalls; user-scope ~/.defenseclaw/ is left untouched and reconciled by the hook-guardian daemon on its 60 s tick. Before launchd is allowed to start either job, it verifies these postconditions:

PathOwnerMode
/opt/cisco/secureclient/defenseclawroot:wheel0755
/opt/cisco/secureclient/defenseclaw/bin/defenseclaw-gatewayroot:wheel0755
/opt/cisco/secureclient/defenseclaw/etcroot:wheel0755
/opt/cisco/secureclient/defenseclaw/etc/config.yamlroot:wheel0640
/opt/cisco/secureclient/defenseclaw/runtimeroot:wheel0750
/opt/cisco/secureclient/defenseclaw/hook-guardianroot:wheel0750
/opt/cisco/secureclient/defenseclaw/hook-guardian/targets.yamlroot:wheel0640
/opt/cisco/secureclient/defenseclaw/hook-guardian-stateroot:wheel0750
/Library/Logs/Cisco/SecureClient/DefenseClawroot:wheel0750
/Library/LaunchDaemons/com.cisco.secureclient.defenseclaw*.plistroot:wheel0644

Before replacing files, the installer snapshots the existing deployment and records which LaunchDaemons are loaded. A failure after jobs are stopped restores the previous binary, config, manifest, and plists, then re-bootstraps only the jobs that were loaded before the attempt.

Use --no-start when MDM or a package transaction must install and verify the files before a separate orchestration step loads the jobs. Even in that mode, an already loaded DefenseClaw job is unloaded before replacement and remains stopped until the administrator starts it.

Both LaunchDaemons run as root. /opt/cisco/secureclient/defenseclaw/hook-guardian-state is the authorization-record directory selected by DEFENSECLAW_HOOK_GUARDIAN_AUTH_DIR; it is distinct from hook_guardian_state.json, which the guardian writes under the configured data_dir. The hook guardian remains a separate periodic job so its lifecycle stays independent of the gateway. Both jobs pin DEFENSECLAW_HOME, DEFENSECLAW_CONFIG, WorkingDirectory, and Umask=077, and the installer loads both jobs in the system domain unless --no-start is selected.

The packaged macOS guardian runs a reconcile every 300 seconds. It is a periodic repair backstop, not the Linux fsnotify watcher. For a shorter repair window or prevention, deploy an MDM/EDR policy that monitors or locks the native agent config and DefenseClaw hook footprint.

Verify the system-domain jobs and filesystem trust:

sudo launchctl print system/com.cisco.secureclient.defenseclaw
sudo launchctl print system/com.cisco.secureclient.defenseclaw.hook-guardian
sudo stat -f '%Su:%Sg %OLp %N' \
  /opt/cisco/secureclient/defenseclaw/bin/defenseclaw-gateway \
  /opt/cisco/secureclient/defenseclaw/etc/config.yaml \
  /Library/LaunchDaemons/com.cisco.secureclient.defenseclaw.plist \
  /Library/LaunchDaemons/com.cisco.secureclient.defenseclaw.hook-guardian.plist
sudo lsof -nP -iTCP:18970 -sTCP:LISTEN
sudo tail -n 100 /Library/Logs/Cisco/SecureClient/DefenseClaw/gateway.err.log
sudo tail -n 100 /Library/Logs/Cisco/SecureClient/DefenseClaw/hook-guardian.err.log

macOS does not apply the Linux systemd sandbox directives. Use code-signing/notarization policy, MDM file ownership and ACL enforcement, launchd system-domain control, Endpoint Security/EDR, and restricted local-admin membership as the surrounding endpoint boundary.

Security behavior

Managed mode changes the runtime contract:

  • config.yaml is the only live configuration source.
  • The gateway validates managed config ownership and permissions before loading.
  • /v1/guardrail/config PATCH is rejected in managed mode; administrators edit the managed config file. Hot reload applies the explicitly reloadable observability, sink, webhook, notification, and identity-metadata fields. Connector topology, gateway listeners, deployment/storage identity, scanners, policies, and other substantive runtime changes require a service restart; gateway.config_reload.mode: restart makes that the default for managed edits.
  • Standard users cannot stop a system service, edit root-owned binaries, or write to administrator-owned config and policy files.
  • Hook credentials written into user-space agent configs must be scoped to hook submission only; never use a machine admin token in user-readable hook files.
  • The gateway service sandbox intentionally prevents broad host mutation. Per-user hook installation and repair belong to the guardian, MDM, or endpoint-management layer.
  • User-owned native agent config files are repair-enforced, not immutable. The event-driven guardian closes the normal edit/delete window by repairing watched tamper events quickly, and the timer provides a periodic backstop. If your deployment requires prevention rather than repair, pair DefenseClaw with an endpoint-control mechanism that can lock the native agent config surface.

Restart-required controls

Managed hot reload is intentionally narrow. Observability destinations and explicitly reloadable metadata may update without restart. Connector topology, listeners, deployment/storage identity, scanners, policies, and other substantive enforcement controls require a service restart. This prevents the process from entering a mixed state where policy says one thing but already-started connector guards enforce another.

Use the platform's administrator channel.

On Linux, edit the root-owned config and restart the system services:

sudoedit /etc/defenseclaw/config.yaml
sudo systemctl restart defenseclaw-gateway.service
sudo systemctl start defenseclaw-hook-guardian.service

On macOS, edit the root-owned config, kick-start the gateway LaunchDaemon, and run the guardian LaunchDaemon immediately instead of waiting for its next periodic reconcile:

sudoedit /opt/cisco/secureclient/defenseclaw/etc/config.yaml
sudo launchctl kickstart -k system/com.cisco.secureclient.defenseclaw
sudo launchctl kickstart -k system/com.cisco.secureclient.defenseclaw.hook-guardian

On Windows, deliver the approved config through the enterprise installer so it can replace and re-verify the protected file transaction, then restart both SCM services:

# These three variables (also used in the install and verify blocks) MUST be
# defined in the same session before running this block — copying just the
# invocation without them fails with an empty -File argument. Resolve
# powershell.exe through [Environment]::GetFolderPath('System') rather than
# $env:SystemRoot so an elevated shell that inherited a poisoned SystemRoot
# cannot redirect this launch to an attacker-controlled powershell.exe
# before install-enterprise.ps1's own checks run.
$SystemFolder = [Environment]::GetFolderPath('System')
if ([string]::IsNullOrWhiteSpace($SystemFolder)) {
    throw 'GetFolderPath(System) returned empty; refusing an unrooted powershell.exe launch'
}
$PowerShell = [IO.Path]::Combine(
    $SystemFolder,
    'WindowsPowerShell\v1.0\powershell.exe'
)
# Fail closed on any anomaly: an unrooted path or a missing target would let
# a same-directory powershell.exe hijack the launch.
if (-not [IO.Path]::IsPathRooted($PowerShell) -or -not (Test-Path -LiteralPath $PowerShell -PathType Leaf)) {
    throw "resolved PowerShell is not a rooted existing file: $PowerShell"
}
$Installer = 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\install-enterprise.ps1'
$Stage = 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw'

& $PowerShell -NoLogo -NoProfile -NonInteractive -File $Installer `
  -Action Repair `
  -GatewayBinary (Join-Path $Stage 'defenseclaw-gateway.exe') `
  -HookBinary (Join-Path $Stage 'defenseclaw-hook.exe') `
  -CLIBinary (Join-Path $Stage 'defenseclaw.exe') `
  -Config (Join-Path $Stage 'config.yaml') `
  -Manifest (Join-Path $Stage 'targets.yaml')

Restart-Service DefenseClawGateway
Restart-Service DefenseClawHookGuardian

Do not use the user CLI or HTTP PATCH as an enterprise automation shortcut; managed mode rejects that path by design.

Automatic application protection

Automatic application protection remains hook-native only. First activation requires a real hook config surface, such as an existing ~/.codex/config.toml.

In managed enterprise mode, automatic multi-user hook installation is intentionally fail-safe inside the gateway service. Leave application_protection.enabled: false in managed service configs and use the guardian command or endpoint-management tooling for user hooks. The gateway service account is not the Codex/Claude Code user, and it usually has a nologin shell; installing hooks into the service account's home does not protect real interactive users.

Expected managed behavior:

  • Discover supported apps for each real login user.
  • Skip users without an existing hook config surface.
  • Patch eligible user hook files through the privileged guardian.
  • Preserve original file owner, group, and mode.
  • Store authoritative application-protection state under the managed data directory.
  • Report per-user hook drift and repair failures in defenseclaw status.

Per-user hook connectors

The hardened gateway runs at system scope, but Codex, Claude Code, Cursor, and similar agents still read hook configuration from the interactive user's home directory. A working enterprise deployment therefore has two layers:

  1. The system service owns /etc/defenseclaw/config.yaml, /opt/defenseclaw/bin, and /var/lib/defenseclaw.
  2. A privileged installer or endpoint-management policy writes the hook entry into each targeted user's native agent config, such as /home/alice/.codex/config.toml.

Do not use the service account's home directory as the hook target for user protection. The hook script may live in the user's DefenseClaw home or another managed per-user location, but it must post only to the local gateway hook endpoint using a hook-scoped token. The gateway accepts that token only for the matching connector hook/notify routes; it does not authorize status, policy reload, config mutation, scans, or other sidecar APIs. Standard users can still edit files they own; preventing hook removal requires the enterprise guardian, MDM profile, filesystem ACL policy, or equivalent endpoint-control mechanism.

Day-2 operations

Add a user or connector

  1. Install and initialize the native agent as the user.
  2. Record the exact binary path and version.
  3. Add one manifest target per user/connector pair.
  4. Run manual reconcile with --json.
  5. Confirm the target appears in both guardian status and trusted coverage.
  6. Exercise one allow and one deterministic block event.

Multiple connectors for the same user must have separate manifest entries. They share the hook directory but use .hook-<connector>.token credentials, so setup or rotation for one connector does not overwrite another connector's credential.

Change an agent version

Update agent_version in the root-owned manifest before or with the endpoint agent upgrade. Run a manual reconcile and review hook-contract output. In action mode, unknown contract drift is a hard error unless an administrator explicitly opts into exploratory drift. Do not set DEFENSECLAW_ALLOW_HOOK_CONTRACT_DRIFT=1 as a persistent fleet default.

Remove a user from management

Removing or disabling a manifest target stops future reconciliation; it is not an uninstall command. Before removing the target:

  1. Use the connector's supported teardown or your endpoint-management package to remove DefenseClaw-owned native entries.
  2. Verify the native config no longer references DefenseClaw.
  3. Remove or disable the manifest entry.
  4. Reconcile the remaining manifest and confirm trusted coverage contains only intended targets.
  5. Remove the local account through normal identity lifecycle management.

Upgrade DefenseClaw

Use an atomic administrator-controlled replacement and restart both trust domains. On Windows, use the enterprise installer's -Action Upgrade transaction described above; do not replace a running service binary in place.

sudo install -o root -g root -m 0755 defenseclaw-gateway \
  /opt/defenseclaw/bin/defenseclaw-gateway.new
sudo mv /opt/defenseclaw/bin/defenseclaw-gateway.new \
  /opt/defenseclaw/bin/defenseclaw-gateway
sudo systemctl daemon-reload
sudo systemctl restart defenseclaw-gateway.service
sudo systemctl start defenseclaw-hook-guardian.service

Re-run status, an allow/block contract check, and the service-sandbox inspection after every upgrade. If packaging units changed, install the new units before daemon-reload and compare local overrides deliberately.

Respond to guardian failures

Do not repeatedly chmod or chown a failing target until you understand the trust error. Common causes are:

SymptomLikely causeAdministrator action
manifest trust check failedManifest or ancestor is writable/untrusted, symlinked, or has unsafe ownershipRestore root ownership and strict modes; replace symlinked paths with real administrator-owned files.
owner uid does not match targetConfig/hook was copied by root or another userDetermine provenance; restore the correct target owner only if the file is legitimate.
group/other writable on first installNative config or hook surface is unsafeRemove extra write bits before the first authorized install.
hook contract driftAgent version differs from the declared/known contractPin the real version, update DefenseClaw if support exists, or keep the target out of action mode.
HTTP 401 from one hookScoped token missing, malformed, unreadable, or driftedInspect service health and scoped-token trust without printing the token; reconcile after restoring trusted mode/owner.
One connector fails while another worksExpected connector credential isolationRepair only the failing connector; do not replace all credentials with a shared token.
Watcher repeatedly reconciles unchanged filesNon-canonical external writer, lock noise, or version mismatchInspect journal events and mtimes; confirm the current binary includes idempotent reconciliation fixes.

Incident containment

If an AI agent obtains root/admin privilege, treat the endpoint as compromised. Collect service/unit hashes, managed config, guardian authorization, audit logs, and EDR telemetry; isolate the endpoint; restore from a trusted package; rotate relevant external credentials; and re-attest the machine. A successful root attacker can remove any local control, including DefenseClaw.

Verify

sudo systemctl --no-pager --full status defenseclaw-gateway.service
sudo systemctl --no-pager --full status defenseclaw-hook-guardian-watch.service
sudo systemctl --no-pager --full status defenseclaw-hook-guardian.timer
sudo env DEFENSECLAW_CONFIG=/etc/defenseclaw/config.yaml \
  DEFENSECLAW_HOME=/var/lib/defenseclaw \
  /opt/defenseclaw/bin/defenseclaw status
sudo env DEFENSECLAW_CONFIG=/etc/defenseclaw/config.yaml \
  /opt/defenseclaw/bin/defenseclaw-gateway enterprise hooks reconcile \
  --manifest /etc/defenseclaw/hook-guardian/targets.yaml \
  --json

On Windows, use the matching managed-service and guardian checks:

Get-Service DefenseClawGateway, DefenseClawCMIDBroker, DefenseClawHookGuardian, DefenseClawHookEnumerator

# Define $PowerShell and $Installer alongside the block-local $Gateway and
# $Manifest so this section can be copied on its own without depending on
# earlier sections. See the install block above: resolve powershell.exe
# through the known-folder API so a poisoned $env:SystemRoot cannot redirect
# the launch, and fail closed if the lookup returns an empty/relative path.
$SystemFolder = [Environment]::GetFolderPath('System')
if ([string]::IsNullOrWhiteSpace($SystemFolder)) {
    throw 'GetFolderPath(System) returned empty; refusing an unrooted powershell.exe launch'
}
$PowerShell = [IO.Path]::Combine(
    $SystemFolder,
    'WindowsPowerShell\v1.0\powershell.exe'
)
if (-not [IO.Path]::IsPathRooted($PowerShell) -or -not (Test-Path -LiteralPath $PowerShell -PathType Leaf)) {
    throw "resolved PowerShell is not a rooted existing file: $PowerShell"
}
$Installer = 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\install-enterprise.ps1'
$Gateway = 'C:\Program Files\Cisco\Cisco Secure Client\DefenseClaw\bin\defenseclaw-gateway.exe'
$Manifest = 'C:\ProgramData\Cisco\Cisco Secure Client\DefenseClaw\hook-guardian\targets.yaml'
& $Gateway enterprise hooks status --manifest $Manifest --json
& $Gateway enterprise hooks verify --manifest $Manifest --json
& $PowerShell -NoLogo -NoProfile -NonInteractive -File $Installer `
  -Action Verify

Run full status from an administrator shell or from a support account that is allowed to read the managed config. Standard users should not need read access to secret-bearing /etc/defenseclaw/config.yaml; expose limited health through your endpoint-management tooling if users need self-service visibility. Configuration and service management require administrator privileges.

Expected evidence:

  • gateway service active under the dedicated service identity;
  • hook API listening only on loopback;
  • managed deployment mode visible in service environment and status;
  • guardrail running, connector state healthy, and enforcement enabled where configured;
  • guardian_verified: true only after trusted target coverage;
  • every enabled manifest target successful in guardian state;
  • service-side scoped token files mode 0600 on UNIX or protected by a Windows DACL, and not readable by protected users;
  • administrator-owned config, binary, service definitions, manifest, and authorization ledger unchanged by unprivileged tamper attempts;
  • user-owned hook tamper repaired within the organization's measured and accepted recovery window;
  • no continuing reconciles after the repaired footprint settles.

Treat this evidence as a release gate for the managed endpoint image. Run destructive tamper scenarios only in a dedicated validation environment with an independent administrator recovery channel.

Production checklist

  • AI agent users are standard users without passwordless sudo/admin rights.
  • Broker, gateway/guardian/enumerator host, and native hook binaries come from the approved build and are administrator-owned; the broker is bound to the approved signed Cisco provider library.
  • Managed config, policy, units/plists/SCM definitions, manifest, and authorization directory have trusted path chains.
  • Gateway service runs as the dedicated service identity (or the documented root identity on macOS), not the AI user.
  • Linux gateway sandbox properties match the packaged unit or a reviewed stricter override.
  • Guardian targets are current and authorized: Linux/macOS manifests contain only explicit approved pairs; Windows enumerator output contains only expected interactive SIDs, configured connector families that pass the Windows managed-hook filter, and discovered supported versions.
  • First reconcile succeeds for every enabled target before action mode is enabled.
  • Watcher and periodic backstop are enabled according to OS capabilities.
  • Scoped token files are connector-specific; no automation copies a shared gateway token into user hooks.
  • Status and central monitoring alert on guardian errors, connector errors, repeated repair, and enforcement downgrade.
  • Allow/block tests and adversarial tamper tests are recorded for the deployed version.
  • MDM/EDR controls cover native config immutability if bounded repair is not sufficient.
  • Upgrade and incident-response procedures use an administrator channel unavailable to the AI agent.