Skip to content

tenuo-ai/tenuo

Repository files navigation

tenuo

Capability tokens for AI agents.

CI Crates.io PyPI Docker Docs License

Tenuo Cloud — Early Access

Managed control plane with revocation, observability, and multi-tenant warrant issuance.

Request access →

Tenuo is cryptographic authorization infrastructure for AI agents. A useful mental model is a prepaid card instead of a corporate Amex: scoped capability tokens that expire with the task.

A warrant is a signed token specifying which tools an agent can call, under what constraints, and for how long. It is bound to a cryptographic key, and the caller must prove possession of that key (PoP). Verification is offline (~27μs), and delegation attenuates monotonically: authority can narrow but not expand. If an agent is prompt-injected, execution is still limited by warrant constraints.

Tenuo is designed for teams running tool-calling and multi-agent workflows where authorization must hold at runtime, not just at session start.

It can be deployed in-process or at boundary enforcement points (sidecar/gateway), with the same warrant semantics and enforcement behavior.

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 tenuo

Open In Colab Explorer Docker Demo Blog

Quick Start

from tenuo import configure, SigningKey, mint_sync, guard, Capability, Pattern
from tenuo.exceptions import AuthorizationDenied

# 1. One-time setup: generate a key and configure Tenuo
configure(issuer_key=SigningKey.generate(), dev_mode=True, audit_log=False)

# 2. Protect a function — calls are blocked unless a warrant allows them
@guard(tool="send_email")
def send_email(to: str) -> str:
    return f"Sent to {to}"

# 3. Mint a warrant that only allows sending to @company.com
with mint_sync(Capability("send_email", to=Pattern("*@company.com"))):
    print(send_email(to="alice@company.com"))  # -> "Sent to alice@company.com"
    
    try:
        send_email(to="attacker@evil.com")
    except AuthorizationDenied:
        print("Blocked: attacker@evil.com")  # -> "Blocked: attacker@evil.com"

Even if the agent is prompt-injected, enforcement still happens at the tool boundary. The warrant allows *@company.com; attacker@evil.com is denied.

When the mint_sync block exits, the warrant expires naturally. No manual cleanup or revocation flow is required.

30-second architecture:

  1. Issuer mints a root warrant for a task.
  2. Delegator attenuates scope for downstream agents/tools.
  3. Authorizer verifies warrant + PoP at the tool-call boundary.
  4. Signed receipt records the authorization decision and execution context.

Why Tenuo?

IAM answers "who are you?" Tenuo adds "what can this workload do right now for this task?" That gives teams a deterministic authorization boundary at agent speed.

Failure mode in agent systems Tenuo strength Practical outcome
Session roles outlive individual tasks Task-scoped warrants with TTL Authority disappears when the task ends
Delegation chains increase blast radius Monotonic attenuation at every hop Scope only narrows, never expands
Bearer credentials can be replayed Holder-bound proofs (PoP) Stolen warrants are unusable without the key
Runtime policy calls add latency and dependency risk Offline verification (~27μs) Enforcement holds under load without network round-trips
Teams need defensible audit evidence Signed authorization receipts Each decision is attributable and reviewable

How It Works

Tenuo implements Subtractive Delegation: each step in the chain can only reduce authority, never expand it.

┌──────────────────┐     ┌──────────────────┐     ┌──────────────────┐
│  Control Plane   │     │  Orchestrator    │     │  Worker          │
│                  │     │                  │     │                  │
│  Issues root     │────▶│  Attenuates      │────▶│  Executes with   │
│  warrant         │     │  for task        │     │  proof           │
└──────────────────┘     └──────────────────┘     └──────────────────┘
     Full scope     -->     Narrower      -->      Narrowest
  1. Control plane issues a root warrant with broad capabilities
  2. Orchestrator attenuates it for a specific task (scope can only shrink)
  3. Worker proves possession of the bound key and executes
  4. Warrant expires — no cleanup needed

What Tenuo Is Not

  • Not a sandbox — Tenuo authorizes actions, it doesn't isolate execution. Pair with containers/sandboxes/VMs for defense in depth.
  • Not prompt engineering — Tenuo does not rely on model instructions for security decisions.
  • Not an LLM filter — Tenuo gates tool calls at execution time rather than filtering model text.
  • Not a replacement for IAM — Tenuo complements IAM by adding task-scoped, attenuating capabilities on top of identity.

Key Features

Feature Description
Offline verification No network calls, ~27μs
Holder binding Stolen tokens are useless without the key
Semantic constraints 11 constraint types including Subpath, UrlSafe, Shlex, CEL — they parse inputs the way the target system will (why this matters)
Monotonic attenuation Capabilities only shrink, never expand
Framework integrations OpenAI, Google ADK, CrewAI, Temporal, LangChain, LangGraph, FastAPI, MCP, A2A, AutoGen

Integrations

OpenAI — Tool-call enforcement with streaming TOCTOU protection

from tenuo.openai import GuardBuilder, Subpath, UrlSafe, Range, Pattern

client = (GuardBuilder(openai.OpenAI())
    .allow("read_file", path=Subpath("/data"))        # Path traversal protection
    .allow("fetch_url", url=UrlSafe())                # SSRF protection
    .allow("transfer", amount=Range(max=1000))        # Value boundary enforcement
    .allow("send_email", to=Pattern("*@company.com"))
    .build())
# Out-of-scope recipient is denied at execution time

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])) # LangGraph

MCP — Model Context Protocol client and server-side verification

from tenuo.mcp import SecureMCPClient, MCPVerifier

# Client: injects warrant proofs into tool arguments
async with SecureMCPClient("python", ["server.py"]) as client:
    async with mint(Capability("read_file", path=Subpath("/data"))):
        result = await client.tools["read_file"](path="/data/file.txt")

