跳到正文
DEV Community· Cleiton Augusto Correa Bezerra·· 3 小時前AI 評分66

CleiMesh 用 Rust 將 AI 智能體權限限制到每次工具呼叫

Your AI agent runs with all your permissions. I built a cage for each tool call, in Rust

AI 導讀

作者以 Rust 開發 CleiMesh,將權限控制與執行隔離落實到 AI 智能體的每次工具呼叫,並為允許或拒絕的呼叫生成簽署收據。權限可沿委派鏈收窄;讀取帶有外部內容的郵件後,代理鏈中標記為 clean-only 的寫入會被拒絕。CleiMesh 只隔離經其執行的操作,不能阻止提示詞注入;專案目前仍處於 alpha 階段。

正文

A subagent got a simple task: read a project's docs and say how to run it. It read docs/readme.txt, found "make run", and then asked for one more file: ~/.ssh/id_rsa.

Why it asked doesn't matter. A hidden instruction in something it read, a buggy third-party tool, a model that "thought" the key would help. What matters is what happens next, and in most setups today the file is read. Not because anyone granted the subagent access to the key, but because nobody granted anything to anyone: the tool runs as your user, and your user can read your key.

There's an old name for this: ambient authority. The kernel doesn't know about agents. It sees user ana and processes of ana. It can't ask "was this Ana, Ana's agent, or the tool her agent's subagent downloaded yesterday?".

Where the usual defenses stop

  • Gateways and proxies check permissions at the door, then let the tool run natively with full access.
  • Agent sandboxes (NVIDIA OpenShell, AWS Strands Box, Anthropic's sandbox-runtime) contain the whole agent process with one policy. Useful, but every subagent and tool inside shares it.
  • Logs ask you to trust whoever wrote them.

I wanted something one level down: per tool call, decided by a permission that can only shrink as it is handed on, and proved afterwards.

Five pieces

CleiMesh is a Rust node (MIT or Apache-2.0) that puts five pieces together:

  1. Delegable, signed permissions. A token, following the IETF Attenuating Agent Tokens JWT profile. An agent can hand a narrower part to a subagent, offline, never more than it got.
  2. The permission becomes the sandbox. Tools run in WebAssembly (wasmtime + WASI) where nothing is allowed by default. The token decides the preopened directories, network or not, instruction and memory limits. A subagent granted ./project/docs can't physically open ./project.
  3. A signed receipt for every call, allowed or denied: Ed25519, hash-chained JSONL with a signed head, in the draft-sahu-agent-action-receipts format. Digests and pseudonyms, never the data. No receipt, no run.
  4. Don't trust the receipt, replay it. Calls run with a frozen clock and seeded RNG recorded in the receipt, so an auditor re-runs the call and compares the output byte for byte. A node that signs a false result is caught.
  5. Taint that follows the delegation chain. Once a tool marked --taints (mail, web) returns content to an agent, tools marked --clean-only (write, send) are refused for that agent's whole chain until a human clears it. Handing the work to a fresh subagent doesn't launder it.
# Reading the inbox brings in outside content; writing to the outbox acts on the world
cleimesh token issue <agent> --tool file_read --read ~/inbox --taints -o read.json
cleimesh token issue <agent> --tool file_write --write ~/outbox --clean-only -o write.json

A real model, a poisoned mail

I ran OpenClaw with a local Qwen3 4B on Ollama and a mail carrying a hidden instruction to write a secret to a file.

  • With OpenClaw's own file tools on, the model obeyed and wrote the file, without ever calling CleiMesh.
  • With only CleiMesh's tools, the read tainted the chain, the write was refused by the node, and the refusal is on a signed receipt.

That first act is the honest limit: CleiMesh only contains what goes through it. cleimesh doctor reads Claude Code's and OpenClaw's config and warns when the agent still has its own file or shell tools. And it doesn't "solve" prompt injection: the model can still be fooled, and an allowed action can still be the wrong one. What the node adds is a rule outside the model that the model can't argue with.

Numbers, measured

  • One static musl binary, 37 MB (13.7 MB gzipped), starts in ~2.5 ms.
  • A cached WASM instance per call is ~0.5 ms; a full call with two fsynced receipts is ~3 ms on a laptop.
  • A load test with 5,000 simulated agents, part of them hostile: zero escapes, a verified log, ~400 calls/s, bounded by fsync. Group commit is next.

Try it

curl -fsSL https://github.com/cleitonaugusto/cleimesh/releases/latest/download/install.sh | sh
# or from source:
cargo build -p cleimesh-cli
CLEIMESH=target/debug/cleimesh examples/demo/demo.sh

The demo runs in a fresh /tmp directory with a fake project, a fake ~/.ssh and throwaway keys. It's alpha: the threat model in the repo lists what it protects, what it doesn't, and the gaps before 1.0.

Repo: https://github.com/cleitonaugusto/cleimesh

I'd like opinions on two things: grouping receipt fsyncs without weakening "no receipt, no run", and the guest ABI (core module now, Component Model before 1.0).

I also wrote up the design, the attacks and the mistakes in a book, AI Agents in a Cage.

來源:DEV Community · dev.to