Skip to main content
Glama

CleiMesh

CI License: MIT OR Apache-2.0 DOI

Signed permissions for AI agents that the sandbox actually enforces, and receipts anyone can replay to check.

Leia em português

Four acts: a subagent granted only ./project/docs is blocked from ~/.ssh/id_rsa, from writing and from widening its token; a mail assistant reads a mail with injected instructions and from then on cannot write, not even through a fresh helper; receipts verify, replay and catch an edited log, with a privacy report; audit suggest narrows the token and a human clears the taint on the record

AI agents (Claude Code, Cursor, LangGraph, CrewAI...) call third-party tools and hand work to other agents, almost always with all of your permissions. One malicious tool, or one prompt injection hidden in an email the agent read, can read ~/.ssh and .env, delete files or leak data. Afterwards nobody can prove which agent did what, on whose behalf.

Most tools in this space check permissions at the door (a proxy or gateway) and then let the tool run with full access, and their audit logs ask you to trust whoever signed them. CleiMesh puts five pieces together:

  1. Delegable, signed permissions. You grant an agent a token; it can pass a narrower part to a subagent, offline, and never more than it received. Tokens follow the IETF Attenuating Agent Tokens JWT profile.

  2. The permission becomes the sandbox. Tools run in WebAssembly where nothing is allowed by default. The token decides which directories the tool can see, whether it has network, and its limits. A subagent granted ./project/docs cannot physically read ./project, even though its parent can.

  3. A signed receipt for every call, allowed or denied, hash-chained, with a signed log head, verifiable offline, in the format of draft-sahu-agent-action-receipts. Receipts hold digests and pseudonyms, never the data the call touched. No receipt, no run.

  4. Don't trust the receipt, replay it. Every call runs with a frozen clock and seeded randomness recorded in its receipt, so an auditor re-runs it and checks the output byte for byte. A node that signs a false result is caught. Only a deterministic sandbox can offer this; a proxy whose tools run natively cannot.

  5. Taint that follows the delegation chain, against prompt injection. Once a tool that brings in outside content (mail, web) returns to an agent, tools that act on the world refuse that agent's whole chain until a human clears it. The node enforces it, outside the agent: the agent can't forge or erase it, and handing work to a fresh subagent doesn't launder it.

