OpenLogi tarpc IPC Versioned Wire Format: Protocol Stability and Binary Compatibility

OpenLogi guarantees binary compatibility between its GUI and background agent through a hardcoded PROTOCOL_VERSION constant, deterministic tarpc enum encoding, and golden-file regression tests that fail when the wire format changes unexpectedly.

The OpenLogi project implements inter-process communication (IPC) between its graphical interface and system agent using tarpc, an async RPC framework for Rust. This architecture depends on a strictly versioned binary wire format defined in the openlogi-ipc crate, where a monotonic protocol version and exhaustive golden-file tests prevent accidental breaking changes. Understanding this tarpc IPC implementation is essential for contributors modifying service contracts or debugging cross-process communication failures.

Core Protocol Contract in ipc.rs

The service definition resides in crates/openlogi-ipc/src/ipc.rs, where the #[tarpc::service] attribute macro generates the AgentClient stub and server-side dispatch code.

Method order is immutable because tarpc encodes request variants using their positional enum index. Reordering methods in the trait definition changes the on-wire binary representation without changing the source code semantics. Consequently, the repository treats method sequence as part of the public protocol surface.

The current wire format revision is declared as a constant:

pub const PROTOCOL_VERSION: u32 = 27;  // src/ipc.rs#L61

A comment at the end of the file (line 426) explicitly binds the contract to this value: "contract (see [PROTOCOL_VERSION])". Every intentional modification to request or response types, or to method ordering, requires incrementing this constant.

Transport Layer and Serialization (transport.rs)

The IPC layer uses local Unix sockets (or named pipes on Windows) wrapped in a tarpc transport with Bincode serialization. In crates/openlogi-ipc/src/transport.rs, the setup looks like:

use tarpc::serde_transport::Transport;
use tarpc::tokio_serde::formats::Bincode;

This configuration wraps the connected local socket stream in a transport that guarantees both GUI and agent processes use identical binary encoding. Bincode provides compact, fast serialization suitable for local socket communication where bandwidth is not constrained but latency must remain minimal.

Client-Side Version Validation (client.rs)

The client implementation in crates/openlogi-ipc/src/client.rs performs strict version negotiation during connection establishment. The constructor reads the server's reported PROTOCOL_VERSION and aborts the connection if it does not match the client's expected value.

Calls are dispatched through tarpc::context::Context, which carries request-wide metadata such as deadlines and cancellation tokens:

/// [`PROTOCOL_VERSION`](crate::PROTOCOL_VERSION) and act on the direction:

This ensures that version mismatches are caught immediately at connection time rather than manifesting as cryptic deserialization errors during method calls.

Golden Wire Format Testing (tests/wire_format.rs)

OpenLogi enforces protocol stability through a golden-file regression test located in crates/openlogi-ipc/tests/wire_format.rs. The test serializes a representative InventoryHealth request using the current code generation and compares it against a committed binary dump.

assert_eq!(PROTOCOL_VERSION, 27);  // tests/wire_format.rs#L104

If the generated wire bytes diverge from the golden file, the test fails with a message directing the developer to bump PROTOCOL_VERSION and regenerate the reference dump. This prevents accidental breaking changes from reaching production.

Version Bump Workflow

When modifying the IPC contract, follow this sequence to maintain protocol integrity:

  1. Modify request or response types, or rearrange service methods in ipc.rs.
  2. Execute cargo test -p openlogi-ipc --test wire_format — the test will fail with a mismatch error.
  3. Increment PROTOCOL_VERSION in src/ipc.rs to reflect the new wire format.
  4. Regenerate the golden binary dump using the repository's xtask automation script.
  5. Re-run the test suite to confirm the new version passes validation.

Because the protocol follows an append-only policy — new fields may be added without breaking existing binaries — the version number serves as the sole source of truth for compatibility checking between distributed components.

Practical Implementation Example

The following example demonstrates a GUI component connecting to the agent and invoking the health check method:

// Example: a GUI component calling the agent.
use openlogi_ipc::{client::connect, PROTOCOL_VERSION};
use tarpc::context::Context;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Connect to the agent's local socket.
    let (client, _bg) = connect().await?;
    // Build a context with a generous deadline.
    let ctx = Context::default().with_deadline(
        std::time::Instant::now() + std::time::Duration::from_secs(5)
    );

    // Simple health check – the request/response types are defined in the IPC service.
    let health = client.inventory_health(ctx).await?;
    println!("Agent health: {:?}", health);
    Ok(())
}

This code uses the generated AgentClient from ipc.rs and automatically respects the current PROTOCOL_VERSION during the connection handshake.

Summary

  • Protocol versioning is enforced through a hardcoded PROTOCOL_VERSION constant (currently 27) in crates/openlogi-ipc/src/ipc.rs.
  • Method order stability is critical because tarpc encodes RPC variants using enum indices; reordering methods breaks wire compatibility.
  • Bincode serialization over local sockets provides the underlying transport for all IPC communication.
  • Golden-file testing in tests/wire_format.rs guarantees that the binary wire format remains stable across releases unless explicitly version-bumped.
  • Strict client validation ensures that GUI and agent processes refuse to communicate if their protocol versions differ, preventing silent data corruption.

Frequently Asked Questions

What happens if the GUI and agent have mismatched PROTOCOL_VERSION values?

The client constructor in client.rs detects the mismatch during connection establishment and aborts immediately. This prevents partial deserialization or logic errors that could occur if incompatible binaries attempted to communicate using different wire formats.

Why does method order in the tarpc service trait affect binary compatibility?

tarpc generates an enum where each method corresponds to a variant. The framework encodes requests using the variant's discriminant index rather than a string identifier. Therefore, reordering methods in ipc.rs shifts all subsequent indices, causing the server to interpret requests as entirely different method calls without any runtime error indication beyond garbage data.

How do I safely add new fields to existing IPC request types?

OpenLogi's IPC follows an append-only policy. You may add new optional fields to request or response structs without breaking existing binaries, provided you do not change the method order or remove existing fields. After adding fields, run the golden-file test and follow the version bump workflow to commit the new wire format snapshot.

What serialization format does OpenLogi use for tarpc IPC?

According to the source in transport.rs, OpenLogi uses Bincode via tarpc::tokio_serde::formats::Bincode. This format offers zero-copy deserialization and compact binary representation, making it ideal for high-frequency local socket communication between the GUI and agent processes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →