2026-02-02 · Authensor

How to Safely Run CrewAI Agents

To safely run CrewAI agents, add SafeClaw action-level gating. Install with npx @authensor/safeclaw and define per-agent deny-by-default policies that control which tools each crew member can invoke and what parameters those tools accept. CrewAI runs multiple AI agents collaboratively — each agent has a role, a goal, a backstory, and a set of tools. When a crew kicks off, agents delegate tasks to each other, call tools, and pass results between themselves, all without human intervention.

What CrewAI Agents Can Do (And Why That's Risky)

CrewAI orchestrates multi-agent workflows. The risks compound because multiple agents act simultaneously:

The core risk: you define agents with broad tools and set them loose. There is no built-in per-tool-call policy layer in CrewAI — if an agent has a tool, it can call it with any arguments.

Step-by-Step Setup

Step 1: Install SafeClaw

npx @authensor/safeclaw

Select SDK Wrapper when prompted.

Step 2: Get Your API Key

Visit safeclaw.onrender.com. Free-tier keys renew every 7 days, no credit card.

Step 3: Wrap CrewAI Tools Per Agent

The key principle with CrewAI is per-agent policies. Each agent should have only the permissions its role requires:

import { SafeClaw } from "@authensor/safeclaw";

const safeclaw = new SafeClaw({
apiKey: process.env.SAFECLAW_API_KEY,
});

// Researcher agent: read-only, web access allowed
const researcherTools = [
safeclaw.wrapTool(new FileReadTool(), "file_read", {
policy: "./policies/researcher.yaml",
}),
safeclaw.wrapTool(new WebSearchTool(), "network", {
policy: "./policies/researcher.yaml",
}),
];

// Developer agent: can write to src/ and run tests
const developerTools = [
safeclaw.wrapTool(new FileWriteTool(), "file_write", {
policy: "./policies/developer.yaml",
}),
safeclaw.wrapTool(new ShellTool(), "shell_exec", {
policy: "./policies/developer.yaml",
}),
];

const researcher = new Agent({
role: "Senior Researcher",
goal: "Research best practices",
tools: researcherTools,
});

const developer = new Agent({
role: "Software Developer",
goal: "Implement the solution",
tools: developerTools,
});

const crew = new Crew({
agents: [researcher, developer],
tasks: [researchTask, developTask],
process: Process.sequential,
});

Step 4: Define Per-Agent Policies

policies/researcher.yaml — Read-only, web search only:

version: 1
default: deny

rules:
- action: file_read
path: "${PROJECT_DIR}/docs/**"
effect: allow

- action: file_read
path: "${PROJECT_DIR}/src/**"
effect: allow

- action: file_read
path: "*/.env"
effect: deny

- action: network
host: "*.googleapis.com"
effect: allow

- action: network
host: "*"
effect: deny

policies/developer.yaml — Write to source, run tests:

version: 1
default: deny

rules:
- action: file_read
path: "${PROJECT_DIR}/**"
effect: allow

- action: file_write
path: "${PROJECT_DIR}/src/**"
effect: allow

- action: file_write
path: "${PROJECT_DIR}/tests/**"
effect: allow

- action: shell_exec
command: "npm test*"
effect: allow

- action: shell_exec
command: "npm run build*"
effect: allow

- action: shell_exec
command: "rm*"
effect: deny

- action: shell_exec
command: "git push*"
effect: deny

- action: network
host: "registry.npmjs.org"
effect: allow

- action: network
host: "*"
effect: deny

Step 5: Simulate Both Policies

npx @authensor/safeclaw simulate --policy policies/researcher.yaml
npx @authensor/safeclaw simulate --policy policies/developer.yaml

What Gets Blocked, What Gets Through

ALLOWED — Researcher reads documentation:

{ "agent": "researcher", "action": "file_read", "path": "/project/docs/api-spec.md", "verdict": "ALLOW" }

DENIED — Researcher tries to write a file:

{ "agent": "researcher", "action": "file_write", "path": "/project/src/fix.ts", "verdict": "DENY", "reason": "no file_write rules in researcher policy, default deny" }

ALLOWED — Developer runs test suite:

{ "agent": "developer", "action": "shell_exec", "command": "npm test", "verdict": "ALLOW" }

DENIED — Developer pushes to remote:

{ "agent": "developer", "action": "shell_exec", "command": "git push origin main", "verdict": "DENY", "reason": "git push* matches deny rule" }

DENIED — Developer agent fetches unknown URL from web scrape:

{ "agent": "developer", "action": "network", "host": "malicious-site.com", "verdict": "DENY", "reason": "host not in allowlist, default deny" }

Without SafeClaw vs With SafeClaw

| Scenario | Without SafeClaw | With SafeClaw |
|---|---|---|
| Researcher agent writes files despite being "read-only" by role description | LLM ignores role boundary, file written | Blocked — researcher policy has no file_write rules |
| Developer agent runs git push --force | Code pushed to remote | Blockedgit push* matches deny rule |
| Delegated agent scrapes website with prompt injection | Injection redirects subsequent tool calls | Tool calls from injected context are evaluated individually — each one must pass policy |
| Developer writes a new test file | File written to tests/ | Allowedtests/** is in developer write allowlist |
| Manager delegates to an agent with broader tools | No control over delegation chain | Each agent's tools are independently wrapped with agent-specific policies |

SafeClaw evaluates policies in sub-millisecond time with zero third-party dependencies. Every verdict is recorded in a tamper-proof SHA-256 hash chain audit trail. The control plane sees only action metadata. The client is MIT-licensed, 100% open source, backed by 446 tests under TypeScript strict mode.

Cross-References

Try SafeClaw

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

$ npx @authensor/safeclaw