Product Specifications

Browse technical specifications and schemas

2 directories

File Tree

spec

SHIELD Integration (Draft)

_Last updated: 2026-02-17_

Overview

SHIELD.md (canonical spec) defines a structured threat feed for AI agents: threat entries with id, fingerprint, category, severity, confidence, action, and recommendation_agent matching logic. Enforcement is exactly one of log, require_approval, or block. Version 0 is context-loaded (advisory); v1 keeps the same threat shape and moves enforcement outside the LLM.

Open Agent Passport (OAP) v1.0 (spec) provides the runtime authorization layer SHIELD v1 envisions:

  • Passports (W3C VC/DID-aligned) describe agent capabilities and limits.
  • Policy packs define evaluation rules; limits (e.g. allowlists, blocked patterns, threat data) live in the passport under limits.{capability}.
  • APort guardrails evaluate policy packs against passport + context and enforce deterministically (e.g. before_tool_call in OpenClaw).

This integration treats SHIELD threat intelligence as an input to OAP: SHIELD content (whether from community feeds or user-defined shield.md rules) is imported into OAP policy packs and passport limits so the same deterministic, signed enforcement applies.

OAP is the authorization standard. SHIELD is threat intelligence.

  • SHIELD: A format for threat definitions. Content can be feeds (community- or vendor-curated lists you import) or user-defined rules (a shield.md your team authors). Either way it’s easy for security teams to maintain.
  • OAP: W3C-compliant authorization (crypto signatures, audit trails)
  • This adapter: Translates SHIELD → OAP for deterministic enforcement

Analogy: SHIELD is like a CVE database (or your own rule set in that format); OAP is the firewall that enforces rules based on that intel. This adapter is the import layer.

See _plan/execution/openclaw/SHIELD_POWER_ANALYSIS_CORRECTED.md.

Goals

  • Spec alignment: Map the canonical SHIELD v0/v1 threat model onto OAP: passport limits.{capability}.shield and policy pack evaluation_rules.
  • Importer tooling: CLI to ingest shield.md and emit (1) a limits fragment for passports and (2) policy pack updates (e.g. a SHIELD evaluation rule) so guardrails can enforce without hand-editing JSON.
  • Confidence semantics: Preserve SHIELD’s rule: ≥0.85 confidence → enforce as-is; <0.85 → treat as require_approval unless action is block and severity is critical.
  • Multi-source feeds: SHIELD alongside ClawMoat, CVE, and custom rules, all under the same OAP schema and enforcement.

SHIELD v0 Reference (canonical)

  • Scopes: prompt, skill.install, skill.execute, tool.call, network.egress, secrets.read, mcp
  • Threat categories: prompt, tool, mcp, memory, supply_chain, vulnerability, fraud, policy_bypass, anomaly, skill, other
  • Actions: log | require_approval | block (only these three)
  • Eligibility: threat is eligible only if revoked is false, revoked_at is null, and current time is before expires_at
  • Decision block: Before gated events, emit a decision with action, scope, threat_id, fingerprint, matched_on, match_value, reason
  • Block response (exact): Blocked. Threat matched: . Match: =.
  • recommendation_agent (v0): Case-sensitive directives BLOCK:, APPROVE:, LOG: with conditions: skill name equals/contains, outbound request to domain/url, secrets read path equals, file path equals; operator OR. Normalization: domains lowercase, no trailing dot; URLs prefix match.

Scope → OAP capability mapping

SHIELD scope OAP capability / policy pack (example) Implemented in adapter
prompt e.g. prompt.guardrail No (OAP supports when policy pack exists)
skill.install Skill policy No
skill.execute Skill policy No
tool.call system.command.execute (policy) Yes
network.egress Network policy No
secrets.read Secrets policy No
mcp mcp.tool.execute (policy) No (OAP supports; adapter can be extended)

When importing, threat.category and SHIELD scope determine which OAP capability (and thus which policy pack and limits.{capability}) the threat is attached to. Each capability already has its own policy pack (e.g. system.command.execute.v1 → policies/system.command.execute.v1/policy.json). We do not create new policy files per scope; within each existing policy pack, we embed SHIELD threats under limits.{capability}.shield (passport limits for that capability).

Strategy for multiple SHIELD scopes

Why doesn’t the adapter support every scope today? Each SHIELD scope has different context and recommendation_agent conditions:

  • tool.call → context: command, args; conditions: “command contains X” → we map to blocked_patterns and an OAP policy for system.command.execute.
  • mcp → context: server, tool; conditions: domain/url, skill name, etc. → would need a different limits shape and policy pack (e.g. mcp.tool.execute).
  • skill.execute → context: skill name, etc.; conditions: “skill name equals/contains” → would need a skill policy pack and condition parser.
  • network.egress, secrets.read, prompt → each has its own context and conditions in the SHIELD spec.

Do we have to implement each scope manually? Yes. Adding a scope means: (1) scope → OAP capability (and policy pack shape), (2) parsing that scope’s recommendation_agent conditions (e.g. “skill name contains X” → limits or conditions), (3) outputting the right policy + limits for that capability. The adapter is a reference implementation; each scope is a small, well-defined addition (new template + condition parser).

Generated on the fly vs stored in policies/? The adapter generates policy and limits on the fly from shield.md; nothing is hardcoded in the repo as the only source. For scopes that already have an APort policy pack (e.g. policies/system.command.execute.v1/, policies/mcp.tool.execute.v1/), we reuse that shape (required_context, evaluation_rules) in the adapter so the output is verifier-compatible. Each capability already maps to its policy pack; we embed SHIELD threats inside those packs under limits.{capability}.shield. So: each SHIELD scope becomes an OAP policy (capability + rules); that policy is generated by the adapter from the feed, not stored as a static file. The existing policies/* in the repo are canonical shapes the adapter can mirror; they are not required to exist for the adapter to emit a valid pack for a new scope.

Field mapping (SHIELD → OAP)

Each OAP capability already has its own policy pack (e.g. policies/system.command.execute.v1/policy.json). SHIELD threat fields are stored under passport limits at limits.{capability}.shield.threats[] for that capability - i.e. within each existing policy pack’s limits, we embed SHIELD under limits.{capability}.shield; we do not create new policy files per scope. The policy pack adds an evaluation rule (e.g. type shield or custom_validator validateShieldThreats) that reads this structure and enforces SHIELD semantics.

SHIELD field (v0) OAP placement (passport limits) Notes
id limits.{capability}.shield.threats[].id Prefixed shield_ to avoid collisions (e.g. shield_T001).
fingerprint limits.{capability}.shield.threats[].fingerprint Optional; dedup and audit.
category limits.{capability}.shield.threats[].category Must align with SHIELD categories; used for scope matching.
severity limits.{capability}.shield.threats[].severity Drives priority when multiple threats match.
confidence limits.{capability}.shield.threats[].confidence ≥0.85 → enforce; <0.85 → require_approval unless block+critical.
action limits.{capability}.shield.threats[].action log \ require_approval \ block.
title limits.{capability}.shield.threats[].title Short label; optional description for body.
recommendation_agent limits.{capability}.shield.threats[].recommendation_agent or parsed into conditions BLOCK/APPROVE/LOG + conditions; see below.
expires_at limits.{capability}.shield.threats[].expires_at Omit or mark inactive when expired.
revoked / revoked_at limits.{capability}.shield.threats[].revoked (and optional revoked_at) Exclude revoked threats from evaluation.

recommendation_agent → conditions

SHIELD v0 conditions map to structured conditions used by the guardrail:

  • skill name equals <value> → e.g. conditions.skill.equals or conditions.skill.name
  • skill name contains <value> → e.g. conditions.skill.contains
  • outbound request to <domain> → e.g. conditions.domain (normalized: lowercase, no trailing dot)
  • outbound request to <url_prefix> → e.g. conditions.url_prefix
  • secrets read path equals <value> → e.g. conditions.secret.path
  • file path equals <value> → e.g. conditions.file.path

BLOCK → enforce as block; APPROVE → require_approval; LOG → log. Multiple conditions may be combined with OR as in the spec.

Resulting passport limits shape (per capability)

Imported SHIELD threats for a given capability live under the passport’s limits, e.g. for system.command.execute:

{
  "limits": {
    "system.command.execute": {
      "allowed_commands": ["npm", "node", "git"],
      "blocked_patterns": ["rm -rf", "sudo"],
      "max_execution_time": 300,
      "shield": {
        "threats": [
          {
            "id": "shield_T001",
            "fingerprint": "optional-hash",
            "category": "tool",
            "severity": "critical",
            "confidence": 0.97,
            "action": "block",
            "title": "Destructive shell commands",
            "recommendation_agent": "BLOCK: command contains rm -rf OR command contains sudo",
            "conditions": {
              "patterns": { "blocked": ["rm -rf", "sudo"] }
            },
            "description": "Block destructive shell commands",
            "expires_at": null,
            "revoked": false
          }
        ]
      }
    }
  }
}

Policy packs that support SHIELD add an evaluation rule that:

  1. Reads limits.{capability}.shield.threats (for the capability the policy governs).
  2. Filters to eligible threats (not revoked, not expired).
  3. Matches using recommendation_agent / conditions first, then explicit string match; applies confidence downgrade rule; resolves multiple matches as block > require_approval > log.
  4. For deny: returns OAP deny with a reason that follows SHIELD’s block format where applicable: Blocked. Threat matched: . Match: =.

Example evaluation rule in the policy pack (conceptual; OAP today supports expression and custom_validator - either add type: "shield" or implement as a custom_validator such as validateShieldThreats):

{
  "name": "shield_enforcement",
  "type": "shield",
  "shield_ref": "limits.system.command.execute.shield",
  "deny_code": "oap.shield_blocked",
  "description": "Evaluate SHIELD threats for this capability"
}

Expression rules in OAP have access to passport, context, and limits; a SHIELD rule would use limits.system.command.execute.shield (or the capability implied by the policy).

Confidence downgrade logic

Same as SHIELD v0:

const shouldEnforce = threat.confidence >= 0.85 || (threat.action === "block" && threat.severity === "critical");
const effectiveAction = shouldEnforce ? threat.action : "require_approval";

Implementation vs canonical SHIELD spec

The canonical SHIELD v0 spec defines scopes, threat fields, enforcement states, decision block format, and recommendation_agent syntax. This integration implements the following:

Spec element Implemented Notes
Scope tool.call only Adapter outputs policy + limits for system.command.execute. Other scopes (prompt, skill.install, skill.execute, network.egress, secrets.read, mcp) are not yet implemented in the adapter; OAP supports them when corresponding policy packs exist.
Active threats (compressed) Yes Parsed: id, fingerprint, category, severity, confidence, action, title, recommendation_agent, expires_at, revoked.
Match eligibility Partially Revoked threats excluded in adapter. Evaluator can filter by expires_at and apply confidence threshold.
Confidence threshold In evaluator ≥0.85 enforce; <0.85 → require_approval unless block+critical. Can be applied when evaluating limits.{capability}.shield.threats.
Enforcement states Yes log, require_approval, block; BLOCK/APPROVE/LOG in recommendation_agent.
recommendation_agent Subset Implemented: BLOCK/APPROVE with “command contains X” (and OR) for tool scope. Not yet: skill name equals/contains, outbound request to domain/url, secrets read path, file path.
Block response format Optional Spec requires exact: Blocked. Threat matched: . Match: =. OAP deny reason can follow this when using a SHIELD-specific evaluator.
Decision block Via OAP SHIELD’s DECISION block (action, scope, threat_id, …) is reflected in OAP’s allow/deny decision + reasons; scope maps to capability.

Alignment with OpenClaw and APort spec

  • OpenClaw #12385 – Adds an optional SHIELD policy layer that runs before sensitive events (skill install/execute, tool call, MCP, network, secrets). Our adapter produces the OAP policy pack and limits fragment that a guardrail uses to enforce that layer; the “before_tool_call” (and other gates) call the same OAP verifier. So: this repo = SHIELD → OAP import; OpenClaw = where that OAP policy is enforced at runtime (e.g. before_tool_call). To point the thread here: post the drafted reply in ISSUE_REPLY_DRAFT.md as a comment on the issue.
  • aporthq/aport-spec discussion #19 – Proposes SHIELD threat intelligence import into OAP policy packs, CLI shield-to-oap, and multi-source threat intel. This adapter is the reference implementation of that import path; field mapping and limits.{capability}.shield shape align with the discussion. Next steps: post the summary in DISCUSSION_SUMMARY.md to the discussion, gather feedback, and link the public discussion from this README once it's live.

APort supports policy-in-body verify and the full OAP stack today; the “future” piece is only optional packaging of the adapter as npx @aporthq/aport-agent-guardrails shield import shield.md for distribution.

Reference adapter

CLI status: The packaged CLI (npx @aporthq/aport-agent-guardrails shield import shield.md) is not yet released - CLI implementation in progress. Use the in-repo script below until it ships.

The reference implementation lives in adapters/:

  • adapters/index.js – Imports each scope adapter, exposes convert(shieldPath), and doubles as a script: node adapters/index.js [shield.md] outputs JSON (policy, limitsFragment, threats). Default input: test/shield.md.
  • Current scope: tool.call → adapters/system-command-execute.js (OAP system.command.execute). Add more scopes by adding an adapter file and registering it in index.js.
  • test/ – Test folder: fixture test/shield.md and integration test test/test-shield-to-verify.js (conversion, APort verify, response shape).
  • Demo: A <2 min demo GIF showing “malicious tool blocked” (e.g. Cisco scenario). Placeholder until recorded.

See test/README.md for run instructions and test coverage.

Workstreams

  1. Spec (this doc): Finalize mapping, scope→capability table, and limits.{capability}.shield shape; document in aport-spec discussion #19.
  2. Importer: adapters/index.js is the reference implementation (run as CLI: node adapters/index.js [shield.md]). Optional packaging as npx @aporthq/aport-agent-guardrails shield import shield.md for distribution. It parses shield.md (threat blocks between ---), maps to limits.{capability}.shield.threats[] and an OAP policy pack (e.g. tool.call → system.command.execute).
  3. Demo and docs: Example shield.md, resulting limits fragment, policy pack snippet, guardrail log excerpt; <2 min demo GIF (malicious tool blocked, e.g. Cisco scenario).

Roadmap

Phase Target Deliverables
Spec alignment Feb 20 Final mapping (this doc), scope→capability table, publish summary and open questions in discussion #19.
Importer + tests Feb 28 CLI implementation in progress. Target: packaged shield-to-oap CLI, fixtures, README + demo, guardrail integration tests. In-repo: node adapters/index.js [shield.md] works today.
Launch & outreach Mar 5 OpenClaw PR/docs, Discord, contribution guide for SHIELD feeds, joint messaging with ClawMoat.

Publishable state: Once the discussion summary is posted to discussion #19, the drafted issue reply is posted to OpenClaw #12385, and an initial ClawMoat schema is added, the spec folder is in a publishable state even before the packaged CLI ships.

Open questions (for discussion #19)

  1. Store SHIELD threats under limits.{capability}.shield (as above) or introduce a top-level threat_feeds section?
  2. Support bidirectional export (OAP limits + rules → SHIELD markdown) for round-trip editing?
  3. Conflict resolution when multiple feeds define the same capability/condition: last-write-wins vs explicit priorities?
  4. How to represent recommendation_agent natural-language fragments that don’t map to explicit strings (heuristics vs structured conditions only)?

Compatibility with APort

This integration is compatible with APort’s policy verifier and the OAP authorization stack: policy packs that include SHIELD threat data (via the mapping above) are evaluated by the same verifier that enforces all OAP policies - deterministic evaluation, cryptographically signed allow/deny decisions, compliance-grade audit trail, and sub-100ms decisions. SHIELD threat intelligence is one input to that standard; the authorization standard remains OAP. This spec (and the broader spec/integrations direction) sets the standard for how threat feeds plug into OAP and positions APort as the authorization layer, consistent with discussion #19.

Open Questions for Discussion #19

  1. Should we support bidirectional export (OAP policy pack → SHIELD markdown) for round-trip editing?
  2. Conflict resolution: when multiple feeds define the same capability/condition, should latest

entry win, or should we require explicit priorities?

  1. How should recommendation_agent directives that rely on natural-language descriptions be

represented (e.g., heuristics vs explicit strings)?

Test and adapters

The test/ folder contains the test fixture (shield.md) and integration test (test-shield-to-verify.js). The adapters/ folder contains one adapter per scope: index.js imports them (currently system-command-execute.js for tool.call). Flow: shield.md → adapters → policy + limits → verify. See test/README.md for run instructions.

References