Enterprise

Configure a standalone deployment

Where the standalone enterprise config lives on Windows, Linux and macOS, the keys every config needs, how to choose the AI agents to protect, every enterprise setting with its default, and how the network proxy works.

A standalone deployment reads one administrator-owned config.yaml per host. The keys are the same on every OS. The lifecycle validates the whole file before it changes anything: an invalid config is refused, and the host keeps running the config it already has.

This page is the reference for that file. For the terms used here, see Concepts.

Where the config lives

OSPathWho can change itHow a change takes effect
Linux/etc/defenseclaw/config.yamlroot. The installed file is 0640, owner root, group defenseclaw.defenseclaw-enterprise-apply.path watches the file and runs ensure --reason path.
macOS/opt/cisco/defenseclaw/etc/config.yamlroot. The installed file is 0640, owner root, group _defenseclaw.The com.cisco.defenseclaw.apply LaunchDaemon watches the file and runs ensure --reason path.
WindowsC:\ProgramData\Cisco\DefenseClaw\etc\config.yamlAdministrators. Standard users cannot read the state root.Nothing watches the file. Run ensure again with the new file, as in Change the config.

Linux and macOS. The apply job also runs when a file directly inside the secrets or policies directory next to the config is added, removed or replaced. Changes in subdirectories do not trigger it; run ensure yourself. The lifecycle takes its config from the first of these that exists:

  1. The file named by --config (an absolute path).
  2. The file already at the path in the table. An MDM can stage it there before the package runs.
  3. The built-in default: observe mode with no agents selected, so it protects nothing.

The package ships no config. The lifecycle does not check who owns a file you stage yourself, so write it as root and keep it unwritable by other users. The MDM wrapper refuses a config file that a non-administrator can write (mdm_untrusted_input); see Deliver the config.

