Cryptographic Access Control for AI Agents
When you ask an AI agent (e.g., through OpenClaw) to "organize my Downloads folder", the agent receives that instruction as an LLM prompt. But nothing prevents the LLM from deciding to read /etc/shadow or delete your home directory.
CryptBond sits between OpenClaw and the LLM API. Before the agent can touch any file, CryptBond checks: "was this operation declared in the user's policy?" If not, it blocks the tool call.
User → OpenClaw → CryptBond Proxy → LLM API
↓
Blocks unauthorized tool_calls
Key terms:
- Policy (
policy.json) — You declare which directories and operations (read/write/delete) the agent may access - Token — A cryptographic signature bound to your policy; the agent cannot forge or modify it
- Proxy — The HTTP server that intercepts LLM responses and filters unauthorized tool calls
- FUSE — A Linux filesystem technology that enforces access at the OS kernel level (stronger than proxy, catches
bashcommands too)
- OpenClaw installed and running — verify with
curl http://localhost:18789/health - An LLM API that OpenClaw is configured to use (Ollama, MiniMax, OpenAI, etc.)
- Docker deployment: Docker 20+ and Docker Compose V2
- Manual deployment: Python 3.9+
Not sure which LLM API your OpenClaw uses? Check your OpenClaw config file:
- On the host:
cat ~/.openclaw/openclaw.json- In Docker:
docker exec openclaw cat /root/.openclaw/openclaw.jsonLook for the
baseUrlfield underproviders— that's your LLM API URL.Verify it works before proceeding:
# For Ollama curl http://localhost:11434/v1/models # For OpenAI-compatible APIs (replace URL and key) curl https://api.example.com/v1/models -H "Authorization: Bearer YOUR_KEY"
Before starting, answer two questions:
1. Where does OpenClaw run? Check with:
docker ps | grep openclaw- If you see a running container → OpenClaw is in Docker
- If nothing shows → OpenClaw is on the host
2. Where will CryptBond run?
- Docker (recommended) — works the same on all platforms (Windows, Mac, Linux)
- On the host via pip — requires Python 3.9+
You'll use these answers to pick the right URLs in Step 3 and Step 5 below.
Step 1: Clone and enter the Docker directory
git clone https://github.com/yujidong/cryptbond.git
cd cryptbond/dockerStep 2: Create your policy
Copy the example and edit it for your environment:
cp policy.example.json policy.jsonEdit policy.json — set resource to the directory the agent should access:
{
"description": "Agent can read and organize Downloads",
"resource": "/home/user/Downloads/**",
"operations": ["read", "write", "delete"],
"ttl": 300
}
**means "all files and subdirectories recursively". Use*for a single level only.What path should you use? The
resourcepath must match what the LLM agent actually outputs. To find out:
Agent runs in Paths look like Example resourceDocker container Container paths /root/.openclaw/workspace/**Linux / WSL2 native Linux paths /home/user/Downloads/**Docker + WSL2 mount WSL2 mount paths /mnt/c/Users/yourname/Downloads/**Not sure? Start OpenClaw without CryptBond, give it a task, and observe the file paths in its output. Those exact paths go into
resource.
Step 3: Configure the upstream URL
Edit docker-compose.cryptbond.yml — find CRYPTBOND_UPSTREAM and set it to the LLM API URL that your OpenClaw is currently using (see Prerequisites above for how to find it):
environment:
- CRYPTBOND_UPSTREAM=http://host.docker.internal:11434/v1 # ← change thisSee the Upstream URL Reference for common values.
Step 4: Start the proxy
docker compose -f docker-compose.cryptbond.yml up -dStep 5: Point OpenClaw to CryptBond
Edit your OpenClaw config file (~/.openclaw/openclaw.json on the host, or /root/.openclaw/openclaw.json inside Docker). Find the providers section and change only the baseUrl:
Before:
"your-provider-name": {
"baseUrl": "http://host.docker.internal:11434/v1",
"apiKey": "ollama-local"
}After:
"your-provider-name": {
"baseUrl": "http://localhost:8001/v1",
"apiKey": "ollama-local"
}Replace
your-provider-namewith the actual name in your config (e.g.,ollama-openai). OnlybaseUrlchanges — keepapiKeyand other fields unchanged.The old
baseUrlgoes intoCRYPTBOND_UPSTREAM. The newbaseUrlpoints to CryptBond. Choose the right one for your setup:
Your Setup How to tell New baseUrlBoth in Docker, same compose file Both services in one docker-compose.ymlhttp://cryptbond-proxy:8001/v1Both in Docker, separate compose files Two separate docker-compose.ymlfileshttp://host.docker.internal:8001/v1OpenClaw in Docker, CryptBond on host docker psshows OpenClaw but not CryptBondhttp://host.docker.internal:8001/v1OpenClaw on host, CryptBond in Docker docker psshows CryptBond but not OpenClawhttp://localhost:8001/v1
Step 6: Verify
curl http://localhost:8001/health # → {"status": "ok"}
curl http://localhost:8001/audit # → lists blocked operationsFirst create policy.json in your working directory (same format as Option A Step 2):
pip install cryptbond[proxy]
cryptbond proxy \
--config policy.json \
--upstream http://localhost:11434/v1 \
--port 8001
--upstreamis the LLM API URL your OpenClaw currently uses (the oldbaseUrlin your OpenClaw config). If you're developing CryptBond itself, usepip install -e ".[proxy]"(editable install from source).
Then configure OpenClaw as in Option A Step 5.
| Docker Proxy | pip Proxy | FUSE + Container | Python SDK | |
|---|---|---|---|---|
| Choose this if | You already use Docker | You prefer CLI tools | You need maximum security | You're building a custom agent |
| What it blocks | LLM tool calls (read/write/delete) | LLM tool calls | All file access, including bash | Only what your code checks |
| Can agent bypass? | No | No | No | Yes (if you forget to check) |
| Catches bash? | No | No | Yes | No |
| Platform | All | All | Linux only | All |
Most users should start with Docker Proxy (Option A). It's the simplest to set up and blocks the most common attack vector (LLM tool calls). If you need to prevent the agent from using
bashto access files, use FUSE. The Python SDK enforces access only when your code callsblock_unauthorized(). If you forget to add that call for a code path, that path is unprotected. The proxy and FUSE enforce automatically regardless of code.
FUSE (Filesystem in Userspace) creates a virtual directory where every file operation is checked at the OS kernel level. This is stronger than the proxy because it catches operations inside bash commands and subprocesses — the proxy only checks tool calls.
Note: FUSE requires a native Linux kernel. It does not work on Windows (including WSL2) or macOS. Windows and Mac users should use the proxy method (Option A or B) instead.
Create policy.json in your working directory (same format as Option A Step 2), then:
pip install -e ".[fuse]"
mkdir -p /mnt/safe
cryptbond mount --config policy.json /mnt/safe --backgroundVerify the mount is working:
ls /mnt/safe # Should list files allowed by policy
cat /mnt/safe/allowed-file.txt # Should succeed
cat /mnt/safe/../../etc/shadow # Should fail with permission deniedThe agent accesses files through /mnt/safe. Any file outside the policy is blocked by the kernel.
For full protection with OpenClaw in Docker:
docker run -v /mnt/safe:/workspace:ro ghcr.io/openclaw/openclaw:latestThe container's only file access is the FUSE mount — the agent cannot bypass it.
See Configuration Guide for details.
For custom Python agents:
from cryptbond.core import Intent
from cryptbond.integrations.openclaw import OpenClawGuard
guard = OpenClawGuard(workspace="/home/user/project", master_key=key)
guard.protect(Intent(prompt="Read files", resource="/home/user/project/**", operation="read"))
guard.block_unauthorized(path, "read") # raises AccessDeniedError if unauthorized
guard.stop()- The proxy intercepts LLM tool calls but cannot inspect file operations inside
bashcommands. Forbashprotection, use FUSE. - Non-file tool calls pass through unaffected.
- The proxy forces non-streaming mode for reliable filtering. Tokens auto-renew on expiry.
- When a tool call is blocked, CryptBond replaces it with an error message so the agent sees a clear "access denied" result and can react accordingly.
| Problem | Solution |
|---|---|
curl localhost:8001/health fails |
Check container is running: docker ps. Check logs: docker logs cryptbond-proxy |
ERROR: CRYPTBOND_UPSTREAM is required |
Set the env var in docker-compose.cryptbond.yml |
ERROR: Policy file not found |
Make sure policy.json is in the same directory as the compose file |
| OpenClaw can't connect to proxy | If OpenClaw is in Docker, use http://host.docker.internal:8001/v1 or container name |
| Agent operations not being blocked | Verify policy resource matches the paths the agent uses. Check /audit endpoint |
docker compose build fails |
Make sure you're in the cryptbond/docker/ directory with the correct -f flag |
The
/auditendpoint (curl http://localhost:8001/audit) returns a JSON log of all blocked operations, including timestamps, paths, and operations attempted. Use it to verify CryptBond is intercepting requests and to debug policy issues.
- Configuration Guide — Docker environment variables, deployment scenarios, upstream URL reference, OpenClaw config examples
- API Reference — Core, Proxy, SDK, Daemon module documentation
- Intent Parser — Natural language to intent parsing
from cryptbond.core import Intent, IntentBoundCrypto
import secrets
crypto = IntentBoundCrypto(secrets.token_bytes(32))
session = crypto.establish_session()
token = crypto.sign(
Intent(prompt="Read downloads", resource="/home/user/Downloads/**", operation="read"),
session_id=session.session_id, ttl=300,
)
allowed, reason = crypto.verify(token, "/home/user/Downloads/file.txt", "read", session_id=session.session_id)
# allowed == True
allowed, reason = crypto.verify(token, "/etc/shadow", "read", session_id=session.session_id)
# allowed == FalseUser declares intent (resource + operation)
↓
CryptBond signs HMAC-SHA256 token bound to intent + session + TTL
↓
Agent attempts a file operation
↓
CryptBond verifies the operation against the token scope
↓
Within scope → Execute Outside scope → AccessDeniedError
The token cannot be forged, tampered with, or transferred to another session.
cryptbond/
├── core/ # Cryptographic core (zero dependencies)
├── proxy/ # HTTP proxy for OpenClaw integration
├── integrations/openclaw/ # OpenClawGuard Python SDK
├── engine/fuse/ # FUSE filesystem enforcement (Linux)
├── daemon/ # Key isolation daemon
├── docker/ # Docker deployment files
├── sdk/ # Python SDK (CryptBondClient)
├── enforce.py # Token consumer for cryptbond protect
├── cli/ # Command-line interface
├── docs/ # Documentation
├── examples/ # Usage examples
└── tests/
pip install -e ".[dev]"
pytest tests/ -vMIT License