# Server: verifies tool constraints offline before execution
verifier = MCPVerifier(...)
@mcp.tool()
async def read_file(path: str, **kwargs) -> str:
    clean = verifier.verify_or_raise("read_file", {"path": path, **kwargs})
    return open(clean["path"]).read()
More integrations: Google ADK, CrewAI, A2A, Temporal, FastAPI

Google 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)

CrewAI — Multi-agent crews with warrant-based authorization

from tenuo.crewai import GuardBuilder

guard = (GuardBuilder()
    .allow("search", query=Pattern("*"))
    .allow("write_file", path=Subpath("/workspace"))
    .build())

crew = guard.protect(my_crew)  # Enforces constraints across crew tools

A2A (Agent-to-Agent) — Warrant-based inter-agent delegation

from tenuo.a2a import A2AServerBuilder

server = A2AServerBuilder().name("Search Agent").url("https://...").key(my_key).trust(orchestrator_key).build()
@server.skill("search", constraints={"url": UrlSafe})
async def search(query: str, url: str) -> dict:
    return await do_search(query, url)

client = A2AClient("https://...")
warrant = await client.request_warrant(signing_key=worker_key, capabilities={"search": {}})
result = await client.send_task(skill="search", warrant=warrant, signing_key=worker_key)

Temporal — Durable workflows with warrant-based activity authorization

from tenuo.temporal import (
    TenuoTemporalPlugin, TenuoPluginConfig, EnvKeyResolver,
    AuthorizedWorkflow, execute_workflow_authorized,
)

plugin = TenuoTemporalPlugin(
    TenuoPluginConfig(key_resolver=EnvKeyResolver(), trusted_roots=[issuer_public_key])
)
client = await Client.connect("localhost:7233", plugins=[plugin])

@workflow.defn
class MyWorkflow(AuthorizedWorkflow):
    @workflow.run
    async def run(self, path: str) -> str:
        return await self.execute_authorized_activity(
            read_file, args=[path], start_to_close_timeout=timedelta(seconds=30),
        )

result = await execute_workflow_authorized(
    client=client,
    client_interceptor=plugin.client_interceptor,
    workflow_run_fn=MyWorkflow.run,
    workflow_id="wf-123",
    warrant=warrant,
    key_id="agent1",
    args=["/data/report.txt"],
    task_queue="my-queue",
)

See full Temporal examples: demo.py | multi_warrant.py | delegation.py

FastAPI — 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)}

Kubernetes — See Kubernetes guide


Documentation

Resource Description
Quickstart Get running in 5 minutes
Concepts Why capability tokens?
Constraints All 11 constraint types explained
Security Threat model and guarantees
OpenAI Direct API protection with streaming
Google ADK ADK agent tool protection
AutoGen AgentChat tool protection
A2A Inter-agent delegation
FastAPI Zero-boilerplate API protection
LangChain Tool protection
LangGraph Multi-agent graph security
CrewAI Multi-agent crew protection
Temporal Durable workflow authorization
MCP Model Context Protocol client + server verification

Requirements

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)

Optional Dependencies

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[crewai]"        # + CrewAI
uv pip install "tenuo[temporal]"      # + Temporal workflows
uv pip install "tenuo[autogen]"       # + AutoGen AgentChat (Python ≥3.10)
uv pip install "tenuo[mcp]"           # + official MCP SDK, client & server verification (Python ≥3.10)
uv pip install "tenuo[fastmcp]"       # + FastMCP (TenuoMiddleware / FastMCP servers)

Docker & Kubernetes

Try the Demo — See the full delegation chain in action:

docker compose up

This 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.22  # Sidecar for warrant verification
docker pull tenuo/control:0.1.0-beta.22     # 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.


Deploying to Production

Self-hosted Tenuo is free forever. The core library and sidecar run entirely in your infrastructure — no external calls at verification time.

Self-hosted checklist:

  • Store signing keys in a secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager) — not environment variables
  • Configure trusted_roots with your control plane's public keys
  • Ensure dry_run is disabled and warrants are required in all enforcement points
  • Enable audit callbacks and metrics for observability

See Security Model for the full threat model and production hardening guidance.

Tenuo Cloud adds managed warrant issuance, key rotation, revocation (SRL), observability dashboards, and multi-tenant isolation for teams that prefer a hosted control plane.


Rust

Building an authorizer sidecar, gateway, or high-throughput service in Rust? Use the core crate directly.

What you get in Rust:

  • Warrant minting, derivation, and verification
  • Monotonic attenuation enforcement across delegation hops
  • Typed constraint evaluation at execution time
  • Holder Proof of Possession (PoP) verification
  • Signed receipt generation for allow/deny decisions
[dependencies]
tenuo = "0.1.0-beta.22"

Use the Rust API when you need a language-native enforcement boundary without Python runtime dependencies.


Prior Art

Tenuo builds on capability token ideas described in CaMeL (Debenedetti et al., 2025). Inspired by Macaroons, Biscuit, and UCAN.

The token format and delegation protocol are being standardized as draft-niyikiza-oauth-attenuating-agent-tokens in the IETF OAuth Working Group. The core attenuation rules are formally verified with Alloy (capability and argument-key monotonicity) and Z3 (constraint-type subsumption bounds).

See Related Work for detailed comparison.


Featured In


Etymology

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.


Contributing

Contributions welcome. See CONTRIBUTING.md.

TypeScript SDK (Help Wanted)

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 dev@tenuo.ai.

Security issues: Email security@tenuo.ai with PGP (key, not public issues).


License

MIT OR Apache-2.0, at your option.

About

High-performance capability authorization engine for AI agents. Cryptographically attenuated warrants, task-scoped authority, verifiable offline. Rust core.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages