ttrpc
A low-overhead GRPC-like RPC framework for Rust built for low-memory container runtimes.
Repository Health
Technical Analysis
ttrpc is the Rust implementation of the ttrpc protocol, a lightweight GRPC-style RPC framework designed by the containerd project for environments where memory and CPU headroom are scarce, such as container runtime shims, VM agents, and sandboxed init processes. It reuses Protocol Buffers for message definitions and code generation but replaces gRPC’s HTTP/2 transport with a minimal length-prefixed framing protocol over Unix domain sockets or vsock, cutting the runtime and dependency footprint that a full HTTP/2 stack would impose.
The crate ships both a synchronous, thread-based client/server implementation and an async/await implementation gated behind feature flags, plus a protoc plugin and a ttrpc-codegen crate for generating Rust service stubs from .proto files. It is a foundational dependency inside the containerd ecosystem, used by runc-adjacent shims, Kata Containers’ agent protocol, and other low-level container tooling that needs an RPC channel between a host process and a guest or sandboxed process without the overhead of a full gRPC stack.
What You Get
- Synchronous client and server built on OS threads and
mpscchannels, enabled by the defaultsyncfeature - Async/await client and server built on Tokio, enabled by the optional
asyncfeature, including async streaming support - A
protoccompiler plugin (ttrpc_rust_plugin) and attrpc-codegencrate for generating Rust request/response types and service traits from.protofiles - Unix domain socket, abstract Unix socket, and vsock transport support on Linux, with named-pipe support on Windows
- A
Contexttype for per-call metadata and timeouts, plus a typedError/Status/Codemodel compatible with gRPC-style status codes
Common Use Cases
- Implementing the OCI/containerd shim protocol between a container runtime and its per-container shim process
- Building the guest agent RPC channel for VM-based sandboxes (e.g. Kata Containers) where the host talks to a lightweight in-guest agent over vsock
- Any host-to-sandboxed-process or daemon-to-plugin RPC channel in Rust that needs gRPC-like ergonomics without pulling in a full HTTP/2 client/server stack
- Low-memory embedded or edge daemons that need structured RPC over Unix sockets rather than a bespoke text protocol
Under The Hood
Architecture: The crate splits into two parallel implementations behind Cargo feature flags — sync (default) and async — sharing a common proto module (generated Protobuf types plus MessageHeader/Codec framing logic in src/proto.rs) and error/context modules. In the sync path (src/sync/client.rs, src/sync/server.rs), Client::new_client spawns a dedicated sender thread that pulls (buf, reply_channel) pairs off an mpsc::Sender and writes framed messages to the socket, and a receiver thread that reads response frames and dispatches them to a HashMap<stream_id, SyncSender> guarded by a Mutex; the async path (src/asynchronous/) replaces the threads with Tokio tasks and adds a stream.rs module for bidirectional streaming and a shutdown.rs module for graceful connection teardown. Transport selection (Unix socket, abstract socket, vsock, or Windows named pipe) is resolved from a sockaddr string prefix and abstracted behind a sys module per platform.
Tech Stack: Core dependencies are protobuf (message encode/decode and codegen, workspace-pinned to 3.7.2), thiserror for the Error enum, nix/windows-sys for platform socket primitives, and crossbeam/log. The optional async feature pulls in tokio (rt, sync, io-util, macros, time, net), async-trait, async-stream, futures, and tokio-vsock on Linux/Android. The workspace also builds a compiler crate (the legacy protoc-gen-ttrpc plugin) and a ttrpc-codegen crate (a programmatic codegen API for build.rs), both versioned and released alongside the core ttrpc crate. Edition 2018, MSRV 1.70, license Apache-2.0.
Code Quality: Unit tests exist but are sparse and localized — 11 #[test] functions total, concentrated in src/proto.rs, src/context.rs, src/common.rs, and the Windows net module, with no tests in the sync or async client/server modules themselves; correctness there is exercised indirectly via tests/run-examples.rs, which runs the example/ binaries end-to-end. Error handling is consistent throughout, funneled through a single thiserror-derived Error enum (Socket, RpcStatus, Nix/Windows, LocalClosed, RemoteClosed, Eof, Others) with a project-wide err_to_others_err! macro for wrapping foreign errors, though several Mutex::lock().unwrap() calls and a couple of comment-documented invariants (e.g. weak-Arc use in the receiver thread) rely on careful manual reasoning rather than the type system to stay safe.
API Design: The public surface is small and gRPC-familiar — Client::connect, Server, MethodHandler, and a Context carrying metadata/timeout — but getting started requires an external toolchain step (installing protoc and the ttrpc_rust_plugin, or wiring ttrpc-codegen/protoc_rust_ttrpc::Codegen into build.rs) before any service code compiles, and the README’s setup instructions are terse relative to that complexity. Sync and async APIs are separate feature-gated modules with overlapping but not identical shapes, so switching between them mid-project requires re-reading both APIs rather than flipping one flag.