Windows. Supply the file to ensure (the CLI's --config, Setup's CONFIG= or the wrapper's -ConfigPath). Ensure compares it with the installed copy and reapplies it when it differs. Do not edit the installed copy in place: ensure only compares the file you supply.

Required keys

Set these keys in every standalone config. On Linux and macOS the lifecycle fills in deployment_mode and enterprise.profile when they are missing and refuses conflicting values:

KeyLinuxmacOSWindows
config_version888
deployment_modemanaged_enterprisemanaged_enterprisemanaged_enterprise
enterprise.profilestandalonestandalonestandalone
data_dirExactly /var/lib/defenseclaw, or unset (the lifecycle then uses that path)Exactly /opt/cisco/defenseclaw/runtime, or unsetLeave unset. The services set it.
gateway.api_bind127.0.0.1, or unset127.0.0.1, or unset127.0.0.1, or unset
gateway.api_port18970, or unset18970, or unset18970, or unset
guardrail.rule_pack_dirUnset, "", or a custom packUnset, "", or a custom packUnset, "", or a custom pack
  • enterprise.profile. Always set it. When it is unset, Windows and macOS resolve to the Secure Client profile, and the enterprise policy commands refuse to run. The profile cannot change without a reinstall.
  • data_dir. On Linux and macOS the service sandbox allows writes only there, so the lifecycle refuses any other value.
  • policy_dir. On Linux and macOS a config that leaves it out uses the root-owned folder of policies DefenseClaw ships (/opt/defenseclaw/share/policies on Linux, /opt/cisco/defenseclaw/share/policies on macOS), never a folder inside data_dir, because the gateway loads its policies from there. Set it to /etc/defenseclaw/policies (Linux) or /opt/cisco/defenseclaw/etc/policies (macOS), as the minimal configs do, to keep your own rule packs there.
  • API listener. The gateway API is 127.0.0.1:18970 on every OS. Every OS refuses any bind address other than 127.0.0.1; Linux and macOS also refuse any other port.
  • guardrail.rule_pack_dir. Set it to "" to select the rule packs built into the gateway. A non-empty value names a rule-pack directory, which only administrators may be able to write.
    • Linux and macOS: when you leave the key out, the gateway uses <policy_dir>/guardrail/default if you created that folder, and otherwise the default pack DefenseClaw ships (/opt/defenseclaw/share/policies/guardrail/default on Linux, /opt/cisco/defenseclaw/share/policies/guardrail/default on macOS), so a config that leaves the key out installs on a fresh host. The lifecycle records the pack the config resolves to: when you create <policy_dir>/guardrail/default later, the next ensure applies the change and restarts the gateway with that pack. Setting rule_pack_dir to the folder you want is clearer and has the same effect. The lifecycle refuses a value that names a folder that does not exist, or one inside data_dir. See Custom rule packs.
    • Windows: when you leave the key out, the gateway uses <policy_dir>/guardrail/default if you set policy_dir outside data_dir, and otherwise the built-in packs. Setup loads every rule pack the config names before it changes anything, and refuses with 1639 a directory that does not exist or that the gateway cannot load.

The whole enterprise block is refused unless deployment_mode is managed_enterprise. In the Secure Client profile the block may contain only profile.

Choose the agents to protect

List each agent under guardrail.connectors. The key is the connector name; the value can be empty ({}) or override guardrail settings for that agent. A config without this map protects nothing.

config.yaml (excerpt)
guardrail:
  enabled: true
  mode: observe          # observe | action (default observe)
  connectors:
    codex: {}            # machine policy
    claudecode: {}       # machine policy
    cursor: {}           # machine policy
    copilot: {}          # machine policy
    devin: {}            # per user
    opencode:
      mode: action       # this agent blocks; the others only observe
  • guardrail.mode. observe inspects every call and logs the verdict without blocking. action blocks what policy denies. The default is observe. A connector's own mode overrides the global value for that agent.
  • Routes. Each connector is protected in one of three ways. See Machine policy for the full table.
RouteConnectorsWhat DefenseClaw changes
Machine policycodex, claudecode, cursor, copilot, and opencode through its managed pluginThe agent's machine-wide policy file, which standard users cannot edit
Per userdevin, antigravity, hermes, amp on every OS; openhands, omnigent, kiro on Linux and macOS onlyEach enrolled user's own agent config, which the guardian repairs
ACPkiro on WindowsEnrolled with defenseclaw-gateway enterprise acp, not through this map; see ACP guard. ACP stays available for Kiro on Linux and macOS too

openclaw and zeptoclaw need the guardrail proxy, which a managed host does not run, so they are not managed: Linux and macOS skip them (route unsupported), and Windows writes no targets for them.

Per-user enrollment also depends on the enrollment settings and on a supported version of the agent being installed for that user.

Leave an agent out entirely to leave it unprotected

On Linux and macOS, the lifecycle publishes machine policy for Codex, Claude Code, Cursor, Copilot and OpenCode when the connector name appears anywhere under guardrail.connectors, even with enabled: false, or under enterprise.machine_policy.connectors. There, enabled: false stops only per-user enrollment. Copilot's and OpenCode's machine policy follow the same rule on Windows. Windows Codex, Claude Code and Cursor policy is written only while the target manifest has an enabled row for that agent. To stop publishing machine policy for an agent, remove its name from both maps, or set enterprise.machine_policy.connectors.<name>.ownership: "off".

The older single-agent key guardrail.connector also selects an agent. Use the map instead.

Settings reference

This table lists every key under enterprise. An unset key takes the default shown. "Linux and macOS" means the key has no effect on Windows.

KeyValuesDefaultApplies onWhat it does
profilestandalone, secure_clientLinux: standalone. Windows and macOS: secure_clientAllSelects the profile. Must match the profile the services were installed with. Set standalone.
inspection.ai_defense.enabledtrue, falsefalseAllAdds Cisco AI Defense to the local engine. See AI Defense key.
inspection.ai_defense.credentialA credential name: lowercase letters, digits and dashes, starting with a letter or digit, up to 63 charactersNoneAllNames the stored key. Required when enabled is true.
enrollment.modeauto, manifestautoAllmanifest stops the enumerator; you write the target manifest yourself. On Windows the first install then needs --manifest (Setup: MANIFEST=).
enrollment.include_usersList of user namesEmptyAll, with different meaningsLinux and macOS: also enroll these accounts. Windows: accepted, but every profile is already a candidate, so it changes nothing.
enrollment.exclude_usersList of user names (Linux and macOS: or numeric uids)EmptyAllNever enroll these accounts. Wins over every include.
enrollment.include_groupsLinux and macOS: group names or numeric IDs. Windows: group names or SIDsEmptyAllWhen not empty, enroll only members of these groups. Windows covers local, Active Directory and Microsoft Entra ID groups; see Enrollment.
enrollment.exclude_groupsAs include_groupsEmptyAllNever enroll members. Wins over include_groups.
enrollment.exempt_usersList of user names (Linux and macOS: or numeric uids)EmptyAll, with different meaningsLinux and macOS: not enrolled, but their calls are allowed, inspected and logged. Windows: the machine-policy agents keep their rows, so the user is inspected; per-user agents get no new rows.
enrollment.unenrolled_usersinspect, denyinspectLinux and macOSHow the gateway treats a user without an enrollment who calls a machine-policy agent's hook.
enrollment.rootinspect, deny, exemptinspectLinux and macOSHow the gateway treats calls from uid 0. exempt is accepted and currently behaves like inspect.
enrollment.uid_minInteger, 0 or more0Linux and macOSLowest uid the enumerator considers. 0 means Linux reads UID_MIN from /etc/login.defs (1000 if absent) and macOS uses 501.
enrollment.uid_maxInteger, 0 or more, not below uid_min0Linux and macOSHighest uid enrolled, for local and directory accounts. 0 means login.defs UID_MAX bounds only local accounts in /etc/passwd, so directory accounts above it are still enrolled.
enrollment.home_rootsList of absolute directoriesEmptyLinux and macOSExtra parents of home directories, beyond /home and /var/home (Linux) or /Users (macOS).
enrollment.agent_prefixesList of absolute directories outside homes and temporary directoriesEmptyLinux and macOSExtra administrator install prefixes (for example an npm --prefix) where the enumerator and guardian look for agent CLIs.
machine_policy.default.ownershipmerge, verify_only, offmergeLinux and macOS; Copilot, OpenCode and the Claude Code version floor on WindowsWhether DefenseClaw writes its entries, only checks them, or leaves the agent's machine policy alone.
machine_policy.default.managed_hooks_onlyenforce, preserveenforceCodex and Claude Code; see note for WindowsSets the vendor lock that stops user and project hooks.
machine_policy.default.foreign_hooksremove, report, allowremoveAllWhat the foreign-hook guard does with unapproved hooks.
machine_policy.default.higher_precedence_sourcesfail, warnfailmacOS (Codex, Claude Code); Windows (Claude Code, in policy show and verify only)Whether a policy source that outranks DefenseClaw's file counts as a coverage failure or only a warning.
machine_policy.default.allowed_hooksList of SHA-256 digests, 64 hex characters, with or without sha256:EmptyAllHooks the foreign-hook guard approves.
machine_policy.connectors.<name>.*The same five keysInheritAs aboveOverrides for one connector. Each key falls back to default, then to the built-in value. allowed_hooks lists are merged.
machine_policy.connectors.claudecode.version_floorenforce, report, offenforceAllWhether DefenseClaw sets Claude Code's requiredMinimumVersion to its lowest verified hook contract while no other source sets it. Only connectors.claudecode takes this key; it does not inherit from default. See Claude Code version floor.
trust.modeauthenticode, hash_pinnedNoneWindowsauthenticode refuses any hash-pinned (unsigned) payload. hash_pinned, "" or unset admits both. See Payload trust.
trust.allowed_signersList of SHA-256 certificate thumbprintsEmptyWindowsAccept only payloads signed by these certificates, like --allowed-signer.
coexistence.per_user_installmigrate, block, ignoreNoneReservedReserved: accepted and validated, no effect in this release.
coexistence.disable_self_updatetrue, falsetrueWindowsSets the Windows DisableSelfUpdate policy value. See below.
network.https_proxyhttp://host[:port] or https://host[:port]EmptyAllProxy for outbound HTTPS. See Network proxy.
network.no_proxyComma-separated hosts, domains, IP addresses or CIDR rangesEmptyAllDestinations that bypass the proxy.

Payload trust

How the lifecycle verifies the payload is chosen at the entry point:

Entry pointTrust modeAllowed signers
Windows CLI (enterprise windows)--trust-mode authenticode (default) or hash_pinned, with --payload-manifest for hash_pinned--allowed-signer <thumbprint>, repeatable
Standalone SetupA signed build uses Authenticode. An unsigned build uses hash_pinned against the pins Setup writes from its embedded manifest.ALLOWEDSIGNERS= (comma-separated)
Windows MDM wrapper-TrustMode HashPinned (default) or Authenticode-AllowedSigners
Linux and macOS MDM wrapper--trust-mode hash_pinned (default, with --sha256) or signedmacOS, .pkg only: --allowed-team-id. Linux: --gpg-keyring with --signature or --signature-url.

On Windows, enterprise.trust in the config then narrows what the standalone lifecycle accepts. It is read from the config you supply, or, for an install, upgrade, repair or ensure without one, from the installed config:

  • mode: authenticode admits only Authenticode-signed payloads. The lifecycle refuses with 1639, before any change, a hash-pinned run (such as the unsigned DefenseClawSetup-Enterprise-Standalone-x64.exe, which passes --trust-mode hash_pinned), and any install, upgrade, repair or ensure over a deployment that was installed hash-pinned. Such a deployment keeps admitting its installed payload by the SHA-256 pins it recorded, so it stays hash-pinned. To move it to authenticode, remove it with uninstall --purge and install a signed Setup.
  • mode: hash_pinned, like an unset mode, is the minimum: it admits the unsigned payload by its pins, and a signed payload is still verified by its signature. It does not refuse a signed Setup. Use it, or leave mode unset, while you deploy unsigned builds. An empty mode: "" is the same as an unset mode.
  • allowed_signers restricts signed payloads to the listed signer certificates, like --allowed-signer. When both are given they must list the same thumbprints, or the run stops with 1639.

Linux and macOS accept and validate enterprise.trust but do not use it: their MDM wrapper checks the package (table above).

disable_self_update

enterprise.coexistence.disable_self_update controls only the Windows HKLM\SOFTWARE\Policies\Cisco\DefenseClaw\DisableSelfUpdate policy value. When the key is true and the value is absent, the lifecycle sets it to 1 and records that it did. It removes the value later only if it set it, so an existing Group Policy value is never changed. The value also stops the per-user install.ps1 on that computer.

Setting the key to false does not bring back update notices or defenseclaw upgrade on any OS. On every host with a managed deployment the update notice stays off, and the per-user installer, the per-user upgrade and rollback commands and a per-user gateway refuse to run, whatever this key says. See Per-user installs.

managed_hooks_only on Windows

Codex always gets its lock on Windows, whatever the key says. Claude Code's Windows drop-in follows the key as on Linux and macOS: enforce, the default, sets allowManagedHooksOnly: true, and preserve leaves the lock out. The key also decides whether the foreign-hook guard covers Codex and Claude Code, on Windows as elsewhere. See Machine policy.

Custom rule packs

The gateway ships three rule packs, default, strict and permissive, under /opt/defenseclaw/share/policies/guardrail on Linux and /opt/cisco/defenseclaw/share/policies/guardrail on macOS. To use your own rules on Linux and macOS:

  1. Create the pack in the administrator policy folder, as root: /etc/defenseclaw/policies/guardrail/<name> on Linux, /opt/cisco/defenseclaw/etc/policies/guardrail/<name> on macOS. Start from a copy of a shipped pack. Keep every file and folder owned by root, not writable by group or others, and readable by the gateway (for example 0644 files in 0755 folders). The lifecycle refuses a pack inside data_dir, which the gateway can write.

  2. Name the folder for the block threshold you want. The pack folder's name selects the thresholds: strict blocks findings of MEDIUM severity and above, permissive only CRITICAL ones, and any other name, default included, CRITICAL ones.

  3. Validate the pack with the gateway's own loader:

    sudo /opt/defenseclaw/bin/defenseclaw-gateway rulepack validate --dir /etc/defenseclaw/policies/guardrail/strict

    On macOS the binary is /opt/cisco/defenseclaw/bin/defenseclaw-gateway. --json prints a versioned result. Enterprise packages ship no defenseclaw CLI, so defenseclaw guardrail validate-pack and defenseclaw setup guardrail from the per-user workflow are not available.

  4. Point the config at it with guardrail.rule_pack_dir (or guardrail.connectors.<name>.rule_pack_dir for one agent) and apply the config with ensure --config or through your MDM. The lifecycle refuses a rule_pack_dir that does not exist and restarts the services with the new pack. If the gateway cannot load it, ensure fails and the previous config stays in force.

To change a pack that is in use, the safest route is the same as for a new one: create the edited copy in a new folder that keeps the threshold name (for example /etc/defenseclaw/policies/guardrail-v2/strict), validate it, and switch rule_pack_dir to it, so ensure applies it with rollback. If you edit the files in place instead, validate them and restart the managed gateway: ensure does not track the files under policies, and the gateway reads the pack only when it starts.

Network proxy

enterprise.network sets an outbound proxy for the gateway.

config.yaml (excerpt)
enterprise:
  network:
    https_proxy: http://proxy.corp.example.com:3128
    no_proxy: .corp.example.com,10.0.0.0/8

With https_proxy set, the gateway reaches Cisco AI Defense, LLM providers (the judge and the guardrail proxy), the LLM passthrough, webhooks, the remote model router and the telemetry exporters (OTLP over HTTP and gRPC, Splunk HEC, HTTP JSONL) through the proxy. It opens an HTTP CONNECT tunnel to the destination host name, so the proxy's host rules apply and TLS stays end to end. The gateway's other HTTPS clients take the proxy from HTTPS_PROXY, https_proxy, NO_PROXY and no_proxy:

OSWhere those variables are set
LinuxThe gateway unit (drop-in 70-defenseclaw-network.conf)
macOSThe gateway LaunchDaemon's environment
WindowsThe gateway sets them in its own process when it starts
  • https_proxy must be http:// or https:// with a host and optional port. A user name, password, path or query is refused.
  • LLM provider calls need an http:// proxy URL. With an https:// URL the gateway refuses those calls rather than connecting directly; the other destinations work with either.
  • no_proxy lists hosts, domains and CIDR ranges that connect directly. Loopback destinations always connect directly.
  • Webhook, passthrough and telemetry destinations still pass the gateway's address check first, so their host names must resolve on the device.
  • The gateway reads enterprise.network only when it starts. A config reload that changes it is reported as restart-required, and the gateway keeps the previous proxy until it restarts. On Linux and macOS, a lifecycle run that applies the changed config restarts the services.
  • When https_proxy is empty, the AI Defense client uses the process environment's proxy variables, if any.
  • The MDM wrapper's --https-proxy (Linux and macOS) is used only to download the package. It does not configure the gateway.

Observability credentials

A telemetry destination under observability.destinations often needs a credential, such as a Galileo API key or a Splunk HEC token. In the standalone profile, store it as a protected credential, like the AI Defense key, and reference it by name. Do not reference an environment variable: the gateway service does not get the variables of an administrator's shell, so {env: NAME}, token_env and bearer_env do not work in this profile.

Destination fieldCredential referenceReplaces
headers.<name> (otlp, http_jsonl){credential: NAME}{env: NAME}
token_credential (splunk_hec)NAMEtoken_env
bearer_credential (http_jsonl)NAMEbearer_env

NAME follows the credential name rules of the AI Defense key: lowercase letters, digits and dashes, starting with a letter or digit. A destination sets either the credential field or the environment field, not both.

  1. Store the credential, the same way as the AI Defense key (see Store the key for macOS, Windows and --from-file). On Linux:

    read -rs GALILEO_API_KEY
    printf '%s' "$GALILEO_API_KEY" | sudo /opt/defenseclaw/bin/defenseclaw-gateway enterprise secret set --name galileo-api-key --from-stdin
    unset GALILEO_API_KEY

    enterprise secret set needs an installed deployment. On a new host, install with a config that does not reference the credential yet, or with the destination set to enabled: false, which resolves no credentials.

  2. Reference it in the config and apply the config as usual:

    config.yaml (excerpt)
    observability:
      destinations:
        - name: galileo
          kind: otlp
          preset: galileo
          protocol: http/protobuf
          endpoint: https://api.galileo.ai/otel/traces
          headers:
            Galileo-API-Key: {credential: galileo-api-key}
            project: defenseclaw
            logstream: production

The gateway reads the credential the same way as the AI Defense key: through systemd LoadCredential= on Linux with systemd 247 or later, through its group from the root-owned file on older Linux and on macOS, and as its service identity on Windows. The value never appears in the config, the effective configuration, status output or logs.

Before it applies a config, the lifecycle checks that every credential an enabled destination references is stored and passes the file checks in Where the key is stored. Otherwise it refuses the config, names the credential and the destination field, and keeps the running config. enterprise secret remove likewise refuses a credential that an enabled destination of the installed config still references, because the gateway could not start without it: remove the reference and apply the config first. To rotate a credential, run enterprise secret set again with the same name. Outside the standalone profile a credential reference never resolves, so a config with an enabled destination that uses one is refused.

Sample configs

Each sample is an observe-mode starter: it inspects Codex, Claude Code, Cursor and Copilot (and OpenCode on Linux and macOS), logs every verdict and does not block on policy verdicts. Change guardrail.mode to action when you are ready to block.

/etc/defenseclaw/config.yaml
config_version: 8
deployment_mode: managed_enterprise
data_dir: /var/lib/defenseclaw          # must be exactly this path
policy_dir: /etc/defenseclaw/policies
gateway:
  api_bind: 127.0.0.1
  api_port: 18970
guardrail:
  enabled: true
  mode: observe                         # observe logs verdicts; action blocks
  rule_pack_dir: ""                     # the built-in rule packs
  connectors:                           # the agents to protect
    codex: {}
    claudecode: {}
    cursor: {}
    copilot: {}
    opencode: {}
enterprise:
  profile: standalone
  inspection:
    ai_defense:
      enabled: false                    # true after you store the key
      credential: ai-defense-api-key
  enrollment:
    mode: auto
    exclude_users: []
    unenrolled_users: inspect
    root: inspect
  machine_policy:
    default:
      ownership: merge
      managed_hooks_only: enforce
      foreign_hooks: remove
      higher_precedence_sources: fail
  coexistence:
    disable_self_update: true
  network:
    https_proxy: ""
    no_proxy: ""
/opt/cisco/defenseclaw/etc/config.yaml
config_version: 8
deployment_mode: managed_enterprise
data_dir: /opt/cisco/defenseclaw/runtime    # must be exactly this path
policy_dir: /opt/cisco/defenseclaw/etc/policies
gateway:
  api_bind: 127.0.0.1
  api_port: 18970
guardrail:
  enabled: true
  mode: observe                             # observe logs verdicts; action blocks
  rule_pack_dir: ""                         # the built-in rule packs
  connectors:                               # the agents to protect
    codex: {}
    claudecode: {}
    cursor: {}
    copilot: {}
    opencode: {}
enterprise:
  profile: standalone                       # macOS otherwise means Secure Client
  inspection:
    ai_defense:
      enabled: false                        # true after you store the key
      credential: ai-defense-api-key
  enrollment:
    mode: auto
    exclude_users: []
    unenrolled_users: inspect
    root: inspect
  machine_policy:
    default:
      ownership: merge
      managed_hooks_only: enforce
      foreign_hooks: remove
      higher_precedence_sources: fail
  coexistence:
    disable_self_update: true
  network:
    https_proxy: ""
    no_proxy: ""
C:\ProgramData\Cisco\DefenseClaw\etc\config.yaml
config_version: 8
deployment_mode: managed_enterprise
gateway:
  api_bind: 127.0.0.1
  api_port: 18970
guardrail:
  enabled: true
  mode: observe                         # observe logs verdicts; action blocks
  rule_pack_dir: ""                     # the built-in rule packs
  connectors:                           # the agents to protect
    codex: {}
    claudecode: {}
    cursor: {}
    copilot: {}
enterprise:
  profile: standalone                   # Windows otherwise means Secure Client
  inspection:
    ai_defense:
      enabled: false                    # true after you store the key
      credential: ai-defense-api-key
  enrollment:
    include_users: []                   # empty: every eligible profile
    exclude_users: []
  machine_policy:
    default:
      foreign_hooks: remove
  coexistence:
    disable_self_update: true

On Windows, keep the file as your input to ensure, not as a hand-edited copy under C:\ProgramData. Deliver it as shown in Deliver the config.

Next steps