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
| OS | Path | Who can change it | How a change takes effect |
|---|---|---|---|
| Linux | /etc/defenseclaw/config.yaml | root. 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.yaml | root. The installed file is 0640, owner root, group _defenseclaw. | The com.cisco.defenseclaw.apply LaunchDaemon watches the file and runs ensure --reason path. |
| Windows | C:\ProgramData\Cisco\DefenseClaw\etc\config.yaml | Administrators. 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:
- The file named by
--config(an absolute path). - The file already at the path in the table. An MDM can stage it there before the package runs.
- 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:
| Key | Linux | macOS | Windows |
|---|---|---|---|
config_version | 8 | 8 | 8 |
deployment_mode | managed_enterprise | managed_enterprise | managed_enterprise |
enterprise.profile | standalone | standalone | standalone |
data_dir | Exactly /var/lib/defenseclaw, or unset (the lifecycle then uses that path) | Exactly /opt/cisco/defenseclaw/runtime, or unset | Leave unset. The services set it. |
gateway.api_bind | 127.0.0.1, or unset | 127.0.0.1, or unset | 127.0.0.1, or unset |
gateway.api_port | 18970, or unset | 18970, or unset | 18970, or unset |
guardrail.rule_pack_dir | Unset, "", or a custom pack | Unset, "", or a custom pack | Unset, "", or a custom pack |
enterprise.profile. Always set it. When it is unset, Windows and macOS resolve to the Secure Client profile, and theenterprise policycommands 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/policieson Linux,/opt/cisco/defenseclaw/share/policieson macOS), never a folder insidedata_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:18970on every OS. Every OS refuses any bind address other than127.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/defaultif you created that folder, and otherwise the default pack DefenseClaw ships (/opt/defenseclaw/share/policies/guardrail/defaulton Linux,/opt/cisco/defenseclaw/share/policies/guardrail/defaulton 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/defaultlater, the nextensureapplies the change and restarts the gateway with that pack. Settingrule_pack_dirto 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 insidedata_dir. See Custom rule packs. - Windows: when you leave the key out, the gateway uses
<policy_dir>/guardrail/defaultif you setpolicy_diroutsidedata_dir, and otherwise the built-in packs. Setup loads every rule pack the config names before it changes anything, and refuses with1639a directory that does not exist or that the gateway cannot load.
- Linux and macOS: when you leave the key out, the gateway uses
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.
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 observeguardrail.mode.observeinspects every call and logs the verdict without blocking.actionblocks what policy denies. The default isobserve. A connector's ownmodeoverrides the global value for that agent.- Routes. Each connector is protected in one of three ways. See Machine policy for the full table.
| Route | Connectors | What DefenseClaw changes |
|---|---|---|
| Machine policy | codex, claudecode, cursor, copilot, and opencode through its managed plugin | The agent's machine-wide policy file, which standard users cannot edit |
| Per user | devin, antigravity, hermes, amp on every OS; openhands, omnigent, kiro on Linux and macOS only | Each enrolled user's own agent config, which the guardian repairs |
| ACP | kiro on Windows | Enrolled 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.
| Key | Values | Default | Applies on | What it does |
|---|---|---|---|---|
profile | standalone, secure_client | Linux: standalone. Windows and macOS: secure_client | All | Selects the profile. Must match the profile the services were installed with. Set standalone. |
inspection.ai_defense.enabled | true, false | false | All | Adds Cisco AI Defense to the local engine. See AI Defense key. |
inspection.ai_defense.credential | A credential name: lowercase letters, digits and dashes, starting with a letter or digit, up to 63 characters | None | All | Names the stored key. Required when enabled is true. |
enrollment.mode | auto, manifest | auto | All | manifest stops the enumerator; you write the target manifest yourself. On Windows the first install then needs --manifest (Setup: MANIFEST=). |
enrollment.include_users | List of user names | Empty | All, with different meanings | Linux and macOS: also enroll these accounts. Windows: accepted, but every profile is already a candidate, so it changes nothing. |
enrollment.exclude_users | List of user names (Linux and macOS: or numeric uids) | Empty | All | Never enroll these accounts. Wins over every include. |
enrollment.include_groups | Linux and macOS: group names or numeric IDs. Windows: group names or SIDs | Empty | All | When not empty, enroll only members of these groups. Windows covers local, Active Directory and Microsoft Entra ID groups; see Enrollment. |
enrollment.exclude_groups | As include_groups | Empty | All | Never enroll members. Wins over include_groups. |
enrollment.exempt_users | List of user names (Linux and macOS: or numeric uids) | Empty | All, with different meanings | Linux 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_users | inspect, deny | inspect | Linux and macOS | How the gateway treats a user without an enrollment who calls a machine-policy agent's hook. |
enrollment.root | inspect, deny, exempt | inspect | Linux and macOS | How the gateway treats calls from uid 0. exempt is accepted and currently behaves like inspect. |
enrollment.uid_min | Integer, 0 or more | 0 | Linux and macOS | Lowest uid the enumerator considers. 0 means Linux reads UID_MIN from /etc/login.defs (1000 if absent) and macOS uses 501. |
enrollment.uid_max | Integer, 0 or more, not below uid_min | 0 | Linux and macOS | Highest 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_roots | List of absolute directories | Empty | Linux and macOS | Extra parents of home directories, beyond /home and /var/home (Linux) or /Users (macOS). |
enrollment.agent_prefixes | List of absolute directories outside homes and temporary directories | Empty | Linux and macOS | Extra administrator install prefixes (for example an npm --prefix) where the enumerator and guardian look for agent CLIs. |
machine_policy.default.ownership | merge, verify_only, off | merge | Linux and macOS; Copilot, OpenCode and the Claude Code version floor on Windows | Whether DefenseClaw writes its entries, only checks them, or leaves the agent's machine policy alone. |
machine_policy.default.managed_hooks_only | enforce, preserve | enforce | Codex and Claude Code; see note for Windows | Sets the vendor lock that stops user and project hooks. |
machine_policy.default.foreign_hooks | remove, report, allow | remove | All | What the foreign-hook guard does with unapproved hooks. |
machine_policy.default.higher_precedence_sources | fail, warn | fail | macOS (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_hooks | List of SHA-256 digests, 64 hex characters, with or without sha256: | Empty | All | Hooks the foreign-hook guard approves. |
machine_policy.connectors.<name>.* | The same five keys | Inherit | As above | Overrides 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_floor | enforce, report, off | enforce | All | Whether 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.mode | authenticode, hash_pinned | None | Windows | authenticode refuses any hash-pinned (unsigned) payload. hash_pinned, "" or unset admits both. See Payload trust. |
trust.allowed_signers | List of SHA-256 certificate thumbprints | Empty | Windows | Accept only payloads signed by these certificates, like --allowed-signer. |
coexistence.per_user_install | migrate, block, ignore | None | Reserved | Reserved: accepted and validated, no effect in this release. |
coexistence.disable_self_update | true, false | true | Windows | Sets the Windows DisableSelfUpdate policy value. See below. |
network.https_proxy | http://host[:port] or https://host[:port] | Empty | All | Proxy for outbound HTTPS. See Network proxy. |
network.no_proxy | Comma-separated hosts, domains, IP addresses or CIDR ranges | Empty | All | Destinations that bypass the proxy. |
Payload trust
How the lifecycle verifies the payload is chosen at the entry point:
| Entry point | Trust mode | Allowed signers |
|---|---|---|
Windows CLI (enterprise windows) | --trust-mode authenticode (default) or hash_pinned, with --payload-manifest for hash_pinned | --allowed-signer <thumbprint>, repeatable |
| Standalone Setup | A 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 signed | macOS, .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: authenticodeadmits only Authenticode-signed payloads. The lifecycle refuses with1639, before any change, a hash-pinned run (such as the unsignedDefenseClawSetup-Enterprise-Standalone-x64.exe, which passes--trust-mode hash_pinned), and any install, upgrade, repair orensureover 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 toauthenticode, remove it withuninstall --purgeand 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 leavemodeunset, while you deploy unsigned builds. An emptymode: ""is the same as an unset mode.allowed_signersrestricts signed payloads to the listed signer certificates, like--allowed-signer. When both are given they must list the same thumbprints, or the run stops with1639.
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:
-
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 example0644files in0755folders). The lifecycle refuses a pack insidedata_dir, which the gateway can write. -
Name the folder for the block threshold you want. The pack folder's name selects the thresholds:
strictblocks findings of MEDIUM severity and above,permissiveonly CRITICAL ones, and any other name,defaultincluded, CRITICAL ones. -
Validate the pack with the gateway's own loader:
sudo /opt/defenseclaw/bin/defenseclaw-gateway rulepack validate --dir /etc/defenseclaw/policies/guardrail/strictOn macOS the binary is
/opt/cisco/defenseclaw/bin/defenseclaw-gateway.--jsonprints a versioned result. Enterprise packages ship nodefenseclawCLI, sodefenseclaw guardrail validate-packanddefenseclaw setup guardrailfrom the per-user workflow are not available. -
Point the config at it with
guardrail.rule_pack_dir(orguardrail.connectors.<name>.rule_pack_dirfor one agent) and apply the config withensure --configor through your MDM. The lifecycle refuses arule_pack_dirthat does not exist and restarts the services with the new pack. If the gateway cannot load it,ensurefails 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.
enterprise:
network:
https_proxy: http://proxy.corp.example.com:3128
no_proxy: .corp.example.com,10.0.0.0/8With 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:
| OS | Where those variables are set |
|---|---|
| Linux | The gateway unit (drop-in 70-defenseclaw-network.conf) |
| macOS | The gateway LaunchDaemon's environment |
| Windows | The gateway sets them in its own process when it starts |
https_proxymust behttp://orhttps://with a host and optional port. A user name, password, path or query is refused.- LLM provider calls need an
http://proxy URL. With anhttps://URL the gateway refuses those calls rather than connecting directly; the other destinations work with either. no_proxylists 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.networkonly 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_proxyis 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 field | Credential reference | Replaces |
|---|---|---|
headers.<name> (otlp, http_jsonl) | {credential: NAME} | {env: NAME} |
token_credential (splunk_hec) | NAME | token_env |
bearer_credential (http_jsonl) | NAME | bearer_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.
-
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_KEYenterprise secret setneeds an installed deployment. On a new host, install with a config that does not reference the credential yet, or with the destination set toenabled: false, which resolves no credentials. -
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.
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: ""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: ""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: trueOn 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
Deploy on Linux with configuration management
Idempotent Ansible, Puppet, Chef, Salt and plain apt or dnf recipes that install, configure, verify, upgrade and remove the standalone DefenseClaw enterprise package on Linux.
Enrollment
How the standalone profile finds users on Windows, Linux and macOS, which enrollment settings each OS honors, how directory accounts, new users and deleted users are handled, and how to publish the target list yourself.