Admission and the block/allow list
How DefenseClaw decides whether a skill, MCP server or plugin may be installed and run. The admission block sets what each scan finding does, and asset_policy holds the only block and allow lists. Both live in config.yaml.
An admin config may contain at most 4 MiB of source text and 65,536 parsed
YAML nodes, including repeated asset_policy entries. Split or simplify
large lists before applying them.
Admission is the check DefenseClaw runs when a skill, an MCP server or a plugin
appears on a machine, or when you install one with the CLI. It answers one
question: may this asset be installed and run? Two sections of config.yaml
(config_version: 9) answer it, and nothing else does:
admission:says what a scan finding does. For each asset type it maps a severity to an action: block it, quarantine it, warn, or allow it.asset_policy:holds the block list and the allow list. A name you deny is blocked without a scan. A name you allow skips the scan.
This is one case of the rule that config.yaml is the only admin intent (see
Configuration: one source of truth). The
older homes of this policy are read once, by the version 9 migration, and are
then ignored (Secure Client devices keep the version 8 behavior):
policies/rego/data.json, the skill_actions, mcp_actions and
plugin_actions keys, and the operator rows of the audit database's actions
table. Upgrading from config_version 8
says exactly what moves. Quarantine, disable and the scan-verdict blocks are
runtime state, never policy.
How a verdict is reached
List view for small screens. Use the expand button to open the drawing.
- A skill, MCP serveror plugin appears
- checksOn a list?
- On a list?asset_policy rules
- denied ruleBlocked
- allowed ruleAllowed
- no ruleFirst party?
- First party?admission list
- matchesAllowed
- no matchScan, then apply
- Blockeddenied rule matched
- Scan, then applythe severity action
- decidesClean, warning,
- Allowedscan skipped
- Clean, warning,allowed or rejected
The check ends in one of six verdicts:
| Verdict | When | What happens |
|---|---|---|
blocked | A denied rule in asset_policy matches. | The install is refused before any scan. The install watcher quarantines the files of a skill or plugin that appeared anyway. Tool calls to a blocked MCP server are refused. |
allowed | An allowed rule matches; or a first-party entry matches while allow_list_bypass_scan is true; or there is no scan result and scan_on_install is false; or the finding's action is allow. | Nothing is enforced. |
scan | Nothing decided yet and scan_on_install is true. | The asset is scanned, then checked again with the scan result. |
clean | The scan found nothing. | Nothing is enforced. |
warning | The scan found something and the action is warn. | The finding is recorded as an install warning. The asset stays. |
rejected | The scan found something and the action blocks, or the scanner failed. | The action's install, file and runtime parts apply. A scanner that errors or exits non-zero is rejected, with install block, file quarantine and runtime disable: admission fails closed. |
Precedence, first match wins:
asset_policydenied:blocked. It beats every allow, including the first-party list.asset_policyallowed:allowed, no scan.admissionfirst_party_allow_list, whileallow_list_bypass_scanis true:allowed, no scan.- No scan result and
scan_on_install: false:allowed. Withscan_on_install: true:scan. - The scan result: the action for the worst severity, or the scanner-specific
action when
scanner_overrideshas one.
The MCP scanner does not connect to a server on a loopback, private,
link-local or multicast address, so the install scan of such a server fails
and the verdict is rejected (fail-closed). An allowed rule with the
name and url of the server (step 2) or scan_on_install: false for mcp
(step 4) admits one you trust; see
Servers on local and internal addresses.
Where it runs: the gateway's install watcher checks a skill, plugin or MCP
server when it appears in a watched directory, and the defenseclaw install
and scan commands run the same check. gateway.watcher.skill.take_action
(and the plugin / mcp equivalents, true by default) controls only
post-scan severity actions. With it false, those severity verdicts are
recorded without their configured block, quarantine or disable actions. An
explicit asset_policy.denied match and a scanner failure still fail closed:
the watcher blocks the install and quarantines skill or plugin files. Do not
use take_action: false as an observation-only install mode. When
take_action is turned back on, the watcher restarts, and its first rescan
runs admission again for each asset rejected while it was off and applies the
actions, as for a new install. The watcher-block row ends with rejected while take_action was off, enforced once it was turned on. This needs the
periodic rescan (watch.rescan_enabled, on by default).
Until then a managed computer's status and verify warn with
asset_rejected_not_enforced.
The admission block
admission: has one block per asset type: skill, mcp and plugin. There
is also a defaults block. Each asset type inherits defaults field by field:
a field you leave out of skill falls back to defaults, then to the built-in
value.
There is no admission.tool. Nothing installs a tool, so there is nothing to
scan; tools have name lists only.
admission:
defaults:
scan_on_install: true
allow_list_bypass_scan: true
skill:
actions:
critical: quarantine
high: quarantine
medium: warn
low: allow
info: allow
scanner_overrides:
skill-scanner:
medium: block
first_party_allow_list:
- name: acme-review
source_path_contains: [".acme/skills/acme-review"]
reason: first-party Acme skill
mcp:
actions:
high: block
medium:
install: none
file: none
runtime: disable
plugin:
allow_list_bypass_scan: falseProp
Type
Actions
An action is a shorthand or the exact triple it stands for. Use the triple only when no shorthand fits.
| Shorthand | install | file | runtime | Verdict |
|---|---|---|---|---|
block | block | none | disable | rejected |
quarantine | block | quarantine | disable | rejected |
warn | none | none | enable | warning |
allow | none | none | enable | allowed |
In a triple, install is block, allow or none; file is none or
quarantine; runtime is disable or enable. A finding is rejected
whenever install is block or runtime is disable. warn and allow do
the same thing to the asset; they differ only in the verdict they record. A
severity that no layer covers fails closed, as a block.
Quarantine moves the files of a skill or plugin into DefenseClaw's quarantine
area. An MCP server has no files to move, so for MCP block and quarantine
both mean: refuse its tool calls.
What applies if you set nothing
Each severity resolves on its own, first match wins:
admission.<type>.actions- For skills only, the scanner gate (below)
admission.defaults.actions- The built-in action
| Severity | Skill (default gate) | MCP server | Plugin |
|---|---|---|---|
critical | quarantine | quarantine | quarantine |
high | quarantine | quarantine | quarantine |
medium | warn | quarantine | warn |
low | allow | install none, file none, runtime disable | warn |
info | allow | warn | warn |
The built-in first-party entries are codeguard for skills, defenseclaw for
plugins, and none for MCP servers. Both names are DefenseClaw's own, so they
trust only DefenseClaw's own content; see First-party assets.
The scanner gate
A skill's actions derive from the skill scanner's own gate unless
admission.skill.actions sets them. scanners.skill_scanner.fail_on_severity
(default HIGH) and scanners.skill_scanner.review_queue_min (default
MEDIUM) give the rule: a finding at or above the gate quarantines, a finding
from the review minimum up to the gate warns, and anything lower is allowed.
Raising the gate in one place therefore changes what a skill scan does without
touching admission:.
defenseclaw config set scanners.skill_scanner.fail_on_severity CRITICAL
defenseclaw config set scanners.skill_scanner.review_queue_min LOW
defenseclaw config get admission.skill.actions --effectiveSet scanners.skill_scanner.fail_on_severity (config generation 6, sha256 295115627558).
Effective policy digest: sha256:37484d42a2a3b4428f4b592f5a33bdf27bfad89a8b81005534bc0e388bdef510
Set scanners.skill_scanner.review_queue_min (config generation 7, sha256 db97f7544649).
Effective policy digest: sha256:8247599d5d5d1b6a8b8dc84a1b9ff4c88669c653805559fa6889daf91a315f9f
(source: derived:scanners.skill_scanner)
critical: quarantine
high: warn
info: allow
low: warn
medium: warnThe gate shadows admission.defaults for skills
Only skills have a scanner gate, and it sits between admission.skill and
admission.defaults. So admission.defaults.actions never changes a skill's
actions: set admission.skill.actions for that. MCP servers and plugins have
no gate, so they do follow admission.defaults.actions.
A severity you set under admission.skill.actions wins for that severity only;
the others keep deriving from the gate:
defenseclaw config set admission.skill.actions.medium block
defenseclaw config get admission.skill.actions --effectiveSet admission.skill.actions.medium (config generation 4, sha256 621b671d0367).
Effective policy digest: sha256:995422aed39baf2e53ad6ac0f3b8e0a489696ea70e21009a04422d210c748fea
(source: config:admission.skill.actions)
critical: quarantine
high: quarantine
info: allow
low: allow
medium: blockThe generation numbers and digests in these examples differ on your machine.
The (source: ...) line goes to stderr and prints first; it names the
highest-priority layer that set any of the severities: builtin,
derived:scanners.skill_scanner, config:admission.<type>.actions or
config:admission.defaults.actions.
Scanner overrides
scanner_overrides changes the action for one scanner's findings. The key is
the name of the scanner that produced the result, not an asset type. This
blocks a skill whose worst finding is MEDIUM when the skill scanner found it,
and leaves the action for findings from other scanners alone:
defenseclaw config set admission.skill.scanner_overrides.skill-scanner.medium blockFirst-party assets
A first-party entry trusts an asset by name and location. It matches when the
asset's name equals name and its path contains one of the
source_path_contains values as whole path components, ignoring case and
using either slash. A look-alike never matches:
| Asset path | Marker .acme/skills/acme-review | Verdict |
|---|---|---|
/home/alice/.acme/skills/acme-review | matches | allowed, scan skipped |
/home/alice/.acme/skills/acme-review-evil | no: a different last component | scan |
/home/alice/.acme-evil/skills/acme-review | no: a different first component | scan |
/Users/alice/.ACME/Skills/acme-review/SKILL.md | matches (case does not matter) | allowed, scan skipped |
defenseclaw config set admission.skill.first_party_allow_list --json '[{"name":"acme-review","source_path_contains":[".acme/skills/acme-review"],"reason":"first-party Acme skill"}]'codeguard and defenseclaw are DefenseClaw's own asset names, and a folder
in a skills or plugins folder can be written by anything the user installs (a
git clone, npx skills add). So an entry with one of these names, built in or
set by you or a policy profile, never trusts a folder by its name and location
alone:
codeguard(skill) skips the scan only for an exact copy of the CodeGuard skill DefenseClaw ships, checked by a SHA-256 digest of its files. A folder namedcodeguardwith any other content is scanned like any other skill.defenseclaw(plugin) skips nothing by itself. The gateway recognizes DefenseClaw's own plugin by its bytes (the OpenClaw plugin it installs, the Amp bridge file) before admission; any other plugin with that name is scanned.
Entries with other names match by name and location, as above.
The list you set replaces the built-in one for that asset type, so list
codeguard again if you still want it trusted. --json '[]' trusts no asset.
The reason is a note for people and is not evaluated. Turn first-party
trust off for one type with allow_list_bypass_scan: false; the entries then
stay in the file but every asset is scanned.
Check what applies
Read the compiled result for one type, with the layer it came from:
defenseclaw config get admission.skill --effective(source: derived:scanners.skill_scanner)
actions:
critical: quarantine
high: quarantine
info: allow
low: allow
medium: warn
allow_list_bypass_scan: true
first_party_allow_list:
- name: codeguard
source_path_contains:
- .openclaw/workspace/skills/codeguard
- .openclaw/skills/codeguard
- .zeptoclaw/skills/codeguard
- .claude/skills/codeguard
scan_on_install: true
scanner_overrides: {}Dry-run one asset through the running policy with the gateway CLI. This asset is on the block list, so it never reaches a scan:
defenseclaw-gateway policy evaluate --target-type skill --target-name risky-skill{
"verdict": "blocked",
"reason": "skill 'risky-skill' is on the block list",
"file_action": "none",
"install_action": "none",
"runtime_action": "allow"
}Add --severity and --findings to simulate a scan result:
defenseclaw-gateway policy evaluate --target-type skill --target-name new-skill --severity HIGH --findings 3On a managed Linux or macOS host, run the gateway command as an administrator; it reads the managed configuration. On a managed Windows standalone host, use an elevated PowerShell prompt and the installed CLI:
& 'C:\Program Files\Cisco\DefenseClaw\bin\defenseclaw.exe' policy evaluate --target-type skill --target-name risky-skillThe managed command reads the administrator's policy. A standard account
cannot use its own ~/.defenseclaw/config.yaml to check the managed verdict.
{
"verdict": "rejected",
"reason": "max severity HIGH triggers block per policy",
"file_action": "quarantine",
"install_action": "block",
"runtime_action": "block"
}In the output, runtime_action uses the policy engine's spelling, block or
allow, for the config's disable or enable. The dry run has no connector
and uses the path /dry-run, so rules scoped to a connector and allow rules
pinned to a path do not match in it. defenseclaw-gateway policy show prints
the whole compiled admission policy.
The block/allow list
asset_policy is the only block list and the only allow list. For each of
skill, mcp and plugin, asset_policy.<type>.denied and
asset_policy.<type>.allowed hold the rules:
asset_policy:
skill:
denied:
- name: risky-skill
reason: unvetted vendor skill
allowed:
- name: vetted-skill
reason: reviewed by security
source_path_contains: ["/opt/acme/skills/vetted-skill"]
mcp:
denied:
- name: evil-mcp
connector: claudecode
reason: exfiltrates data
- url: https://mcp.example.net/v1
reason: unknown vendor
tool:
denied:
- name: delete_file
connector: claudecode
reason: destructive
allowed:
- name: read_file
reason: vettedProp
Type
A rule needs at least one of these fields to match anything. There are no wildcards or regular expressions, because this list gates code and tool execution.
Which rule wins
- A rule scoped to the asset's connector is checked before an unscoped one. A connector-scoped allow therefore overrides a global deny for that connector.
- At the same scope,
deniedis checked beforeallowed. - The first matching rule decides.
Path pins
source_path_contains ties a rule to one on-disk copy. An allowed rule only
matches when its pin appears in the asset's path as whole path components, so
an allow for /opt/acme/skills/vetted-skill never covers
/opt/acme/skills/vetted-skill-v2, and an allow with a pin never matches an
asset that comes with no path. A denied rule with a pin is looser: its text matches as a
case-insensitive substring of the path, and it matches nothing when no path is
presented. The CLI writes a pin when you allow an installed skill or plugin,
and never writes one on a block, so a block follows the name wherever the
asset is installed.
enabled and mode
The explicit lists always apply, whatever asset_policy.enabled and
asset_policy.mode say. Those two keys (enabled: false and mode: observe
by default) govern only the other asset policy rules: a default: deny for a
type, registry_required, and runtime detection. With mode: observe such a
rule records that it would have blocked and lets the asset through; with
mode: action it blocks. See Registries for the
approved-catalog rules.
asset_policy.<type>.runtime_detection works the same way. With
runtime_detection.enabled: false (the default for skills and plugins) an
agent hook still refuses a skill, plugin command or MCP server on the denied
list; only the default, registry and approval rules are skipped at the hook.
Those rules then apply only where the install watcher admits the asset
(per-user installs and managed Windows). A managed Linux or macOS device runs
no install watcher for skills and plugins, so there a default: deny or
registry_required for them blocks nothing until you set
runtime_detection.enabled: true; ensure, status and verify warn with
asset_policy_not_enforced, and the gateway log says so when it starts.
Where a denied skill is refused
An agent hook refuses a denied skill when the agent selects it: Claude Code's
Skill tool and slash commands, and Codex's $name selection. A denied name
also matches the copy a Claude Code plugin bundles (<plugin>:<name> at the
Skill tool and /<plugin>:<name>). Codex has no hook
on its own skill loading, so asked for a skill in plain words it reads the
skill's SKILL.md with a shell command and follows it. The Claude Code and
Codex hooks therefore also refuse any tool call that reaches into the folder of
a denied skill: a .../skills/<name>/... path in a command, a file path or a
working folder. A skill that install admission blocked and disabled (one whose
quarantine failed stays in its folder) is refused the same ways, also when
Codex selects it by the name its SKILL.md declares rather than its folder
name. Two things stay out of reach of a hook:
- the skill's name and description, which Codex lists to the model from its own skill index before any hook runs;
- a command that builds the path at run time, for example from a variable, a
glob, or a
cdinto the skills folder followed by a relative path.
Project skill folders (<project>/.claude/skills, <project>/.agents/skills,
<project>/.codex/skills) are watched once an agent session runs in the
project: its first hook (the session start, or the first skill call) adds the
project's existing skill folders to the install watcher, which restarts and
admits what they hold, as at a gateway start. Until a project skill's first
scan has finished, the hooks refuse it with "is not scanned yet ... try again
in a minute". Up to 32 project folders are watched; on a managed computer only
projects inside the user's home count, and explicit
gateway.watcher.skill.dirs add none. The hooks also cover them as they cover every other folder: a denied name is refused at the
Skill call, the $name selection and the folder read, wherever the skill
lives. With runtime_detection.enabled: true, mode: action and a
default: deny or an approved registry, the Skill call and $name selection
of any project skill that is not approved are refused too.
The folder rule covers the denied list only. A skill that only a
default: deny or registry_required keeps out is refused at the Skill call
and the $name selection, but not when Codex, asked for the skill in plain
words, reads its SKILL.md and follows it. To keep such a skill from running
in Codex, add it to asset_policy.skill.denied or take it off the device.
Take a denied skill off the device to remove it from that index. Where the
install watcher runs (per-user installs and managed Windows), a config change
that adds a denied entry makes it check the installed skills, plugins and
MCP servers at once: one the entry names is refused (a skill or plugin is
quarantined), with an install-rejected audit row, without waiting for its
files to change. An MCP server is matched on its definition too, so a
command or url rule added later rejects a server that was already
installed; the hooks refuse its tool calls from the moment the config
applies. On other
managed devices remove it with your device management tool; the hooks refuse
it in the meantime.
A Claude Code plugin is refused at the hook as well. Claude Code runs a plugin
installed from a local marketplace folder (claude plugin marketplace add <folder>) from that folder, not from its copy under
~/.claude/plugins/cache, and DefenseClaw never changes a marketplace folder,
which other users may share. When asset_policy.plugin denies a plugin, or
admission blocked it, the cache copy is quarantined as usual and the Claude
Code hook refuses the plugin's skills and commands (/<plugin>:<skill>), its
agents (<plugin>:<agent>) and its MCP tools
(mcp__plugin_<plugin>_<server>__<tool>). The hook sees the plugin name only,
so a denied rule written as plugin@marketplace refuses that plugin from any
marketplace. claude plugin list keeps showing such a plugin as enabled, since
that list is Claude Code's own record and DefenseClaw does not edit it;
defenseclaw plugin list shows the block.
The MCP servers a Claude Code plugin bundles are admitted with their plugin:
they get no admission row or MCP inventory entry of their own. The hooks hold
their tool calls to asset_policy.mcp all the same, under the name
plugin:<plugin>:<server> and by their command, args_prefix or url.
Tool lists
asset_policy.tool.denied and asset_policy.tool.allowed hold rules with a
name, an optional connector and a reason. Tool names match
case-sensitively. For a tool call from connector C, the order is: denied for C,
allowed for C, denied for any connector, allowed for any connector, then the
normal guardrail checks. An allow skips the rule, pattern and judge checks, but
a tool that writes files still goes through CodeGuard.
Change the lists
| Writer | Use it for |
|---|---|
defenseclaw skill, mcp, plugin, tool block, allow, unblock | The everyday way. Each command edits the list and the gateway applies it. |
defenseclaw config set asset_policy... | Anything the commands do not cover: URL or command rules, path pins, bulk edits. |
REST POST and DELETE /enforce/block, POST /enforce/allow | Scripts and other tools. See Gateway API. |
Editing config.yaml by hand | Works. The gateway applies a valid change within seconds and rejects an invalid one. config set checks the change before it writes. |
Every writer goes through the one config writer, and the gateway applies the change without a restart. A change takes effect on the next decision; there is nothing to reload.
defenseclaw skill block risky-skill --reason "unvetted vendor skill"
defenseclaw mcp block evil-mcp --connector claudecode --reason "exfiltrates data"
defenseclaw plugin block shady-plugin --reason "unsigned"
defenseclaw tool block delete_file --connector claudecode --reason "destructive"
defenseclaw tool allow read_file --reason "vetted"
defenseclaw tool list[skill] Blocked 'risky-skill' (every connector).
[mcp] Blocked 'evil-mcp' (claudecode).
Note: 'evil-mcp' is not configured on claudecode. Configured MCP servers: none.
The block applies if a server with this name or URL is added later.
Check the name with: defenseclaw mcp list --connector claudecode
[plugin] Blocked 'shady-plugin' (every connector).
[tool] 'delete_file' added to block list (connector 'claudecode')
[tool] 'read_file' (covers connector=claudecode) added to allow list
Tools (connector=claudecode)
TOOL STATUS SCOPE REASON UPDATED
----------------------------------------------------------------------------------
delete_file block connector destructive 2026-10-06 18:19
read_file allow global vetted 2026-10-06 18:19- Without
--connector, a block or allow covers every connector. With--connector, it covers that one.skill allowandplugin allowon an installed copy write a rule scoped to the connector that has the copy and pinned to its path.skill allowon a quarantined copy pins the rule to the path the copy is restored to, and says the files stay in quarantine untildefenseclaw skill restore <name>. When there is no copy to pin to, the command says the rule matches the name only. mcp allowpins the rule to how the server starts: itsurl, or itscommandandargs_prefix(andtransportwhen the server sets one), read from the connector's MCP config. A different server added later under the same name is scanned again. For a server that is not configured (for example onemcp setjust rejected), pass the definition you reviewed:defenseclaw mcp allow context7 --command npx --args '["-y", "@upstash/context7-mcp"]'. Without a configured copy or a pin the rule matches the name only, andmcp allowsays so.skill allowandplugin allowremove the name from the block list as well.unblockremoves the block or allow rule for the name and clears its runtime state, so its scan verdict decides again: the CLI says the asset is scanned on the next check. It never restores files; userestorefor that.- A block does not touch an asset that is already running. Use
disableorquarantinefor that. - With the gateway stopped, the commands still write
config.yamland warn that no audit event was recorded. The gateway applies the list when it starts.
Over REST, state-changing calls need the X-DefenseClaw-Client header and a
JSON content type, besides the gateway token:
curl -s -X POST http://127.0.0.1:18970/enforce/block \
-H "Authorization: Bearer $DEFENSECLAW_GATEWAY_TOKEN" \
-H "X-DefenseClaw-Client: cli" \
-H "Content-Type: application/json" \
-d '{"target_type":"skill","target_name":"curl-skill","reason":"blocked from a script"}'{"effective_policy_digest":"sha256:8e6d614e5be0460109785e8e0d532aa980ab41dd87babf936d2675c4726f0c4b","generation":17,"status":"blocked"}Managed devices
On a managed standalone device the admin config is the only source. Every writer refuses, and the attempt is recorded in the audit log:
Error: This device is managed: add it to asset_policy in the admin config (MDM or management plane)The CLI exits with code 3, and defenseclaw config set refuses with the
message error: This device is managed: change config.yaml in the admin config (MDM or management plane), not on the device, also exit 3. REST answers 403
with {"error":"managed_device","detail":"policy changes are made in the management plane"}. Put the same asset_policy rules in the admin config. See
the enterprise guide for how that config reaches devices.
Reading the lists (GET /enforce/blocked and /enforce/allowed) works
everywhere.
Secure Client devices are unchanged: they keep reading the version 8 admission policy and keep their operator entries in the audit database. See Secure Client.
Users and groups
Admission and the block/allow lists belong to the device. A rule can be scoped to a connector, but not to a user or a group. Guardrail profiles are what you target at users and groups, and they carry guardrail settings (mode, thresholds, human approval, rule pack), not admission. See User- and group-based policies.
What stays runtime state
Some enforcement results are records of what happened on one machine, not
intent. They stay in the actions table of audit.db, the enforcement
journal, and are never read as policy:
- quarantine and restore of a skill's or plugin's files (
quarantine,restore) - runtime disable and enable (
disable,enable) - the install block that a scan verdict records (its reason starts with
auto-block,post-scan:or similar)
The journal is not part of the effective policy digest, and config.yaml
never lists it. unblock also clears the journal's install block for the
name, so a later restore does not leave the asset blocked. On a managed
device, which has no enable or restore, an allowed rule for a skill or
plugin in the admin config clears its install block and runtime disable at
the next admission or rescan. See
Release a blocked or quarantined skill.
Upgrading from config_version 8
defenseclaw config migrate (and defenseclaw upgrade, which runs it) moves
the old admission inputs into config.yaml. --dry-run shows what would move
and writes nothing. The migration keeps config.yaml.v8.bak and writes
migration-v9.json. Until you run it, the version 9 gateway reads a version 8
file in memory, with the old inputs still applying; after it, those keys are
rejected in a version 9 file.
| Old input | Becomes |
|---|---|
policies/rego/data.json config.scan_on_install and allow_list_bypass_scan | admission.defaults.*, written only when the value was off |
data.json actions and the per-type scanner_overrides | admission.<type>.actions, a full five-severity table, written only for a type that differs from the built-in |
data.json first_party_allow_list | admission.<type>.first_party_allow_list, written only when it differs from the built-in |
skill_actions, mcp_actions, plugin_actions | Removed. They never changed an outcome for the severities data.json covered. A value you had customised away from what data.json enforced is listed as a conflict in migration-v9.json. |
watch.allow_list_bypass_scan | Removed. Nothing read it. |
Operator rows of the audit actions table | asset_policy.<type>.denied and allowed |
data.json itself | Renamed data.json.migrated-v9 |
An operator row is an actions row whose install field is block or
allow, unless its reason is a scan verdict (it starts with auto-block,
post-scan:, post-install scan:, scan: or scan clean or within policy).
Each moved row becomes a rule with its name, connector and reason. An allow
also keeps its source_path as a source_path_contains pin; a block does not.
A tool row named @connector/tool becomes a rule for tool on that connector,
and a row named source/tool drops the source. After the config is written,
the migration clears the install field of every moved row, and deletes a row
that has nothing else in it. Quarantine and disable state, scan-verdict blocks
and rows of an unknown type stay in the table.
A dry run on a version 8 home whose actions table holds five operator rows
(a1 to a5) and three journal rows (a6 to a8: a scan-verdict block, a
quarantine and a watcher auto-block):
defenseclaw config migrate --dry-runWould move 6 values into config.yaml; 0 conflicts.
scanners.skill_scanner.policy -> scanners.skill_scanner.policy
actions:a1 -> asset_policy.skill.denied
actions:a2 -> asset_policy.skill.allowed
actions:a3 -> asset_policy.mcp.denied
actions:a4 -> asset_policy.tool.denied
actions:a5 -> asset_policy.tool.deniedOn a managed standalone device nothing moves. The migration only counts the
local rows, ignores them, and reports a local_enforcement_entries_ignored
warning with the count; the admin config is the policy. On a Secure Client
device they stay in the table, unchanged.
Related
Thresholds
block_at and alert_at: when a guardrail finding blocks a prompt or tool call.
Rules and rule packs
guardrail.rules, rule packs and custom packs pinned by digest.
Registries
Approved catalogs, deny-by-default and registry_required.
Skill scanner
The scanner behind skill findings and its fail_on_severity gate.
Gateway API
The /enforce and /policy routes.
Configuration reference
Every key of config.yaml.
Rules: guardrail.rules and custom packs
Choose a guardrail rule pack, add your own pack pinned by digest, and adjust individual rules, suppressions and sensitive tools in config.yaml without copying a pack. Covers scopes, layering order and what happens when a pack or digest is wrong.
Deterministic detection reference
Complete repository-backed reference for DefenseClaw ActionFacts, CEL, regex, semantic proofs, YARA, bounded chains, profiles, and protection packs.