How OpenLogi’s IPC Architecture Facilitates Communication Between GUI, Agent, and Overlay Processes
OpenLogi uses a local IPC channel built on tarpc and bincode over local sockets, with an append‑only protocol and explicit version handshake to synchronize the GUI and overlay while isolating all hardware I/O in a long‑running agent process.
The OpenLogi project's inter-process communication (IPC) design solves a critical architectural challenge: how to let a rich desktop GUI and a lightweight cursor overlay communicate with hardware devices without either process touching HID++ directly. According to the OpenLogi source code, this is achieved through the openlogi-ipc crate, which defines a strict RPC contract that all three processes share. This article examines the protocol design, client implementations, and communication patterns that make this architecture reliable and version-safe.
IPC Protocol Design: tarpc, bincode, and Append-Only Contracts
The foundation of OpenLogi's IPC architecture sits in crates/openlogi-ipc/src/ipc.rs. This file declares the shared RPC service used by the GUI, overlay, and agent, including core types like AgentClient, AgentSnapshot, GuiUpdate, and Command.
The wire format uses tarpc (an RPC framework for Rust) layered on bincode for serialization, transmitted over interprocess local sockets. A critical design invariant governs the protocol: the format is append-only. Method order and enum discriminants are never reordered or removed. Any structural change forces a bump of PROTOCOL_VERSION, documented in the human-readable contract at crates/openlogi-ipc/AGENTS.md.
This append-only discipline guarantees binary compatibility across GUI and overlay versions, preventing accidental protocol drift even when components ship on different release cycles.
The GUI Client: Long-Poll Observation and Reflex-Based Agent Launch
The GUI implementation lives in crates/openlogi-desktop/src/services/ipc.rs. It runs on the GPUI thread but spawns a dedicated OS thread that owns a Tokio runtime for async I/O.
Connection Lifecycle and Version Handshake
When IpcClient::spawn initializes, it delegates to openlogi_ipc::client::connect(). The returned AgentClient carries the agent's PROTOCOL_VERSION. The ensure function (lines 53–105) validates this against the GUI's compile-time constant:
- Match: The client proceeds to
declare_client, identifying itself as a GUI client. - Mismatch: The client either retries on
ConnectFailure::Unreachableor signalsGuiUpdate::OutdatedGuifor a mandatory restart.
This explicit handshake prevents version skew from corrupting device state.
The Long-Poll Loop
The GUI maintains a single observe RPC call continuously (observe_loop). This call carries the client's latest generation number; the agent responds only when its internal state changes, delivering a full AgentSnapshot.
This push-only model eliminates polling timers and ensures that reconnections automatically receive the complete current state. Because tarpc multiplexes RPC calls, the long-poll remains active during concurrent commands—state updates flow uninterrupted while device operations execute.
Reflex-Based Agent Launch
If the socket is unavailable, SpawnReflex governs agent startup:
- Immediate launch when the socket has never existed
- Bounded delay after connection loss (
SPAWN_AFTER_LOSS) - Retry period between attempts (
SPAWN_RETRY_PERIOD)
These rules prevent aggressive respawn loops while enabling fast recovery. The implementation appears around lines 85–114.
// Spawn the IPC client in the GUI (openlogi-desktop)
let ipc_client = openlogi_desktop::services::ipc::spawn(); // returns IpcClient
// Listen for snapshots
tokio::spawn(async move {
while let Some(update) = ipc_client.updates.recv().await {
match update {
openlogi_desktop::services::ipc::GuiUpdate::Snapshot(snapshot) => {
// Apply the new state to the UI
}
openlogi_desktop::services::ipc::GuiUpdate::Unreachable => {
// Show a banner that the agent is down
}
_ => {}
}
}
});
The Overlay Client: Minimal Surface, Shared State
The overlay process—responsible for rendering the ring around the cursor—uses the same IPC contract but with a reduced footprint. Its implementation appears in crates/openlogi-overlay/src/agent.rs.
The spawn_ipc() function provides an Ipc handle that supports:
- Receiving
AgentSnapshotupdates for rendering - Sending light-control commands via
OverlayCommand
Because the overlay imports shared types (Identity, RUN_ENV) from openlogi_ipc and uses the same connection logic, it observes exactly the same device state as the GUI. This reuse eliminates duplication and prevents synchronization bugs.
// Overlay asking the agent to change a light
let overlay_ipc = openlogi_overlay::agent::spawn_ipc(); // returns Ipc
let cmd = openlogi_ipc::Command::SetLight(route, LightCommand::Power(true), "logo".into(), 42);
let _ = overlay_ipc.send(cmd);
Command Multiplexing and State Consistency
All device-control requests—SetDpi, SetLight, StartPairing, and others—traverse the same tarpc connection. The handle function in the GUI client processes each Command and reports results via GuiUpdate variants like LightCommandResult.
This unified channel ensures:
- No race conditions between state observation and command execution
- Ordered delivery of operations to hardware
- Consistent failure reporting back to the UI layer
// Send a DPI change from the GUI to the agent
let cmd = openlogi_desktop::services::ipc::Command::SetDpi(route, new_dpi);
let _ = ipc_client.commands.send(cmd);
Core Source Files
| Path | Purpose |
|---|---|
crates/openlogi-ipc/src/ipc.rs |
RPC service definition, AgentSnapshot, Command, GuiUpdate, protocol version |
crates/openlogi-ipc/AGENTS.md |
Human-readable append-only contract and version policy |
crates/openlogi-desktop/src/services/ipc.rs |
GUI client, long-poll loop, SpawnReflex, command handling |
crates/openlogi-overlay/src/agent.rs |
Thin overlay client for snapshots and light commands |
crates/openlogi-agent/src/main.rs |
Agent server implementing the RPC interface (referenced, not shown) |
Summary
- Hardware isolation: All HID++ I/O lives in the agent process; GUI and overlay are unprivileged clients.
- Version safety: Append-only protocol with explicit
PROTOCOL_VERSIONhandshake prevents binary mismatches. - Efficient synchronization: Push-based long-poll eliminates polling overhead and ensures state consistency.
- Robust process management: Reflex-based launch rules balance fast recovery with respawn protection.
- Code reuse: Shared
openlogi-ipccrate gives GUI and overlay identical views of device state.
Frequently Asked Questions
What IPC technologies does OpenLogi use?
OpenLogi uses tarpc for RPC framing, bincode for binary serialization, and interprocess local sockets for transport. This stack provides zero-copy-friendly performance without requiring external dependencies like D-Bus or gRPC.
How does OpenLogi prevent protocol version mismatches?
The protocol declares a PROTOCOL_VERSION constant. On connection, clients receive the agent's version and compare it against their compile-time value. A mismatch triggers either retry logic or a hard GuiUpdate::OutdatedGui signal forcing restart. The wire format is append-only—methods and enum variants are only added, never reordered or removed.
Why does the GUI use a long-poll instead of WebSockets or polling?
The observe RPC implements server-sent updates: the agent holds the request open and responds only when AgentSnapshot changes. This achieves lower latency than polling timers, simpler state management than bidirectional WebSockets, and automatic state recovery on reconnection (the agent sends the full current snapshot).
Can the overlay and GUI run different versions?
Yes, within constraints. Both must share a compatible PROTOCOL_VERSION. Because the protocol is append-only, a newer GUI can typically speak to an older agent (it simply won't use newer methods), and an older GUI can speak to a newer agent (unrecognized variants are handled gracefully). Version mismatches that break compatibility trigger explicit errors rather than silent corruption.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →