Trentina
Trentina sits between your AI agents and everything they touch: MCP servers,
the web, Matrix, LLM providers and alerts. It stops prompt injection at every
ingress — or at a trusted boundary you declare — shrinks what reaches the
context window, and enforces a policy the agent cannot talk its way past. It is named after the 1377 trentino of
Ragusa, where ships anchored offshore for thirty days before anyone came
ashore: commerce keeps flowing, and nothing dangerous gets in.
Why Trentina
-
Security. Untrusted content crosses three independent layers at every
ingress to the trust boundary, which can be federated
with other instances: L1 deterministic checks, L2 a local classifier, L3 a
quarantined LLM with no tools. Semantic request guards
judge what goes out against a rule you write in English. What is and is not
defended is in Coverage.
Defense Pipeline · Benchmark ·
OWASP mapping
-
Token savings. Hidden tools, short names, compacted schemas, compressed
descriptions and minified responses: 52% fewer response bytes on
production traffic. Compression ·
Tool Filtering
-
Determinism. Literal request guards, literal response guards and
allowlists are evaluated by the gateway, so an agent that ignores its
instructions is stopped before the backend sees the call.
Literal Request Guards ·
Literal Response Guards
-
Authentication. Trentina is the OAuth authorization server for web
clients such as claude.ai: dynamic client registration, a provisioned
client, external issuers or a static bearer, chosen per profile.
Authentication
-
Architectural flexibility. A personal assistant, a coding agent and a
swarm each get their own profile, with their own backends, defense mode and
credentials — and an operator agent configures all of it through tools,
with no shell. Profiles · Operator
Quick Start
# The container image is the distribution: the L2 model, the parsers and
# their process isolation ship in it. There is no PyPI package.
podman run -d -p 127.0.0.1:8019:8019 \
-v ./profiles.yaml:/config/profiles.yaml:ro,Z \
-e TRENTINA_GATEWAY_ENABLED=true \
-e TRENTINA_PROFILES_PATH=/config/profiles.yaml \
-e TRENTINA_PROFILE_MYAGENT_TOKEN=your-token \
-e OPENROUTER_API_KEY=your-key -e TRENTINA_MODEL_PROVIDER=openrouter \
quay.io/crunchtools/trentina \
--transport streamable-http --host 0.0.0.0 --port 8019
L3 needs a key for one LLM provider. Any of Gemini, OpenRouter, OpenAI,
Anthropic or Ollama works. A minimal profiles.yaml:
profiles:
myagent:
auth:
bearer_token_env: TRENTINA_PROFILE_MYAGENT_TOKEN
backends:
web:
url: "internal://web" # Trentina's own content tools
tools_allow: ["*"]
gmail:
url: "http://gws-personal:8000/mcp"
tools_allow: # it may read and draft; you send
- search_gmail_messages
- get_gmail_message_content
- draft_gmail_message
defense:
enforcement: block
Then point Claude Code at it:
{
"mcpServers": {
"trentina": {
"type": "streamable-http",
"url": "http://localhost:8019/gateway/myagent/mcp",
"headers": { "Authorization": "Bearer your-token" }
}
}
}
Documentation
| Document | Description |
|---|
| MCP Gateway | One endpoint per profile: routing, tool names, circuit breakers |
| Per-Agent Profiles | Profile schema, modes, minifying, roles, multi-agent setup |
| Operator Profile | The agent that installs and runs the gateway |
| Configuration | Every environment variable |
| Authentication | Static bearer, OAuth proxy with DCR or a provisioned client, delegated issuers |
| Backend OAuth | Each profile signs in to OAuth-only backends with its own grant |
| Defense Pipeline | L1/L2/L3, modes, coverage and known gaps |
| Benchmark | Detection rates per layer and per L3 provider |
| Corpora | Every attack and benign set the defense is measured on |
| Prompt Packs | The judge's prompts tuned per model |
| Content Tools |
Development
uv sync --all-extras
uv run ruff check src tests
uv run mypy src
uv run pytest -v
The container image is built by CI, never locally; see CLAUDE.md. The demo
is recorded by demo.yml; its fixtures are in
docs/demo/.
License
AGPL-3.0-or-later