Product Specifications
Browse technical specifications and schemas
File Tree
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_callin 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.mdyour 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}.shieldand policy packevaluation_rules. - Importer tooling: CLI to ingest
shield.mdand 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_approvalunless action isblockand severity iscritical. - 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
revokedis false,revoked_atis null, and current time is beforeexpires_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 toblocked_patternsand an OAP policy forsystem.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.equalsorconditions.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:
- Reads
limits.{capability}.shield.threats(for the capability the policy governs). - Filters to eligible threats (not revoked, not expired).
- Matches using
recommendation_agent/ conditions first, then explicit string match; applies confidence downgrade rule; resolves multiple matches as block > require_approval > log. - 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: 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 andlimits.{capability}.shieldshape 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 inindex.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
- Spec (this doc): Finalize mapping, scope→capability table, and
limits.{capability}.shieldshape; document in aport-spec discussion #19. - Importer: adapters/index.js is the reference implementation (run as CLI:
node adapters/index.js [shield.md]). Optional packaging asnpx @aporthq/aport-agent-guardrails shield import shield.mdfor distribution. It parsesshield.md(threat blocks between ---), maps tolimits.{capability}.shield.threats[]and an OAP policy pack (e.g. tool.call → system.command.execute). - 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)
- Store SHIELD threats under
limits.{capability}.shield(as above) or introduce a top-levelthreat_feedssection? - Support bidirectional export (OAP limits + rules → SHIELD markdown) for round-trip editing?
- Conflict resolution when multiple feeds define the same capability/condition: last-write-wins vs explicit priorities?
- How to represent
recommendation_agentnatural-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
- Should we support bidirectional export (OAP policy pack → SHIELD markdown) for round-trip editing?
- Conflict resolution: when multiple feeds define the same capability/condition, should latest
entry win, or should we require explicit priorities?
- How should
recommendation_agentdirectives 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
- SHIELD spec (canonical): shield.md and Upgrade path to v1
- OAP spec: spec/oap/oap-spec.md
- Policy packs: policies/README.md, e.g. system.command.execute.v1, mcp.tool.execute.v1
- OpenClaw issue (SHIELD policy layer): openclaw/openclaw#12385 - draft reply: ISSUE_REPLY_DRAFT.md
- OAP spec discussion (SHIELD import): aporthq/aport-spec discussions #19 - draft summary: DISCUSSION_SUMMARY.md
- Positioning (OAP = standard, SHIELD = threat intel): _plan/execution/openclaw/SHIELD_POWER_ANALYSIS_CORRECTED.md