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

> OpenLogi ensures tarpc IPC wire format stability and binary compatibility with versioned encoding and regression tests. Learn how OpenLogi maintains protocol integrity.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-24

---

**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`](https://github.com/AprilNEA/OpenLogi/blob/main/ipc.rs)

The service definition resides in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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:

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/transport.rs), the setup looks like:

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/client.rs))

The client implementation in [`crates/openlogi-ipc/src/client.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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:

```rust
/// [`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`](https://github.com/AprilNEA/OpenLogi/blob/main/tests/wire_format.rs))

OpenLogi enforces protocol stability through a golden-file regression test located in [`crates/openlogi-ipc/tests/wire_format.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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.

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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:

```rust
// 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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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.