How Serde Enums Are Handled Across the IPC Boundary in OpenLogi

OpenLogi serializes Serde enums across its IPC boundary using bincode's variant-index encoding with strict append-only rules, protocol versioning, and golden tests to guarantee backward compatibility.

OpenLogi's GUI and background agent communicate over a local-socket RPC layer built with tarpc and **bincode`. Every Serde enum that crosses this IPC boundary must follow a carefully enforced wire-format contract to prevent silent data corruption between mismatched binaries. This article examines the encoding strategy, versioning mechanism, and testing infrastructure that makes this safe.

Bincode Encoding of Serde Enums

OpenLogi uses bincode to serialize all RPC payloads. For enums, bincode writes the variant index—the zero-based position of each variant in the source code order. Critically, bincode does not use explicit discriminants like #[repr(u8)]. This means the physical layout of your source code directly determines the wire format.

In crates/openlogi-ipc/src/ipc.rs, enums like MonitorEvent, InventoryHealth, and PairingPhase are defined with standard Serde derives:

// crates/openlogi-ipc/src/ipc.rs
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum MonitorEvent {
    Button { button: String, pressed: bool },
    Scroll { delta_x: f32, delta_y: f32 },
    CaptureInterrupted,
}

The Button variant encodes as index 0, Scroll as 1, and CaptureInterrupted as 2. Reordering these variants would instantly break compatibility with existing agents.

The Append-Only Rule for Enum Variants

To maintain backward compatibility, OpenLogi enforces an append-only policy for both enum variants and RPC methods:

  • New variants must be added only at the end of the enum definition
  • Existing variants must never be removed or reordered
  • Method signatures in the Agent trait follow the same constraint

This rule exists because tarpc generates a request enum from the Agent trait. The method order in the trait becomes part of the wire format—changing it shifts every subsequent method's index.

Protocol Version Checking

Every incompatible change requires bumping PROTOCOL_VERSION in ipc.rs:

// crates/openlogi-ipc/src/ipc.rs
pub const PROTOCOL_VERSION: u32 = 29;

The GUI checks this constant on connection establishment and rejects connections from agents reporting a different version. This prevents silent misinterpretation of data when enum indices no longer align.

Golden Tests for Wire Format Stability

OpenLogi protects the serialization contract with golden tests located in crates/openlogi-ipc/tests/wire_format.rs. These tests serialize known values and assert exact byte sequences. Adding any variant forces regeneration of the golden file, creating an auditable record of every wire-format change.

Run the golden tests with:

cargo test -p openlogi-ipc --test wire_format

When a new variant changes the encoding, the test fails with a diff showing the new byte sequence. Developers must explicitly accept and commit this change, ensuring no accidental format drift.

Correct Bincode Configuration

The wire format uses tokio-serde's Bincode::default(), which applies bincode::DefaultOptions with varint encoding and little-endian byte order. Direct calls to bincode::serialize use different fixed options and produce incompatible bytes. All IPC serialization must route through the tokio-serde layer.

Practical: Adding a New MonitorEvent Variant

Here's the complete workflow for extending an IPC enum:

1. Append the variant without reordering:

// crates/openlogi-ipc/src/ipc.rs
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum MonitorEvent {
    Button { button: String, pressed: bool },
    Scroll { delta_x: f32, delta_y: f32 },
    CaptureInterrupted,
    // NEW: Battery level change reported by the driver.
    BatteryLevel { percent: u8 },
}

2. Increment the protocol version:

pub const PROTOCOL_VERSION: u32 = 30;  // ← bumped from 29

3. Run and update golden tests:

cargo test -p openlogi-ipc --test wire_format

# Review diff, accept new bytes, commit updated file

4. Handle the new variant in client code:

use openlogi_ipc::client::AgentClient;
use openlogi_ipc::MonitorEvent;

let events = client.poll_event_monitor().await.unwrap();
for ev in events {
    match ev {
        MonitorEvent::Button { button, pressed } => { /* … */ }
        MonitorEvent::Scroll { delta_x, delta_y } => { /* … */ }
        MonitorEvent::CaptureInterrupted => { /* … */ }
        MonitorEvent::BatteryLevel { percent } => {
            println!("Battery at {} %", percent);
        }
    }
}

Key Source Files

File Purpose
crates/openlogi-ipc/src/ipc.rs Serde-enabled enums (MonitorEvent, InventoryHealth, PairingPhase, PairingFailure) and PROTOCOL_VERSION constant
crates/openlogi-ipc/AGENTS.md Official wire-format contract documenting append-only rules and versioning policy
crates/openlogi-ipc/tests/wire_format.rs Golden-test suite validating exact bincode byte sequences
crates/openlogi-ipc/src/client.rs Client-side AgentClient wrapper for RPC calls

Summary

  • Serde enums in OpenLogi encode as bincode variant indices, not discriminants
  • Append-only modifications prevent index shifts that would break existing binaries
  • Protocol versioning (PROTOCOL_VERSION) blocks connections from incompatible agents
  • Golden tests enforce auditable, committed records of every wire-format change
  • tokio-serde's Bincode::default() must be used; raw bincode::serialize produces incompatible output

Frequently Asked Questions

What happens if I reorder enum variants in OpenLogi's IPC types?

Reordering changes every variant's bincode index, causing existing agents to deserialize values as wrong variants. The PROTOCOL_VERSION check may catch this, but only if you remembered to bump it. The golden tests will fail with unexpected byte sequences, alerting you to the break.

Why doesn't OpenLogi use #[repr(u8)] discriminants for IPC enums?

Bincode ignores explicit discriminants and always serializes the zero-based variant index. Adding #[repr(u8)] would create a false sense of stability—the wire format would still change if variants were reordered.

How do I know if my change requires a protocol version bump?

Any modification that adds, removes, or reorders enum variants or RPC trait methods requires a PROTOCOL_VERSION increment. The golden tests serve as the definitive signal: if they fail with a byte diff, you must bump the version and regenerate the golden file.

Can different OpenLogi versions communicate if they only added new variants?

No—by design. Even purely additive changes increment PROTOCOL_VERSION, causing the older peer to reject the connection. This strict policy prevents subtle bugs where an old binary encounters an unknown variant index and either panics or misinterprets data.

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 →