On top: tokens that shrink to what the agent actually used (audit suggest), and privacy reports per data subject and per agent (GDPR, Brazil's LGPD), in English or Portuguese.

Where it fits

Agent sandboxes such as NVIDIA OpenShell, AWS Strands Box and Anthropic's sandbox-runtime contain the agent process: files, system calls and network, by a local policy. CleiMesh works one level down, on each tool call: what this tool, called by this agent or subagent, may touch, decided by a signed token that can only narrow as it is delegated, and proved afterwards by a signed receipt anyone can replay. They complement each other: run the agent inside one of those sandboxes to contain what it does outside CleiMesh (its own shell, its other tools), and route the tools that touch your data through CleiMesh.

Inside such a sandbox the node's host usually has a name rather than an IP; --node and --bootstrap take /dns4/HOST/tcp/PORT/p2p/PEER_ID. Step by step with NVIDIA OpenShell, tested: docs/openshell.md.

Agent sandboxes (OpenShell, Strands Box, sandbox-runtime)

CleiMesh

What is contained

the whole agent process

each tool call

Where the permission comes from

a policy file or a central gateway

a signed token the sandbox is built from

Subagents

share the agent's policy

get a narrower token, offline, never a wider one

Afterwards

decision logs

signed, hash-chained receipts, replayable byte for byte

Prompt injection

rules over the session's events (Strands Box)

taint on every holder of the delegation chain, cleared only by a human

(As of October 2026; check each project for what it does today.)

⚠️ Alpha, under active development. Don't rely on it to protect anything important yet. What it protects, what it doesn't and the gaps before 1.0 are in the threat model.

Try it

cargo build -p cleimesh-cli
CLEIMESH=target/debug/cleimesh examples/demo/demo.sh

The demo above runs in about 15 seconds (the recording pauses to be read), entirely inside a fresh /tmp directory with a fake project, a fake ~/.ssh, a fake mailbox and throwaway keys. Its four acts (containment, prompt injection, proof, the human decides) are also cut as short clips; it also runs in Portuguese with DEMO_LANG=pt. See examples/demo.

Use it from Claude Code (MCP)

Claude Code runs shell commands as your user, so the node must run as another one, or the agent could read the node key and lift its own restrictions. One-time setup: docs/separate-user.md (a cleimesh user and a systemd service).

# As the node's user, its key signs a token granting your agent read access to one project
cd / && sudo -u cleimesh cleimesh token issue $(cleimesh id --client) --tool file_read --read ~/code/my-project -o /var/lib/cleimesh/chain.json
sudo cat /var/lib/cleimesh/chain.json > ~/chain.json

# mcp serve only forwards each tools/call, with that token, to the node; the node runs it in the sandbox and keeps the receipts
claude mcp add cleimesh -- cleimesh mcp serve --node /ip4/127.0.0.1/tcp/7400/p2p/<NODE_ID> --token ~/chain.json

# What happened, and proof the log wasn't edited or cut
sudo -u cleimesh cleimesh audit tail
sudo -u cleimesh cleimesh audit verify

To try it without the setup, drop --node and the sudo -u cleimesh: the node then runs inside mcp serve, as your user, and says on start that this protection is off.

Use it from OpenClaw, Hermes Agent and other MCP clients

Any agent that speaks MCP over stdio can use the same mcp serve command. Same node and token as above; use absolute paths.

# OpenClaw
openclaw mcp add cleimesh --command cleimesh --arg mcp --arg serve \
  --arg --node --arg /ip4/127.0.0.1/tcp/7400/p2p/<NODE_ID> --arg --token --arg /home/you/chain.json
openclaw mcp doctor cleimesh --probe
// ~/.openclaw/openclaw.json: only the CleiMesh tools, none of OpenClaw's own file or shell tools
"tools": { "profile": "minimal", "alsoAllow": ["bundle-mcp", "cleimesh__*"],
           "deny": ["read", "write", "edit", "apply_patch", "exec", "process", "ls"] }
# Hermes Agent: ~/.hermes/config.yaml
mcp_servers:
  cleimesh:
    command: "cleimesh"
    args: ["mcp", "serve", "--node", "/ip4/127.0.0.1/tcp/7400/p2p/<NODE_ID>", "--token", "/home/you/chain.json"]

Tested in throwaway containers with a scripted model in place of an LLM (tests/agents, weekly in CI): a full OpenClaw 2026.10.3 and Hermes Agent v0.22 turn writes a draft, reads a mail carrying an injection, and is refused the next write by the node, with the refusal on a signed receipt. With a real local model (Qwen3 4B on Ollama) and OpenClaw configured as above, the agent read the mail through CleiMesh and its write was refused; with OpenClaw's own file tools left on, the same injection wrote the file without ever calling CleiMesh. CleiMesh only contains the tools that go through it: an agent that also keeps its own shell or file tools can still use them, so turn those off, filter them (OpenClaw's tool policy, Hermes' per-server tools), or run the agent inside a sandbox (see Where it fits).

OpenClaw with a local Qwen3 4B: with its own file tools it obeys a hidden instruction in a mail and writes a secret; with only CleiMesh's tools the write is refused after the read and the model explains why

A real local model, a poisoned mail, two acts: examples/demo-real-model.

Put an existing MCP server behind a token

# The fetch server, as the agent's only way to the web, tainting what it brings in
cleimesh token issue <agent> --tool fetch --taints -o fetch.json
claude mcp add fetch -- cleimesh mcp wrap --token fetch.json -- uvx mcp-server-fetch

mcp wrap shows the agent only the server's tools that the token grants, checks each call against the token's argument constraints and taint, and writes a signed receipt before passing it on. The wrapped server itself is not sandboxed: it runs natively and reaches whatever its OS user can, so a token granting it directories or network is refused instead of recorded as if it held, and its calls don't replay. For containment, the tool has to run in the WASM sandbox, or the server inside an agent sandbox.

Contain prompt injection

# 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

# After the agent reads a mail, file_write is refused for its whole chain:
#   file_write refused: holder … in this chain received untrusted content (receipt …)
cleimesh taint list
cleimesh taint clear <agent-peer-id> --reason "mail reviewed"   # a human decision, recorded as a receipt

Documentation

Usage guide

delegate, replay, shrink a token, audit and retention, token flags, writing a tool

NVIDIA OpenShell

the agent in an OpenShell sandbox, CleiMesh outside it, tested

Separate user

run the node as its own OS user, out of the agent's reach

Threat model

what is protected, what isn't, and the test behind each claim

The Ten Commandments

the principles, each with the tests that enforce it

Benchmarks

cost per call, reproducible with scripts/bench.sh

What works today

Part

State

WASM sandbox (wasmtime): no files, no network by default; time, instruction and memory limits

✅ tested

AAT delegation chains: issue, derive offline, verify (signatures, attenuation, proof of possession, replay protection)

✅ tested, incl. property tests and fuzzing

Token → sandbox: per-tool directories, network and limits, narrowed at every link, symlink-safe

✅ tested

Signed, hash-chained receipts; fail-closed; signed log head; retention with a receipt of the removal

✅ passes the receipt draft's official test vectors

Replayable receipts (audit replay): frozen clock, seeded randomness

✅ tested, incl. catching a node that signs a false result

Taint along the delegation chain (--taints, --clean-only, taint clear)

✅ tested: no laundering by delegating or switching tokens

Privacy report per data subject (keyed pseudonym) and per agent, GDPR/LGPD (audit report)

✅ legal review of the wording pending

cleimesh mcp serve (stdio, official rmcp SDK)

✅ tested with a real MCP client and with Claude Code (headless), including an injected mail

Remote calls between nodes (libp2p) with tokens

✅ tested

Self-shrinking permissions: the sandbox records which directories a call touched (by keyed pseudonym), audit suggest proposes the narrower token

✅ tested

Quarantine for untrusted content

📋 planned (C6)

RAG storage, multi-tenancy, adapters, HTTP/gRPC API

🧊 frozen until the core is solid

Cost

Preliminary, on a busy desktop (i7-10750H); reproduce with scripts/bench.sh, full table in docs/benchmarks.md:

Median

echo in the bare sandbox

0.46 ms

echo with a 2-token chain, sandbox grants and 2 fsync'd receipts

3.27 ms

Verify a chain of 1 / 16 tokens

98 µs / 1.06 ms

Reference: one Docker container per call

~320 ms

Most of the enforcement cost is the two receipt fsyncs.

Under load

5,000 simulated agents at once, a sixth of them in each role: honest readers and writers, and agents that read outside their grant, write after reading an injected mail, replay a proof or widen their token. Nothing got through, and the log verifies with exactly the receipts the calls imply (cleimesh-p2p/tests/load.rs). One node holds about 400 calls per second on a laptop, two fsynced receipts per call; past that the queue grows and late calls time out without running.

What it does not do

  • It doesn't make an agent want the right thing. It contains what a tool can reach and proves what happened.

  • An action allowed by the token can still be the wrong action (an allowed email with the wrong content). Human approval for risky actions is on the roadmap.

  • It only isolates tools that run in its sandbox. Native MCP servers behind mcp wrap go through the token and leave receipts, but are not isolated; anything else, not even that.

  • Taint approximates "the agent read untrusted content" by "the node returned output of a --taints tool to this chain": content that reaches the agent some other way doesn't taint it. It is coarse on purpose: after reading a mail the agent loses clean-only tools until a human clears it. The policy is only as good as the tools' labels.

  • Randomness inside the sandbox is seeded and the seed is in the receipt, so a tool must not generate secrets there. Calls that read directories or use the network don't replay yet; the call's input stays with the data controller, not in the receipt.

  • Someone with disk access could roll back both the log and its head together; keep a head elsewhere (audit head) and check with audit verify --head.

  • Taint is kept per node: the same token used on two nodes is tainted separately.

The full list, with what each protection rests on and the test that checks it, is in the threat model.

The principles behind it, each with the tests that enforce it: The Ten Commandments of the Agent Under Control.

The book

AI Agents in a Cage

This README says what CleiMesh does. The book says why: how agents end up with all of your permissions, how prompt injection gets through filters, why a permission should become the sandbox instead of a check at the door, what a signed receipt proves and what it does not, the agent with a shell that could absolve itself, and how the project was built and fixed, mistakes included. Chapter 10 maps GDPR, the EU AI Act, HIPAA, SOC 2 and Brazil's LGPD to the command that produces each piece of evidence.

Every command in it runs against the cleimesh binary before each edition, and the build fails if one stops doing what the text says.

The free sample shows the cover and the full table of contents. There is no publisher behind it, so if it helps you, an honest review goes a long way.

Development

cargo build --workspace --all-targets && cargo test --workspace
scripts/bench.sh     # benchmarks -> docs/benchmarks.md
scripts/fuzz.sh      # fuzzing (nightly + cargo-fuzz)

See CONTRIBUTING.md. Security issues: SECURITY.md.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Available Tools

4 tools
echoB
Read-onlyIdempotent

Echoes back the input message

NameRequiredDescriptionDefault
messageYesThe message to echo back

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true and openWorldHint=false, so the safety profile is fully covered elsewhere. The description adds nothing beyond restating the echo behavior — no note on output format, size limits, or edge cases such as empty messages.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single short sentence with no filler, and the core behavior is front-loaded. It is efficient, though so terse that it carries little information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, trivially idempotent utility with a fully described schema and complete annotations, this is essentially enough to call it correctly. Only a hint about the intended testing purpose would improve it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single 'message' parameter is documented as 'The message to echo back'. The description adds no syntax, length, or encoding guidance beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it echoes back the input message. That makes the tool's contract unambiguous and clearly distinct from the sibling tools (file_read, file_write, json_transform), though it spends no words explicitly differentiating itself from them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no statement of when to use this tool versus alternatives, no mention of the common use case (connectivity/round-trip testing), and no exclusions. Usage is only inferable from the one-line behavior.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

file_readA
Read-onlyIdempotent

Reads a file from the allowed filesystem paths

NameRequiredDescriptionDefault
pathYesPath to the file to read

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and openWorldHint=false. The description adds the allowed-filesystem-paths constraint, but does not mention return format, encoding behavior, error cases, or size limits. This adds some useful context but leaves behavioral gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no wasted wording. The core action and scope are stated immediately.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter read tool, the description is adequate to invoke it. However, with no output schema, it could clarify that the result is file contents, and it does not mention error behavior for missing or disallowed paths.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 100% description coverage for the only parameter, path, so the schema already documents it. The description adds no syntax, relative-vs-absolute path, or permission details beyond what the schema provides. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Reads a file'. The 'allowed filesystem paths' scope is useful. It distinguishes reading from the sibling file_write, but does not explicitly differentiate from json_transform or echo.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the read operation and the allowed-path constraint, but the description gives no explicit when-to-use guidance, no when-not-to-use guidance, and no comparison with siblings such as file_write.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

file_writeB
DestructiveIdempotent

Writes content to a file in the allowed filesystem paths

NameRequiredDescriptionDefault
pathYesPath to the file to write
contentYesContent to write to the file

TDQS

B3.2/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and idempotentHint=true, yet the description never says whether an existing file is truncated or overwritten, whether parent directories are created, or what happens on an out-of-scope path. For a destructive write, the overwrite semantics are the single most important behavioral fact and they are absent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler. It is efficient, though its brevity comes partly from omitting information an agent would need.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has no output schema and only two parameters, so the surface is small, but the description leaves open overwrite behavior, error conditions, and the return value. Adequate but with clear gaps for a destructive operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both parameters ('path' and 'content') are already documented in the schema. The description adds no syntax, encoding, or path-format guidance beyond that, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Writes content to a file') plus a scoping constraint ('allowed filesystem paths'). It does not explicitly distinguish itself from file_read or json_transform, but the write-versus-read distinction is unambiguous from the verb.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'allowed filesystem paths' clause hints at a precondition, but there is no statement of when to use this instead of alternatives, nor any exclusion such as 'not for appending' or 'use json_transform to modify JSON in place'. Usage is only implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

json_transformC
Read-onlyIdempotent

Transforms JSON data using a JQ-like filter

NameRequiredDescriptionDefault
inputYesInput JSON data
filterYesJQ-style filter expression

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, openWorldHint=false, so the safety profile is covered. Beyond that the description adds almost nothing: the qualifier 'JQ-like' hints at a dialect subset but never says which JQ features are unsupported, what errors look like for malformed filters, or what the output shape is.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single front-loaded sentence with no filler. It is efficient, though its brevity is partly under-specification rather than pure concision.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is simple (2 required params, no nesting in practice), but with no output schema the description should say what is returned — the filtered result, its type, or error behavior. That gap means an agent cannot fully predict the response shape before calling.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, with both 'input' and 'filter' described, so the schema carries the parameter burden. 'JQ-like filter' marginally narrows expectations for the filter parameter, but the description supplies no syntax examples or dialect constraints beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Transforms') and resource ('JSON data') plus the mechanism ('JQ-like filter'), so an agent knows exactly what the tool does. It is trivially distinguishable from the file/echo siblings, though the description never explicitly contrasts them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this versus alternatives (e.g., file_read for pulling JSON off disk, or echo for passthrough). No preconditions, no exclusions, no mention of what happens on invalid filters. The agent must infer applicability entirely from the one-line purpose.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedecho
    • First observedfile_read
    • First observedfile_write
    • First observedjson_transform

TDQS

A3.5/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: echo returns input, file_read reads files, file_write writes files, and json_transform applies a JQ-like filter. There is no meaningful overlap or ambiguity among the four tools.

Naming Consistency4/5

The set mostly follows a predictable snake_case resource_action pattern (file_read, file_write, json_transform). The only minor deviation is echo, which is a bare verb rather than a resource_action name.

Tool Count5/5

Four tools is a well-scoped size for a lightweight utility server. Each tool covers a distinct, useful operation without bloat or redundancy.

Completeness3/5

The file surface supports read and write but lacks obvious companion operations such as listing, deleting, or checking file existence. The JSON tool supports transformation but not validation or parsing, leaving notable gaps for an agent working in this utility domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors