Capability tokens for AI agents.
Tenuo is a cryptographic authorization primitive for AI agents. Think prepaid debit card, as opposed to corporate Amex: ephemeral, scoped capability tokens that expire when the task ends.
It constrains ambient identity-based permissions with task-scoped capabilities that attenuate as they delegate. Offline verification in ~27μs. If an agent is prompt-injected, the authority still can't escape its bounds.
Status: v0.1 Beta - Core semantics are stable. APIs may evolve. See CHANGELOG.
# Using uv (recommended)
uv pip install tenuo
# Or standard pip
pip install tenuofrom tenuo import configure, SigningKey, mint_sync, guard, Capability, Pattern
from tenuo.exceptions import AuthorizationDenied
configure(issuer_key=SigningKey.generate(), dev_mode=True, audit_log=False)
@guard(tool="send_email")
def send_email(to: str) -> str:
return f"Sent to {to}"
with mint_sync(Capability("send_email", to=Pattern("*@company.com"))):
print(send_email(to="[email protected]")) # -> "Sent to [email protected]"
try:
send_email(to="[email protected]")
except AuthorizationDenied:
print("Blocked: [email protected]") # -> "Blocked: [email protected]"The agent can be prompt-injected. The authorization layer doesn't care. The warrant says *@company.com. The request says [email protected]. Denied.
IAM answers "who are you?" Tenuo answers "what can you do right now?"
| Problem | Tenuo's Answer |
|---|---|
| Static IAM roles outlive tasks | Warrants expire with the task (TTL) |
| Broad permissions, big blast radius | Constraints narrow on every delegation |
| Tokens can be stolen and replayed | Proof-of-possession binds warrants to keys |
| Central policy servers add latency | Offline verification in ~27μs |
- Not a sandbox - Tenuo authorizes actions, it doesn't isolate execution. Pair with containers/VMs for defense in depth.
- Not prompt engineering - No "please don't do bad things" instructions. Cryptographic enforcement, not behavioral.
- Not an LLM filter - We don't parse model outputs. We gate tool calls at execution time.
- Not a replacement for IAM - Tenuo complements IAM by adding task-scoped, attenuating capabilities on top of identity.
Tenuo implements Subtractive Delegation.
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Control Plane │ │ Orchestrator │ │ Worker │
│ │ │ │ │ │
│ Issues root │────▶│ Attenuates │────▶│ Executes with │
│ warrant │ │ for task │ │ proof │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Full scope --> Narrower --> Narrowest
- Control plane issues a root warrant
- Orchestrator attenuates it (scope can only shrink)
- Worker proves possession and executes
- Warrant expires - no revocation needed
| Feature | Description |
|---|---|
| Offline verification | No network calls, ~27μs |
| Holder binding | Stolen tokens are useless without the key |
| Constraint types | Exact, Pattern, Range, OneOf, Regex, Cidr, UrlPattern, Subpath, UrlSafe, Shlex, CEL |
| Monotonic attenuation | Capabilities only shrink, never expand |
| Framework integrations | OpenAI, FastAPI, LangChain, LangGraph, MCP |
| Component | Supported |
|---|---|
| Python | 3.9 – 3.14 |
| Node.js | Coming v0.2 |
| OS | Linux, macOS, Windows |
| Rust | Not required (binary wheels for macOS, Linux, Windows) |
uv pip install tenuo # Core only
uv pip install "tenuo[openai]" # + OpenAI Agents SDK
uv pip install "tenuo[google_adk]" # + Google ADK
uv pip install "tenuo[a2a]" # + A2A (inter-agent delegation)
uv pip install "tenuo[fastapi]" # + FastAPI integration
uv pip install "tenuo[langchain]" # + LangChain (langchain-core ≥0.2)
uv pip install "tenuo[langgraph]" # + LangGraph (includes LangChain)
uv pip install "tenuo[mcp]" # + MCP client (Python ≥3.10 required)Why semantic constraints? CVE-2025-66032 showed allowlists fail when shells interpret strings differently than validators. Tenuo's
Shlex,Subpath, andUrlSafeparse inputs the way the system will. Full analysis.
OpenAI - Direct API protection with streaming TOCTOU defense
from tenuo.openai import GuardBuilder, Subpath, UrlSafe, Shlex, Pattern
client = (GuardBuilder(openai.OpenAI())
.allow("read_file", path=Subpath("/data")) # Path traversal protection
.allow("fetch_url", url=UrlSafe()) # SSRF protection
.allow("run_command", cmd=Shlex(allow=["ls"])) # Shell injection protection
.allow("send_email", to=Pattern("*@company.com"))
.build())
# Prompt injection -> send_email(to="[email protected]") -> DENIEDGoogle ADK
from tenuo.google_adk import GuardBuilder
from tenuo.constraints import Subpath, UrlSafe
guard = (GuardBuilder()
.allow("read_file", path=Subpath("/data"))
.allow("web_search", url=UrlSafe(allow_domains=["*.google.com"]))
.build())
agent = Agent(name="assistant", before_tool_callback=guard.before_tool)A2A (Agent-to-Agent) - Warrant-based inter-agent delegation
from tenuo.a2a import A2AServer
@server.skill("search", constraints={"url": UrlSafe})
async def search(query: str, url: str) -> dict:
return await do_search(query, url)LangChain / LangGraph
from tenuo.langchain import guard_tools
from tenuo.langgraph import TenuoToolNode
protected = guard_tools([search_tool, email_tool]) # LangChain
graph.add_node("tools", TenuoToolNode([search, email])) # LangGraphFastAPI - Extracts warrant from headers, verifies PoP offline
@app.get("/search")
async def search(query: str, ctx: SecurityContext = Depends(TenuoGuard("search"))):
return {"results": do_search(query)}More: MCP | Kubernetes
Try the Demo - See the full delegation chain in action:
docker compose upThis runs the orchestrator -> worker -> authorizer demo showing warrant issuance, delegation, and verification.
Official Images on Docker Hub:
docker pull tenuo/authorizer:0.1.0-beta.6 # Sidecar for warrant verification
docker pull tenuo/control:0.1.0-beta.6 # Control plane (demo/reference)Helm Chart:
helm install tenuo-authorizer ./charts/tenuo-authorizer \
--set config.trustedRoots[0]="YOUR_CONTROL_PLANE_PUBLIC_KEY"See Helm chart README and Kubernetes guide.
| Resource | Description |
|---|---|
| Quickstart | Get running in 5 minutes |
| Concepts | Why capability tokens? |
| OpenAI | Direct API protection with streaming |
| Google ADK | ADK agent tool protection |
| A2A | Inter-agent delegation |
| FastAPI | Zero-boilerplate API protection |
| LangChain | Tool protection |
| LangGraph | Multi-agent graph security |
| MCP | Model Context Protocol client |
| Security | Threat model |
Tenuo builds on capability token ideas described in CaMeL (Debenedetti et al., 2025). Inspired by Macaroons, Biscuit, and UCAN.
See Related Work for detailed comparison.
- TLDR InfoSec - "Capabilities Are the Only Way to Secure Agent Delegation"
- Awesome Object Capabilities - Curated list of capability-based security resources
- Awesome LangChain
- Awesome LLM Agent Security
- Awesome LLMSecOps
| Feature | Status |
|---|---|
| A2A integration | Implemented (uv pip install tenuo[a2a]) |
| Google ADK integration | Implemented (uv pip install tenuo[google_adk]) |
| Multi-sig approvals | Partial (notary in v0.2) |
| TypeScript/Node SDK | Planned for v0.2 |
| Context-aware constraints | Spec under development |
| Revocation service | Basic revocation via Authorizer; distributed revocation in v0.3 |
Building a sidecar or gateway? Use the core directly:
[dependencies]
tenuo = "0.1.0-beta.6"See docs.rs/tenuo for Rust API.
Tenuo (/tɛn-ju-oʊ/ • Ten-YOO-oh)
From Latin tenuare: "to make thin; to attenuate." Authority starts broad at the root and is attenuated as it flows down the delegation chain.
Contributions welcome. See CONTRIBUTING.md.
We're planning a TypeScript/Node SDK for v0.2. If you're interested in leading or contributing to this effort, open an issue or email us at [email protected].
Security issues: Email [email protected] with PGP (key, not public issues).
MIT OR Apache-2.0, at your option.