libgrite-ipc
Zero-copy inter-process communication over Unix domain sockets for high-performance local RPC.
libgrite-ipc defines the wire protocol between the grite CLI and the grite-daemon background process. It uses Unix domain sockets for low-latency local communication and rkyv for zero-copy serialization, enabling the CLI to delegate expensive operations to a warm daemon without the overhead of traditional RPC.
If you are building a Rust application that needs fast local IPC between a CLI and a background service, this crate provides a battle-tested protocol and implementation.
What This Crate Provides
Zero-Copy Serialization with rkyv
Traditional serialization (JSON, Protocol Buffers, MessagePack) requires parsing — converting bytes into heap-allocated structures. rkyv takes a different approach:
- Zero-copy deserialization — The serialized bytes are laid out in memory exactly as the Rust structs would be. Deserialization is a typecast, not a parse.
- No heap allocation — Archived types contain no pointers. They can be used directly from a memory-mapped file or socket buffer.
- Deterministic size — Archived types have a fixed size, making IPC message framing trivial.
This means a CLI request that takes microseconds to serialize with JSON takes nanoseconds with rkyv. When your agent is issuing hundreds of queries per session, this adds up to real time savings.
Unix Domain Socket Transport
Unix domain sockets provide several advantages over TCP for local communication:
- Lower latency — No network stack overhead. Messages pass through the kernel directly.
- Higher throughput — No packetization, no congestion control, no retransmission.
- File-system visibility — Socket paths are visible in the file system, making discovery and debugging straightforward.
- Permission control — Standard Unix file permissions apply to sockets.
Typed Request/Response Protocol
The IPC protocol is fully typed with IpcCommand and IpcResponse enums covering every CLI operation:
| Command | Response | Description |
|---|---|---|
IssueList |
IssueListResult |
Query issues with filters |
IssueCreate |
IssueCreateResult |
Create a new issue |
IssueShow |
IssueShowResult |
Get issue details |
IssueUpdate |
IssueUpdateResult |
Update issue properties |
IssueClose / IssueReopen |
IssueStateResult |
Change issue status |
IssueComment |
CommentResult |
Add a comment |
SyncPull / SyncPush |
SyncResult |
Synchronize with remote |
LockAcquire / LockRelease |
LockResult |
Distributed lock operations |
Doctor |
DoctorResult |
Health check |
Daemon Discovery
The DaemonLock type manages daemon lifecycle discovery:
- Lock file — The daemon writes a lock file containing its PID and socket path.
- Stale detection — If the daemon crashes, the lock file is stale. The next CLI invocation detects this and starts a new daemon.
- Socket resolution — The CLI discovers the correct socket path from the lock file, supporting multiple repositories and actors.
Key Types
IPC Client
use IpcClient;
// Connect to the daemon via Unix domain socket
let client = connect?;
// Send a command and receive a response
let response = client.send?;
IPC Server
use IpcServer;
// Bind a server socket
let server = bind?;
// Accept connections and handle commands
for request in server.incoming
Daemon Lock
use DaemonLock;
// Check if a daemon is running and get its socket path
if let Some = read? else
Quick Example
use ;
Performance
| Metric | Value | Notes |
|---|---|---|
| Serialization overhead | ~0ns | Zero-copy with rkyv |
| IPC round-trip | ~50µs | Unix domain socket on modern hardware |
| Throughput | 10,000+ req/s | Single client, warm connection |
| Message size | Compact | rkyv binary is typically 50-80% smaller than JSON |
Compare with JSON-over-HTTP localhost:
| Approach | Serialization | Transport | Total RTT |
|---|---|---|---|
| JSON + HTTP | ~50µs | ~200µs | ~250µs |
| rkyv + Unix socket | ~0ns | ~50µs | ~50µs |
| Speedup | Infinite | 4x | 5x |
Protocol Design
Message Framing
IPC messages use a simple length-prefixed framing:
+----------+------------------+
| Length | rkyv bytes |
| (4 bytes)| (Length bytes) |
+----------+------------------+
This is minimal overhead and trivial to implement correctly.
Why Not gRPC / Cap'n Proto / FlatBuffers?
We evaluated several IPC frameworks and chose a custom rkyv-based protocol:
- gRPC — Requires HTTP/2, protobuf codegen, and a runtime. Massive overkill for local IPC.
- Cap'n Proto — Excellent zero-copy design, but the Rust ecosystem is smaller and the API more complex than rkyv.
- FlatBuffers — Similar to Cap'n Proto. Good for cross-language, but we only need Rust-to-Rust.
- rkyv — Native Rust, zero-copy, simple API, excellent performance. The clear winner for our use case.
Why Not TCP?
Unix domain sockets are strictly better than TCP for local communication:
- No network stack = lower latency
- No port conflicts = simpler deployment
- File-system visibility = easier debugging
- Standard permissions = better security
The only downside is that Unix domain sockets are not portable to Windows. Grite currently targets Unix platforms only (Linux, macOS).
Use Cases Beyond Grite
libgrite-ipc is designed for the Grite CLI/daemon pair but its primitives are reusable:
- CLI + daemon pairs — Any application that wants a fast CLI frontend with a warm background service.
- Editor plugins — Language servers and editor extensions that need to query a local index.
- Multi-process Rust apps — Worker pools, job queues, or sandboxed plugins that communicate over IPC.
- Testing — In-process IPC servers for fast integration tests.
See Also
- Grite Repository — Main project and documentation
- grite-daemon — The background daemon that uses this protocol
- grite — The CLI frontend that speaks this protocol
- docs.rs/libgrite-ipc — Full Rust API documentation
License
MIT License — see LICENSE for details.