How OpenLogi Handles Inter-Process Communication Between Agent, GUI, and Overlay

OpenLogi isolates all hardware access in the agent process and uses a cross-platform local socket with tarpc RPCs to let both the desktop GUI and overlay communicate asynchronously after a protocol version handshake and role declaration.

OpenLogi follows a strict multi-process architecture where the openlogi-agent holds exclusive hardware access while the desktop GUI (openlogi-desktop) and Actions‑Ring overlay (openlogi-overlay) remain isolated client processes. According to the AprilNEA/OpenLogi source code, all inter-process communication flows through the openlogi-ipc crate, which provides a platform-agnostic transport layer and versioned RPC protocol that multiplexes state observation and command issuance over a single byte stream.

Cross-Platform IPC Transport

Local Socket Implementation

The IPC transport uses Unix-domain sockets on macOS and Linux and named pipes on Windows. The implementation lives in crates/openlogi-ipc/src/transport.rs and handles length-delimited framing with bincode serialization for efficient, compact message encoding.

tarpc RPC Framework

All communication uses the tarpc framework for typed remote procedure calls. The transport exposes a Client struct that both the GUI and overlay instantiate via openlogi_ipc::client::connect(), creating a persistent, asynchronous connection to the agent’s local socket.

Connection Handshake and Role Declaration

Before issuing commands, every client must complete a two-step handshake defined in crates/openlogi-ipc/src/ipc.rs.

First, the client calls protocol_version() to verify wire compatibility. The current protocol version is defined as PROTOCOL_VERSION (currently 29). If the client and agent versions mismatch, the GUI triggers an agent restart while the overlay exits gracefully.

Second, the client declares its role using declare_client(context::current(), kind) with either ClientKind::Gui or ClientKind::Overlay. This declaration tells the agent whether to arm the full device interface or remain dormant for overlay-only operations.

// Connect and declare the GUI role
let client = openlogi_ipc::client::connect().await?;
client.client
    .declare_client(context::current(), ClientKind::Gui)
    .await?;

GUI Communication Patterns

The desktop GUI spawns a dedicated thread running a Tokio runtime to manage the IPC client, keeping the GPUI main thread responsive.

State Observation Loop

Inside crates/openlogi-desktop/src/services/ipc.rs, the GUI runs an observe_loop that repeatedly calls observe(last_generation). This RPC blocks until the agent’s observable state changes or a hold timeout expires, then returns a GuiUpdate::Snapshot that is forwarded to the GPUI loop over an mpsc channel.

Device Command Issuance

Device-specific commands—such as setting DPI, changing lighting effects, or initiating pairing—are sent through the same AgentClient methods as fire-and-forget RPCs. The agent reports asynchronous results via GuiUpdate variants rather than direct RPC responses.

// Blocking observe call from the GUI
let ctx = context::current();
let obs = client.observe(ctx, last_generation).await?;
// obs contains the new state snapshot

Overlay Communication Patterns

The overlay initializes its IPC runtime in spawn_ipc() inside crates/openlogi-overlay/src/agent.rs. After connecting and declaring ClientKind::Overlay, it runs two concurrent asynchronous tasks:

  • poll_invocations – calls observe_action_ring() to retrieve the current Actions‑Ring invocation state and pushes updates onto an mpsc::UnboundedReceiver for the overlay UI.
  • send_commands – receives UI events (Hover, Activate, Cancel) and forwards them to the agent via action_ring_hover, action_ring_activate, and action_ring_cancel.

The overlay implements command coalescing and retry logic directly in crates/openlogi-overlay/src/agent.rs to handle rapid user inputs without overwhelming the agent.

// Overlay observing the action ring
let obs = client.observe_action_ring(ctx, last_generation).await?;
if let Some(invocation) = obs.invocation {
    // Push to UI channel
}

Protocol Contract and Versioning

All RPC methods are defined in the Agent tarpc service located in crates/openlogi-ipc/src/ipc.rs. This contract strictly versions the wire format through PROTOCOL_VERSION, ensuring that both GUI and overlay clients speak the same language as the agent core.

The protocol is asynchronous and multiplexed, allowing the single local socket to carry concurrent request/response pairs and server-pushed state updates without blocking either client.

Summary

  • Transport: Cross-platform local sockets (Unix-domain or named pipes) with length-delimited bincode framing implemented in crates/openlogi-ipc/src/transport.rs.
  • Handshake: Clients verify PROTOCOL_VERSION (currently 29) and declare their role via ClientKind::Gui or ClientKind::Overlay.
  • GUI Pattern: Blocking observe() calls in a dedicated thread feed state snapshots to the GPUI loop via mpsc channels.
  • Overlay Pattern: Concurrent observe_action_ring() polling and command forwarding via action_ring_hover, action_ring_activate, and action_ring_cancel.
  • Contract: All RPC definitions live in crates/openlogi-ipc/src/ipc.rs and are consumed through openlogi_ipc::client::connect().

Frequently Asked Questions

What transport protocol does OpenLogi use for IPC?

OpenLogi uses a cross-platform local socket transport: Unix-domain sockets on macOS and Linux, and named pipes on Windows. The byte stream is framed with length-delimited headers and serialized using bincode, as implemented in crates/openlogi-ipc/src/transport.rs.

How does the GUI receive state updates from the agent?

The GUI spawns a dedicated Tokio thread that enters a blocking loop calling observe(last_generation). This method blocks until the agent state changes, then returns a GuiUpdate::Snapshot that is sent to the main GPUI thread over an mpsc channel.

Can the overlay operate independently of the GUI?

Yes. The overlay establishes its own IPC connection via openlogi_ipc::client::connect(), declares ClientKind::Overlay, and communicates directly with the agent without requiring the GUI to be running. The agent treats overlay connections as distinct client sessions.

What happens if the protocol versions mismatch?

If the client and agent report different PROTOCOL_VERSION values during the initial handshake, the connection aborts. The GUI implementation triggers an agent restart to force version alignment, while the overlay process exits immediately to prevent undefined behavior.

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 →