Skill and MCP scanning

Registries

Subscribe DefenseClaw to public or internal skill / MCP catalogs. Sources are fetched, scanned, and clean entries are auto-promoted into asset_policy so admission decisions can attribute the rule back to its origin.

A registry source is a fetchable manifest of skills and/or MCP servers that DefenseClaw ingests on demand. Public catalogs (clawhub, smithery, skills.sh), internal HTTPS YAMLs / JSONs, git repos, and on-disk YAMLs are all supported. Synced entries flow through the existing skill / MCP scanners; clean ones land in asset_policy.{skill,mcp}.registry so a later admission decision can say "approved by registry:<id>" instead of "approved by an unattributed rule someone wrote in 2024."

Why registries matter

Without a registry, every new skill / MCP that lands on disk is admission-evaluated against ad-hoc operator-authored rules. With one or more registries, the catalog drives admission: clean entries get auto-promoted, everything else is held for review, and toggling registry_required flips the asset class to deny-by-default for anything the catalog hasn't blessed.

Guided example · Registry sync is on demand

Turn a catalog entry into an admission decision

On-demand sync verifies source integrity, routes through a scanner, and promotes, reviews, or denies a pre-authored variant.

Deterministic
Outcome
asset_policy:  skill:    registry:      workspace-helper@1.2.0:        action: allow        reason: registry:internal-catalogasset_policy:  skill:    registry:      workspace-helper@1.2.0:        action: allow        reason: registry:internal-catalog
DecisionPromote clean entry
Reason

Integrity verified and scanner result is clean

Action

Write attributed asset_policy rule

03

PromoteWrite an attributed allow rule into asset_policy.

Step 3 / 3
What DefenseClaw did — and did not do

What it did

  • Run registry sync on demand
  • Verify source integrity and route through existing scanners
  • Attribute promoted rules to the registry source

What it did not do

  • Poll registries automatically at runtime
  • Let registry trust bypass scanner findings unless policy allows it
  • Read a credential value from auth_env

What you just saw

An on-demand registry sync verified source integrity, routed an entry through its existing scanner, and selected a deterministic outcome: promote a clean entry, hold a warning for review, or deny an unknown asset when registry_required=true. Stored sync settings do not create a runtime poller.

The seven registry kinds

Prop

Type

Three worked examples

Most enterprises run a curated mirror inside their own DNS:

defenseclaw registry add corp-skills \
  --kind http_yaml \
  --url https://catalog.corp.example.com/skills.yaml \
  --content skill \
  --auth-env CORP_CATALOG_TOKEN \
  --non-interactive

export CORP_CATALOG_TOKEN="$(vault kv get -field=token kv/defenseclaw/catalog)"
defenseclaw registry test corp-skills              # dry-run: fetch + parse, no writes
defenseclaw registry sync corp-skills              # fetch + scan + promote

The --auth-env option takes the name of an env var; the literal token never lands in config.yaml. SHA-256 is required on every entry's source_url for http_yaml, http_json, git, and file kinds — the manifest publisher computes it once.

For the smithery.ai public MCP catalog:

defenseclaw registry add smithery-public \
  --kind smithery \
  --content mcp \
  --non-interactive

defenseclaw registry sync smithery-public

Each entry is run through the MCP scanner; clean ones land in asset_policy.mcp.registry so an MCP listed in the catalog passes admission with the registry named as the reason. Remote (URL) entries are scanned directly. Scanning a stdio entry starts the publisher's package on your machine, so it happens only with registry sync --scan-stdio; without that flag the entry stays pending and is not promoted. Use --scan-stdio only for a catalog whose packages you are willing to run.

ClawHub lists OpenClaw plugins, so it matters mainly for OpenClaw installs. No URL and no auth are needed:

defenseclaw registry add clawhub \
  --kind clawhub \
  --content skill \
  --non-interactive

defenseclaw registry sync clawhub

The first sync downloads the manifest, runs the skill scanner on every entry, and promotes the clean ones into asset_policy.skill.registry.

All twelve subcommands

Prop

Type

The lifecycle of an entry

clean
anything else
you approve
you reject
Add a sourceregistry addsaved in config.yaml
Sync the sourceregistry syncfetch the manifest
Scan each entryskill scanner or MCP scanner
Scan result?
Held for reviewwarning or blockederror or pending
Added to asset policyrule tagged registry:<id>
Never promotedlisted as blocked

List view for small screens. Use the expand button to open the drawing.

  1. Add a sourceregistry addsaved in config.yaml
    • Sync the source
  2. Sync the sourceregistry syncfetch the manifest
    • Scan each entry
  3. Scan each entryskill scanner or MCP scanner
    • Scan result?
  4. Scan result?
    • cleanAdded to asset policy
    • anything elseHeld for review
  5. Held for reviewwarning or blockederror or pending
    • you approveAdded to asset policy
    • you rejectNever promoted
  6. Never promotedlisted as blocked
  7. Added to asset policyrule tagged registry:<id>
How a registry entry reaches asset policy. Sync fetches the manifest and scans every entry; only a clean entry is promoted on its own. Syncing with --no-promote scans without touching policy. Approve and reject override the scanner either way, and a rejected entry is never promoted.

Promote and require — the deny-by-default story

Once entries are flowing in, asset_policy.<type>.registry_required flips the corresponding asset class to deny-by-default:

defenseclaw registry require --type skill --enabled
defenseclaw registry require --type mcp   --enabled --enforce

The requirement is enforced only while asset policy is on: asset_policy.enabled: true and asset_policy.mode: action. A per-user install starts with enabled: false and mode: observe, so require alone blocks nothing and the CLI says so. Add --enforce to turn both on in the same save, or set them in the TUI config editor (Setup → c → Asset Policy). In observe mode an unregistered asset is logged, not blocked. To turn enforcement back off, run defenseclaw registry require --type mcp --disabled --no-enforce (--no-enforce sets mode: observe).

A registry rule that pins transport treats http and streamable-http as the same HTTP transport, and a URL server added without --transport counts as http.

An unscoped operation is broad operator intent: it updates the global default and clears only the targeted registry_required override on every active connector, so stale opposite overrides cannot keep enforcement disabled. All other per-connector fields and the global registry rule/filter lists remain unchanged. --connector <name> changes only that connector's explicit override. Known but inactive connector overrides are preserved so future activation does not silently inherit a weaker or stronger posture than the one previously saved.

When registry_required=true:

  • An asset whose name matches a rule in asset_policy.<type>.registry follows that rule.
  • An asset whose name does not match any rule falls through to the configured registry_empty_action (default deny).
  • An empty registry list with require=on blocks every asset of that class by default until the next sync populates it. Setting registry_empty_action: warn or allow relaxes only this empty-registry gate and falls through to the type default; warn is non-blocking. The CLI warns you in colour when you flip require=on against an empty list — read the warning carefully.

The Go gateway tags every admission decision with Reason="registry:<id>" (or "registry:operator-approved" for manually approved entries). Inspect the TUI or run defenseclaw-gateway audit export --output - to see which catalog won; an optional JSONL destination contains only the records its v8 route selects.

What --auth-env, --allow-private, and SHA-256 actually do

KnobDefaultPurpose
--auth-env <ENV>""Name of an env var holding a bearer token sent on every fetch. The CLI rejects values that look like literal secrets.
--allow-privateoffA registry test and registry sync flag (not add). Permits RFC 1918 / ULA destinations: off in production, on for an internal mirror on a private subnet.
--scan-stdiooffA registry sync flag. Also scans stdio MCP entries, which starts each publisher-controlled package on this machine.
--non-interactiveoffRequired for CI; missing required flags exit non-zero with a clear error: --foo is required.
SHA-256 on source_urlrequired for http_yaml / http_json / git / filePer-entry integrity; the scanner refuses to download an entry whose computed digest does not match.

Registry fetches use the same hardened URL path in test and sync: loopback, link-local, cloud-metadata, and private-network destinations are refused by default, including DNS resolutions that rebind into a reserved range. Use --allow-private only for a registry mirror you intentionally run on an internal network. Local file registries must use absolute paths, and remote entry downloads must pass the per-entry SHA-256 check before scanner promotion can happen.

Inspect what landed

This is a real file source with two stdio MCP entries, synced without --scan-stdio, so both entries are still pending:

defenseclaw registry list
  Registry sources
  ----------------
  ID                       KIND         CONTENT  ON  ENTRIES            LAST SYNC              URL
  ------------------------ ------------ -------- --- ------------------ ---------------------- --------------------------------
  corp-mcp                 file         mcp      yes 2 (0/0/0)          2026-10-03T04:25:28Z   /tmp/corp-mcp.yaml

  ENTRIES column: total (clean/warning/blocked)
defenseclaw registry entries corp-mcp --status pending
  Entries for corp-mcp
  --------------------
  NAME                             TYPE   STATUS     SEV      A/R
  -------------------------------- ------ ---------- -------- ---
  context7                         mcp    pending    -        --
  filesystem                       mcp    pending    -        --

ENTRIES reads total, then clean, warning and blocked counts; a dash means the source was never synced. A/R shows whether you approved or rejected an entry.

defenseclaw registry list                              # one row per source, with cached counts
defenseclaw registry show corp-skills                  # full detail + verdict summary
defenseclaw registry entries corp-skills --status warning
defenseclaw registry approve corp-skills my-skill --type skill
defenseclaw registry reject  corp-skills evil-skill --type skill

approve and reject re-promote against the cached manifest immediately by default, so the new rule lands without a network round-trip. Pass --no-repromote if you want to defer the change to the next registry sync. --type is optional when the source holds one content type (--content skill or --content mcp) or the entry name exists under one type only.

Sync schedules

Today, syncing is on demand: scheduled sync is not available yet, and add / edit refuse --auto-sync and --sync-interval-hours. Run registry sync --all on a schedule (cron, a systemd timer or a CI schedule) until scheduled sync ships in a future release.

DefenseClaw configuration is per user, so schedule the sync as the user who runs DefenseClaw, for example in that user's crontab (crontab -e):

0 * * * * defenseclaw registry sync --all

Keep the gateway running: registry sync records an audit event and exits with an error when the gateway is unavailable. Use the full path to defenseclaw if cron's PATH does not include it.

Common gotchas

Reference