Deploying SafeClaw Alongside Agents in Docker Compose
SafeClaw is action-level gating for AI agents, built by Authensor. This guide covers deploying SafeClaw as a sidecar service alongside your AI agents in Docker Compose. Every agent action is evaluated against a deny-by-default policy before execution.
Prerequisites
- Docker Engine 24 or later
- Docker Compose v2
- A SafeClaw account at safeclaw.onrender.com (free tier, 7-day renewable keys, no credit card)
- SafeClaw API key from the browser dashboard
- An existing agent application (any framework: LangChain, CrewAI, AutoGen, custom)
Step-by-Step Instructions
Step 1: Install SafeClaw Locally for Policy Configuration
npx @authensor/safeclaw
Complete the browser-based setup wizard. Use the dashboard to generate your API key. SafeClaw has zero third-party dependencies and is validated by 446 tests in TypeScript strict mode.
Step 2: Create the Project Structure
project/
├── docker-compose.yaml
├── safeclaw/
│ ├── Dockerfile
│ ├── safeclaw.config.yaml
│ └── server.ts
├── agent/
│ ├── Dockerfile
│ └── app.py
└── .env
Step 3: Create the SafeClaw Sidecar Dockerfile
safeclaw/Dockerfile:
FROM node:20-alpine
WORKDIR /app
RUN npm install @authensor/safeclaw
COPY safeclaw.config.yaml .
COPY server.ts .
RUN npx tsc server.ts --esModuleInterop --module nodenext --moduleResolution nodenext
EXPOSE 3100
CMD ["node", "server.js"]
Step 4: Create the SafeClaw HTTP Gateway
safeclaw/server.ts:
import { SafeClaw } from "@authensor/safeclaw";
import http from "node:http";
const safeclaw = new SafeClaw({
apiKey: process.env.SAFECLAW_API_KEY!,
agentId: process.env.AGENT_ID || "docker-agent",
mode: process.env.SAFECLAW_MODE || "enforce",
});
const server = http.createServer(async (req, res) => {
if (req.method === "POST" && req.url === "/evaluate") {
let body = "";
req.on("data", (chunk) => (body += chunk));
req.on("end", async () => {
try {
const action = JSON.parse(body);
const result = await safeclaw.evaluate({
actionType: action.actionType,
target: action.target,
metadata: action.metadata || {},
});
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify(result));
} catch (err) {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: (err as Error).message }));
}
});
} else if (req.method === "GET" && req.url === "/health") {
res.writeHead(200);
res.end("ok");
} else {
res.writeHead(404);
res.end();
}
});
server.listen(3100, () => {
console.log("SafeClaw gateway running on port 3100");
});
Step 5: Write the Docker Compose File
docker-compose.yaml:
version: "3.9"
services:
safeclaw:
build: ./safeclaw
container_name: safeclaw-gate
ports:
- "3100:3100"
environment:
- SAFECLAW_API_KEY=${SAFECLAW_API_KEY}
- AGENT_ID=docker-agent
- SAFECLAW_MODE=enforce
volumes:
- ./safeclaw/safeclaw.config.yaml:/app/safeclaw.config.yaml:ro
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3100/health"]
interval: 10s
timeout: 5s
retries: 3
restart: unless-stopped
agent:
build: ./agent
container_name: ai-agent
depends_on:
safeclaw:
condition: service_healthy
environment:
- SAFECLAW_URL=http://safeclaw:3100
- OPENAI_API_KEY=${OPENAI_API_KEY}
volumes:
- ./workspace:/app/workspace
restart: unless-stopped
Step 6: Create the Agent Client
In your agent application, call the SafeClaw sidecar before every action.
agent/app.py:
import requests
import os
SAFECLAW_URL = os.environ.get("SAFECLAW_URL", "http://safeclaw:3100")
def check_action(action_type: str, target: str) -> dict:
response = requests.post(
f"{SAFECLAW_URL}/evaluate",
json={
"actionType": action_type,
"target": target,
"metadata": {"agent": "docker-agent"},
},
)
return response.json()
def safe_write_file(path: str, content: str) -> str:
result = check_action("file_write", path)
if result["decision"] != "ALLOW":
return f"BLOCKED: {result['decision']}"
with open(path, "w") as f:
f.write(content)
return f"Written: {path}"
def safe_shell_exec(command: str) -> str:
result = check_action("shell_exec", command)
if result["decision"] != "ALLOW":
return f"BLOCKED: {result['decision']}"
import subprocess
return subprocess.run(command, shell=True, capture_output=True, text=True).stdout
Step 7: Create the Environment File
.env:
SAFECLAW_API_KEY=your-safeclaw-api-key
OPENAI_API_KEY=your-openai-api-key
Step 8: Deploy
docker compose up --build -d
Verify the SafeClaw sidecar is healthy:
docker compose ps
curl http://localhost:3100/health
Step 9: Monitor via Dashboard
All evaluations are logged to the tamper-proof audit trail (SHA-256 hash chain). The control plane at safeclaw.onrender.com sees only action metadata, never your keys or data. Review decisions in the browser dashboard.
Example Policy
# safeclaw/safeclaw.config.yaml
version: "1.0"
agent: docker-agent
defaultAction: deny
rules:
- id: allow-read-workspace
action: file_read
target: "/app/workspace/**"
decision: allow
- id: allow-write-workspace
action: file_write
target: "/app/workspace/**"
decision: allow
- id: deny-write-system
action: file_write
target: "/etc/**"
decision: deny
- id: deny-write-app
action: file_write
target: "/app/*.ts"
decision: deny
description: "Block overwriting application code"
- id: allow-python
action: shell_exec
target: "python3*"
decision: allow
- id: deny-destructive
action: shell_exec
target: "rm *"
decision: deny
- id: gate-shell
action: shell_exec
target: "*"
decision: require_approval
- id: deny-network
action: network
target: "*"
decision: deny
Example Action Requests
1. ALLOW — Writing to workspace:
{
"actionType": "file_write",
"target": "/app/workspace/output.json",
"agentId": "docker-agent",
"decision": "ALLOW",
"rule": "allow-write-workspace",
"evaluationTime": "0.4ms"
}
2. DENY — Writing to system directory:
{
"actionType": "file_write",
"target": "/etc/passwd",
"agentId": "docker-agent",
"decision": "DENY",
"rule": "deny-write-system",
"evaluationTime": "0.2ms"
}
3. ALLOW — Running Python script:
{
"actionType": "shell_exec",
"target": "python3 /app/workspace/analyze.py",
"agentId": "docker-agent",
"decision": "ALLOW",
"rule": "allow-python",
"evaluationTime": "0.3ms"
}
4. DENY — Destructive shell command:
{
"actionType": "shell_exec",
"target": "rm -rf /app/workspace",
"agentId": "docker-agent",
"decision": "DENY",
"rule": "deny-destructive",
"evaluationTime": "0.2ms"
}
5. REQUIRE_APPROVAL — Unmatched shell command:
{
"actionType": "shell_exec",
"target": "apt-get install curl",
"agentId": "docker-agent",
"decision": "REQUIRE_APPROVAL",
"rule": "gate-shell",
"evaluationTime": "0.3ms"
}
Troubleshooting
Issue 1: Agent cannot reach SafeClaw sidecar
Symptom: ConnectionError: http://safeclaw:3100 from the agent container.
Fix: Ensure both services are in the same Docker Compose file and default network. Verify the depends_on condition waits for the SafeClaw healthcheck. Check docker compose logs safeclaw for startup errors.
Issue 2: Policy file not mounted in container
Symptom: SafeClaw returns ConfigError: safeclaw.config.yaml not found.
Fix: Verify the volume mount in docker-compose.yaml maps the host policy file to /app/safeclaw.config.yaml inside the container. The :ro flag makes it read-only, which is recommended.
Issue 3: Environment variables not loading
Symptom: SafeClaw returns 401 Unauthorized despite correct API key.
Fix: Ensure the .env file is in the same directory as docker-compose.yaml. Docker Compose loads .env automatically. Verify with docker compose config to see resolved environment variables.
Cross-References
- SafeClaw Policy Configuration Reference
- Glossary: Sidecar Pattern
- FAQ: How Does SafeClaw Handle Container Networking?
- SafeClaw vs Container-Level Isolation
- Use Case: Dockerized Multi-Agent Pipeline
Try SafeClaw
Action-level gating for AI agents. Set it up in your browser in 60 seconds.
$ npx @authensor/safeclaw