Skip to main content

Crate tonin

Crate tonin 

Source
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 the tonin command 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 the Service builder, 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-buildbuild.rs compile helper wrapping tonic-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 port 50052, 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 resulting PgPool or redis::Client is stored inside the reference-counted State struct.
  • 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, the tonin-proxy sidecar 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.

Modules§

commands