Expand description
§tonin
The one-person framework for Kubernetes microservices.
tonin is an opinionated framework designed to handle the entire lifecycle of a Kubernetes
microservice — from protocol buffers and gRPC routing to containerization, Helm chart rendering,
OpenTelemetry telemetry, and LLM-callable MCP tool exposure — all controlled from a single
declarative tonin.toml.
§Crate Architecture
The tonin ecosystem is split across specialized crates:
tonin(this crate) — The umbrella CLI binary. Installs thetonincommand to scaffold services (tonin service new), compile protocols, and deploy using Helm (tonin helm). It has no runtime API.tonin-sdk— The runtime framework. Provides theServicebuilder, capability interfaces (database, cache, events), JWT auth, and OTel telemetry.tonin-client— Peer-service client transport, retry policies, circuit breaking, and trace propagation without full server dependencies.tonin-build—build.rscompile helper wrappingtonic-build.
§The CLI Workflow (tonin binary)
To get a running, instrumented, mesh-secured gRPC service deployed to Kubernetes:
# 1. Scaffold a new service from built-in templates
tonin service new greeter --lang rust
cd greeter
# 2. Run locally (boots gRPC server on :50051 + MCP tool sidecar on :50052)
cargo run
# 3. Generate Helm chart templates based on tonin.toml
tonin helm generate
# 4. Deploy to Kubernetes (performs 'helm upgrade --install')
tonin helm upgrade --env prod§Declarative Configuration (tonin.toml)
Every infrastructure decision (replicas, security context, databases, caches, mesh ingress policies) is declared in a single file at the root of your service:
[service]
name = "greeter"
version = "0.7.8"
[deploy]
replicas = 2
mesh = "cilium" # auto-generates CiliumNetworkPolicies
namespace = "default"
mcp_sidecar = true # auto-exposes gRPC routes as LLM tools
[database]
engine = "postgres" # auto-provisions and injects Postgres
size = "10Gi"
[cache]
engine = "redis" # auto-provisions Redis StatefulSet§Writing a Service (tonin-sdk)
Services use the tonin-sdk crate to implement their handlers. Below is a complete
guide showing how to implement a gRPC trait, access database pools, extract user authentication,
and call other services.
§1. Implementing a gRPC Handler (with Auth & DB State)
Handlers are standard Rust structs that implement gRPC traits generated by tonic (prost).
You can access database connections and client pools directly from the State bundle, and read
the request’s JWT authentication context using task-locals.
use tonin_sdk::prelude::*;
use greeter_server::gen::greeter_v1_server::{Greeter, GreeterServer};
use greeter_server::gen::{HelloRequest, HelloReply};
#[derive(Clone)]
struct GreeterImpl {
// Store the pre-wired database pool
db: sqlx::PgPool,
}
#[async_trait]
impl Greeter for GreeterImpl {
async fn say_hello(
&self,
req: Request<HelloRequest>
) -> Result<Response<HelloReply>, Status> {
// 1. Retrieve the task-local JWT authentication context
let auth = tonin_sdk::auth::current();
if !auth.is_authenticated() {
return Err(Status::unauthenticated("missing or invalid token"));
}
// Access user identification principal
let user_id = auth.principal_id();
tracing::info!(?user_id, "serving authenticated gRPC request");
// 2. Perform raw database queries using sqlx (connection managed by state)
let username: String = sqlx::query_scalar("SELECT name FROM users WHERE id = $1")
.bind(user_id)
.fetch_one(&self.db)
.await
.map_err(|e| Status::internal(e.to_string()))?;
Ok(Response::new(HelloReply {
message: format!("Hello, {}!", username),
}))
}
}§2. Bootstrapping and Starting the Service
In your main.rs, initialize the connection state from the environment, register your gRPC
handlers, and spin up the runtime listener:
#[tokio::main]
async fn main() -> Result<(), tonin_sdk::Error> {
// Construct DB/Cache pools automatically from environment variables
let state = State::from_env().await?;
let handler = GreeterImpl {
db: state.pg()?.clone(),
};
// Service handles OTel tracing, logging, and graceful shutdown automatically
Service::new("greeter")
.with_auth(tonin_sdk::auth::default_verifier())
.handler(GreeterServer::new(handler))
.run()
.await
}§3. Outbound Calls to Peer Services (Service Discovery)
To call another service in the cluster, use tonin_sdk::discovery::service_url to resolve the
Kubernetes DNS route. The service mesh automatically handles mTLS encryption, load-balancing, and retries.
use greeter_client::gen::greeter_v1_client::GreeterClient;
async fn call_greeter() -> Result<(), tonin_sdk::Error> {
// Resolves to http://greeter.default.svc.cluster.local:50051
let url = tonin_sdk::discovery::service_url("greeter", "default");
let mut client = GreeterClient::connect(url).await?;
let response = client.say_hello(HelloRequest { name: "Alice".into() }).await?;
println!("Received: {:?}", response.into_inner().message);
Ok(())
}§Core Features
tonin simplifies distributed application development by providing built-in implementations of key microservice patterns:
- gRPC Server & Routing: Built on top of
tonic, providing standard server scaffolding, graceful shutdown, and automated client stub generation. Services can also expose a parallel HTTP port for metrics, health checks, or REST endpoints. - Model Context Protocol (MCP): Every service can act as an MCP tool host. By adding the
#[mcp_expose]attribute to your gRPC implementation, methods are automatically parsed into JSON schemas and served by a sidecar on port50052, allowing LLMs to invoke them natively. - Telemetry & Observability: Integrated OpenTelemetry (OTel) tracing and structured logging. W3C trace context is automatically extracted from incoming gRPC metadata and injected into outbound calls to maintain distributed request chains.
- Authentication & Zero-Trust: Structured token verifiers handle header extraction, JWT signature validation,
and JWKS keyset loading. The resulting user context is stored as a thread/task-local
AuthCtx, allowing any inner library code to query user claims safely. - Pre-Wired Connection State: Boot-time env variables (
DATABASE_URL,REDIS_URL, etc.) are parsed, validated, and connected to eagerly. The resultingPgPoolorredis::Clientis stored inside the reference-countedStatestruct. - Service Discovery: The framework provides simple helper functions to compile cluster-native DNS routes
(e.g.,
<service>.<namespace>.svc.cluster.local) for peer-to-peer communication. - Outbound Proxy Sidecar (
tonin-proxy): For services not written in Rust, thetonin-proxysidecar implements request coalescing (singleflight), response caching, circuit breaking, and backoff retries.
For more details, read the conceptual guides
or view the end-to-end example at examples/greeter.