The Zentinel Agent Rust SDK provides a high-performance, async-first API for building agents that integrate with the Zentinel reverse proxy. Agents can inspect requests and responses, block malicious traffic, add headers, and attach audit metadata—all from Rust.
Quick Start
Add to your Cargo.toml:
[]
= "0.1"
= { = "1", = ["full"] }
= "0.1"
Create src/main.rs:
use *;
;
async
Run the agent:
Features
| Feature | Description |
|---|---|
| Simple Agent API | Implement on_request, on_response, and other hooks |
| Fluent Decision Builder | Chain methods: Decision::deny().with_body(...).with_tag(...) |
| Request/Response Wrappers | Ergonomic access to headers, body, query params, metadata |
| Typed Configuration | ConfigurableAgent trait with serde support |
| Async Native | Built on tokio for high-performance concurrent processing |
| Protocol Compatible | Full compatibility with Zentinel agent protocol v1 |
Why Agents?
Zentinel's agent system moves complex logic out of the proxy core and into isolated, testable, independently deployable processes:
- Security isolation — WAF engines, auth validation, and custom logic run in separate processes
- Language flexibility — Write agents in Python, Rust, Go, or any language
- Independent deployment — Update agent logic without restarting the proxy
- Failure boundaries — Agent crashes don't take down the dataplane
Agents communicate with Zentinel over Unix sockets using a simple length-prefixed JSON protocol.
Architecture
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│ Client │────────▶│ Zentinel │────────▶│ Upstream │
└─────────────┘ └──────────────┘ └──────────────┘
│
│ Unix Socket (JSON)
▼
┌──────────────┐
│ Agent │
│ (Rust) │
└──────────────┘
- Client sends request to Zentinel
- Zentinel forwards request headers to agent
- Agent returns decision (allow, block, redirect) with optional header mutations
- Zentinel applies the decision
- Agent can also inspect response headers before they reach the client
Core Concepts
Agent
The Agent trait defines the hooks you can implement:
use ;
use async_trait;
;
Request
Access HTTP request data with convenience methods:
async
Response
Inspect upstream responses before they reach the client:
async
Decision
Build responses with a fluent API:
// Allow the request
allow
// Block with common status codes
deny // 403 Forbidden
unauthorized // 401 Unauthorized
rate_limited // 429 Too Many Requests
block // Custom status
// Block with response body
deny.with_body
block.with_json_body
// Redirect
redirect // 302 temporary
redirect_permanent // 301 permanent
// Modify headers
allow
.add_request_header
.remove_request_header
.add_response_header
.remove_response_header
// Audit metadata (appears in Zentinel logs)
deny
.with_tag
.with_rule_id
.with_confidence
.with_reason_code
.with_metadata
// Routing metadata for upstream selection
allow
.with_routing_metadata
// Request more data before deciding
allow.needs_more_data
// Body mutations
allow
.with_request_body_mutation
.with_response_body_mutation
ConfigurableAgent
For agents with typed configuration:
use ;
use Deserialize;
use RwLock;
Running Agents
Command Line
The AgentRunner parses CLI arguments:
# Basic usage
# With options
| Option | Description | Default |
|---|---|---|
--socket PATH |
Unix socket path | /tmp/zentinel-agent.sock |
--log-level LEVEL |
trace, debug, info, warn, error | info |
--json-logs |
Output logs as JSON | disabled |
Programmatic
use AgentRunner;
async
Zentinel Configuration
Configure Zentinel to connect to your agent:
agents {
agent "my-agent" type="custom" {
unix-socket path="/tmp/my-agent.sock"
events "request_headers"
timeout-ms 100
failure-mode "open"
}
}
filters {
filter "my-filter" {
type "agent"
agent "my-agent"
}
}
routes {
route "api" {
matches {
path-prefix "/api/"
}
upstream "backend"
filters "my-filter"
}
}
Configuration Options
| Option | Description | Default |
|---|---|---|
unix-socket path="..." |
Path to agent's Unix socket | required |
events |
Events to send: request_headers, request_body, response_headers, response_body |
request_headers |
timeout-ms |
Timeout for agent calls | 1000 |
failure-mode |
"open" (allow on failure) or "closed" (block on failure) |
"open" |
See docs/configuration.md for complete configuration reference.
Examples
The examples/ directory contains complete, runnable examples:
| Example | Description |
|---|---|
simple_agent |
Basic request blocking and header modification |
configurable_agent |
Rate limiting with typed configuration |
body_inspection_agent |
Request and response body inspection |
Run an example:
See docs/examples.md for more patterns: authentication, rate limiting, IP filtering, header transformation, and more.
Development
This project uses mise for tool management.
# Install tools
# Build
# Run tests
# Run tests with output
# Check formatting
# Run clippy
# Build documentation
Without mise
# Requires Rust 1.75+
Project Structure
zentinel-agent-rust-sdk/
├── src/
│ ├── lib.rs # Public API exports and prelude
│ ├── agent.rs # Agent trait and AgentHandler
│ ├── decision.rs # Decision builder
│ ├── request.rs # Request wrapper
│ ├── response.rs # Response wrapper
│ └── runner.rs # AgentRunner and CLI handling
├── examples/ # Example agents
├── Cargo.toml
└── mise.toml
Protocol
This SDK implements Zentinel Agent Protocol v1:
- Transport: Unix domain sockets (UDS) or gRPC
- Encoding: Length-prefixed JSON (4-byte big-endian length prefix) for UDS
- Max message size: 10 MB
- Events:
configure,request_headers,request_body_chunk,response_headers,response_body_chunk,request_complete,websocket_frame,guardrail_inspect - Decisions:
allow,block,redirect,challenge
The protocol is designed for low latency and high throughput, with support for streaming body inspection.
For the canonical protocol specification, see the Zentinel Agent Protocol documentation.
Community
- Issues — Bug reports and feature requests
- Zentinel Discussions — Questions and ideas
- Zentinel Documentation — Proxy documentation
Contributions welcome. Please open an issue to discuss significant changes before submitting a PR.
License
Apache 2.0 — See LICENSE.