Case Study: Securing OpenClaw Without Changing Its Code
This is the story of how PRECINCT secured a real, production-capable AI assistant framework (one with shell access, browser automation, and connections to 13+ messaging channels) without modifying a single line of its source code.
OpenClaw is a personal AI assistant that operates across multiple channels, including HTTP API, WebSocket, and command-line interfaces. It was chosen as the PRECINCT case study because it represents a complex, real-world agentic application: it has shell access, invokes potentially dangerous tools, and manages multi-turn conversations. It is exactly the kind of agent that needs governance.
The Challenge
OpenClaw is a feature-rich AI assistant framework built in TypeScript/Node.js. It is designed as a local-first personal assistant with an impressive range of capabilities:
- Connects to 13+ messaging channels including WhatsApp, Telegram, Slack, Discord, Signal, iMessage, and more
- Direct access to shell commands and file systems
- Browser automation for web interaction
- Arbitrary tool execution with no built-in restrictions
OpenClaw is a powerful tool for personal use. But it was designed as a local-first personal assistant, not with enterprise security in mind. In a corporate environment, deploying it without governance would be a non-starter. An application that can execute arbitrary shell commands, read any file, and communicate across a dozen external channels represents an unacceptable risk surface without policy enforcement, identity verification, and audit trails.
How do you bring enterprise-grade security to an application that was never designed for it, without forking it, patching it, or adding security middleware inside its codebase?
The Approach: Boundary Enforcement
The key insight is deceptively simple: enforce security at the boundary, not inside the application.
Instead of modifying OpenClaw to understand policies, identities, or audit requirements, we wrote a thin adapter layer that translates OpenClaw's application protocols into the gateway's enforcement model. From OpenClaw's perspective, it is talking to its normal backend services. It does not know (and does not need to know) that every request is being evaluated against 13 layers of security middleware before it reaches its destination.
The application does not know it is being governed.
What We Did NOT Do
It is worth being explicit about what this integration did not require:
- We did not fork OpenClaw
- We did not patch it
- We did not add security middleware inside its codebase
- We did not modify a single line of its source code
OpenClaw runs as its upstream maintainers ship it. We track their releases and pull updates without merge conflicts because there is nothing to merge; we have changed nothing in their repository.
What We Did
The entire integration consists of three adapter components totaling approximately 800 lines of Go:
HTTP Adapter openclaw_http_adapter.go
Handles two HTTP endpoints that OpenClaw uses for its core operations:
/v1/responses: Model inference requests, routed through the gateway's full policy mediation chain/tools/invoke: Tool execution requests, subject to admission decisions by the gateway's tool policy engine
WebSocket Adapter openclaw_ws_adapter.go
Manages persistent WebSocket connections for OpenClaw's real-time features:
/openclaw/ws: Persistent connection for device management, health monitoring, and event streaming- Role-based access control: operator role grants broad access; node role requires device identity presentation
Contract Layer internal/integrations/openclaw/http_adapter.go
The internal contract layer that bridges OpenClaw's request format to the gateway's enforcement model:
- Request parsing and validation against expected schemas
- Policy target resolution, mapping OpenClaw's tool names to gateway policy identifiers
- Dangerous tool blocking at admission: shell, exec, and session management tools are flagged before they reach the middleware chain
How It Works
Consider what happens when OpenClaw tries to execute a shell command. The following sequence diagram shows the full request lifecycle:
{ "tool": "bash", "args": { "command": "ls -la" } } Adapter->>SPIFFE: Verify OpenClaw workload identity SPIFFE-->>Adapter: SPIFFE ID confirmed Adapter->>Audit: Log decision ID, trace ID, SPIFFE ID Adapter->>Registry: Verify bash tool hash (SHA-256) Registry-->>Adapter: Hash valid Adapter->>OPA: Is spiffe://precinct/openclaw authorized for bash? OPA-->>Adapter: Allowed (conditionally) Adapter->>DLP: Scan args for credentials, PII, injection DLP-->>Adapter: Clean Adapter->>Session: Update risk score (bash = critical) Session-->>Adapter: Risk score: 8 Adapter->>StepUp: Risk > 6, evaluate step-up requirement StepUp-->>Adapter: DENIED: stepup_required Adapter-->>OC: 403 { "reason": "stepup_required",
"message": "critical tool requires approval" }
The request never reaches an actual shell. It is denied at step 9 because
bash is classified as a critical tool, pushing the
session risk score above the step-up threshold. OpenClaw receives a structured denial
with a clear reason code.
Policy Contrast: Same Application, Different Outcomes
The power of boundary enforcement becomes clear when you compare two tool invocations from the same OpenClaw instance:
tavily_search: Allowed
- Risk classification: medium
- Session risk score impact: +3
- Step-up required: no
- Result: PERMITTED
A web search tool. It can retrieve information but cannot modify the local system, execute arbitrary code, or access sensitive resources. The gateway lets it through.
bash: Denied
- Risk classification: critical
- Session risk score impact: +8
- Step-up required: yes (risk > 6)
- Result: DENIED (stepup_required)
A shell execution tool. It can run arbitrary commands, read any file, modify the system, and exfiltrate data. The gateway blocks it until a human approves.
OpenClaw has no concept of "medium risk" or "critical risk." It does not know that
tavily_search is treated differently from bash. The policy
is entirely external to the application. This is the fundamental advantage of boundary
enforcement: security policy evolves independently of the application it governs.
The Only Code Change Required
After tracking 50+ upstream commits to OpenClaw, we encountered exactly one contract drift. The OpenClaw WebSocket protocol changed to require that node-role connections present a device identity. This required a 12-line guard in our WebSocket adapter:
if role == "node" {
device, hasDevice := frame.Params["device"].(map[string]any)
nodeDeviceID := ""
if hasDevice {
nodeDeviceID = strings.TrimSpace(getStringAttr(device, "id", ""))
}
if nodeDeviceID == "" {
g.writeOpenClawWSFailure(conn, frame.ID, http.StatusForbidden,
reasonWSDeviceRequired, "node role requires device identity",
decisionID, traceID)
return nil
}
}
12 lines in the adapter. Zero lines in OpenClaw. Zero lines in the gateway core.
This is the entire cost of tracking a rapidly evolving upstream application across 50+ commits. The adapter absorbed the contract change. The gateway was untouched. OpenClaw was untouched. The change was isolated exactly where it belonged: in the thin translation layer between the application and the enforcement boundary.
What This Proves
The OpenClaw integration demonstrates a general principle: you can secure agentic AI applications without modifying them.
- The adapter pattern is the key insight. Each application integration is a thin translation layer (approximately 800 lines of Go) that maps the application's protocols to the gateway's enforcement model. The application remains unmodified.
- The gateway is shared infrastructure. The 13-layer middleware chain, the policy engine, the audit system, the identity verification: none of these change when you onboard a new application. They are shared infrastructure that every adapter reuses.
- New applications are onboarded by writing an adapter, not by retrofitting security. Adding a new application to PRECINCT means writing a new adapter. It does not mean modifying the application, forking it, or injecting security code into its runtime.
- Security controls evolve independently. OpenTelemetry metrics, structured logging, OPA v1 policy support; all of these were added to the gateway without any changes to OpenClaw or its adapter. The application benefits from improved security without being aware of it.
- Compliance evidence is centralized. One audit log. One policy engine. One identity system. Auditors review the gateway and its policies, not individual application codebases. This dramatically simplifies compliance posture for organizations running multiple agentic AI applications.
Agent Implementations
Beyond OpenClaw, PRECINCT supports any agent framework through its SDK. The following examples show how popular Python agent frameworks integrate with PRECINCT's governance layer.
DSPy Research Agent
The DSPy research agent demonstrates how a structured LLM programming framework integrates with PRECINCT's governance layer. The agent uses DSPy's module system for prompt optimization while the PRECINCT SDK handles identity and policy enforcement transparently.
# agents/dspy_researcher/agent.py
import dspy
from mcp_gateway_sdk import GatewayClient
# DSPy handles prompt optimization; PRECINCT handles governance
client = GatewayClient() # Auto-discovers SPIFFE identity
class ResearchAgent(dspy.Module):
def forward(self, query: str):
# All tool calls routed through the governed gateway
result = client.call_tool("web_search", {"query": query})
return dspy.Prediction(answer=result)
PydanticAI Research Agent
The PydanticAI agent showcases type-safe, structured-output integration with PRECINCT. PydanticAI's agent framework provides schema validation while the PRECINCT gateway enforces runtime policy.
# agents/pydantic_researcher/agent.py
from pydantic_ai import Agent
from mcp_gateway_sdk import GatewayClient
client = GatewayClient() # SPIFFE identity auto-injected
agent = Agent(
"openai:gpt-4o",
system_prompt="You are a research assistant.",
)
@agent.tool
async def search(ctx, query: str) -> str:
"""Search via the governed gateway."""
return await client.call_tool_async("web_search", {"query": query})
The Numbers
| Metric | Value |
|---|---|
| OpenClaw source lines modified | 0 |
| Adapter code written | ~800 lines Go |
| Gateway core changes | 0 |
| Upstream commits tracked | 50+ |
| Contract drifts found | 1 (12-line fix) |
| E2E test scenarios | 28 |
| Middleware layers traversed | 13 |
800 lines of adapter code. Zero lines of application modification. Full enterprise security governance (identity, authorization, audit, DLP, step-up gating, and compliance evidence) applied to an application that was never designed for it.
Want to do this with your agent?
The Integration Guide distills this pattern into 4 concrete steps you can follow for any agent.
Want to build your own adapter? The Adapter Guide breaks down the PortAdapter interface and walks through HTTP, WebSocket, and webhook handlers step by step, using the OpenClaw adapter you just read about as the worked example.