2026-01-28 · Authensor

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

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

Try SafeClaw

Action-level gating for AI agents. Set it up in your browser in 60 seconds.

$ npx @authensor/safeclaw