OpenLogi Three-Process Architecture: How It Separates GUI, Agent, and Overlay Functions

OpenLogi isolates user interface, hardware control, and on-screen overlay rendering into three distinct processes that communicate exclusively through a local tarpc socket, ensuring privilege separation, crash isolation, and low-latency interaction.

AprilNEA/OpenLogi implements a strict separation of concerns through its three-process architecture, isolating high-level user interactions, low-level device I/O, and visual feedback into independent binaries. This design prevents the configuration interface from interfering with hardware communication while maintaining responsive overlay graphics. The architecture relies on a versioned IPC contract defined in crates/openlogi-ipc/src/ipc.rs and documented in AGENTS.md to coordinate state across the system.

The Three Core Processes

OpenLogi divides responsibilities across three standalone binaries that run as separate operating system processes.

GUI Process (openlogi-desktop)

The GUI process presents the configuration interface, per-application profiles, and user settings. Implemented in crates/openlogi-desktop with its core logic in crates/openlogi-desktop/src/lib.rs, this process runs as a pure GPUI client and never communicates directly with HID devices.

All requests for device state, DPI adjustments, or Actions Ring activation flow through the tarpc socket to the Agent process. This isolation ensures that user interface crashes cannot destabilize hardware communication.

Agent Process (openlogi-agent)

The Agent process serves as the privileged daemon that owns input hooks, enumerates Logitech HID++ devices, and performs all hardware reads and writes. The binary entry point resides in crates/openlogi-agent/src/main.rs, while the core logic lives in crates/openlogi-agent-core/src/lib.rs.

This process is the sole component that opens /dev/hidraw on Linux, accesses Unifying and Bolt receivers, and manages macOS input monitoring permissions. It maintains central state for profiles and DPI cycles, receiving commands from both the GUI and Overlay via the IPC channel and replying with status updates or event notifications.

Overlay Process (openlogi-overlay)

The Overlay process draws the cursor-centered Actions Ring that appears when a profile hotkey activates. Located in crates/openlogi-overlay with its entry point at crates/openlogi-overlay/src/main.rs, this lightweight binary runs as a sibling process to the GUI (not a child process).

It imports the shared openlogi-ui crate for rendering but never performs device I/O. The Overlay subscribes to the IPC channel to receive current profile data and action icons, then reports user selections back to the Agent for execution.

Inter-Process Communication Layer

All three processes communicate through a local socket IPC layer implemented with tarpc. The shared contract lives in crates/openlogi-ipc/src/ipc.rs and defines the request/response protocol used across the system.

The contract is versioned and append-only, guaranteeing backward compatibility as the protocol evolves. Processes connect to a local socket (such as unix:/tmp/openlogi.sock on Linux) to exchange messages.

Implementation Examples

Connecting the GUI to the Agent

In crates/openlogi-desktop/src/lib.rs, the GUI creates a tarpc client to communicate with the Agent daemon:

use openlogi_ipc::ipc::{Client, Request};
use tarpc::client::Config;

// Create a tarpc client that talks to the local socket the agent listens on.
let client = Config::default()
    .protocol(tarpc::protocol::Bincode::default())
    .connect("unix:/tmp/openlogi.sock")
    .await?
    .into_client();

// Ask the agent for the current device list.
let devices = client.request(Request::ListDevices).await?;
println!("Connected devices: {:?}", devices);

Overlay Subscription and Rendering

The Overlay process subscribes to profile updates and renders the Actions Ring without device interaction:

use openlogi_ipc::ipc::{Client, Request, Response};

fn start_overlay() {
    // The overlay process is a separate binary; here we just spawn it.
    std::process::Command::new("openlogi-overlay")
        .arg("--socket")
        .arg("/tmp/openlogi.sock")
        .spawn()
        .expect("failed to start overlay");
}

// In the overlay (running the same IPC client):
async fn run_overlay(client: Client) {
    // Subscribe to profile updates.
    let mut updates = client.subscribe(Request::SubscribeProfile).await?;

    while let Some(profile) = updates.next().await {
        // Render the ring with the icons from `profile`.
        render_actions_ring(profile);
    }
}

// When the user picks an action, the overlay sends it back:
client.request(Request::ExecuteAction { action_id }).await?;

Agent Request Dispatch

In crates/openlogi-agent-core/src/lib.rs, the Agent handles incoming requests from both GUI and Overlay:

// Inside the agent's request dispatcher:
match request {
    Request::ExecuteAction { action_id } => {
        // Perform the HID++ command associated with the action.
        device.write_action(action_id)?;
        Ok(Response::Ack)
    }
    // other request variants …
}

Architectural Benefits

The OpenLogi three-process architecture delivers specific advantages for system stability and security.

Safety and Stability

Because the GUI runs independently from the Agent, the configuration interface can crash or restart without jeopardizing low-level HID communication. The Agent continues running as a background daemon, maintaining device connections and state regardless of GUI lifecycle events.

Privilege Isolation

On macOS, the Agent must hold the "Input Monitoring" permission to access HID devices. The GUI and Overlay run with normal user privileges and do not require this entitlement. This separation follows the principle of least privilege, limiting elevated permissions to the single process that requires hardware access.

Performance Optimization

The Overlay operates as a minimal process dedicated solely to graphics rendering, keeping latency low for the Actions Ring interaction. By offloading visual feedback to a separate binary, the system prevents UI-heavy operations in the GUI from interfering with time-sensitive overlay rendering.

Summary

  • OpenLogi divides functionality across three independent processes: the GUI (openlogi-desktop), the privileged Agent (openlogi-agent), and the visual Overlay (openlogi-overlay).
  • All inter-process communication occurs through a versioned tarpc contract defined in crates/openlogi-ipc/src/ipc.rs.
  • The Agent is the sole process accessing /dev/hidraw and HID++ devices, isolating hardware permissions from the user interface.
  • Crash isolation ensures GUI failures do not interrupt device communication handled by the Agent.
  • The Overlay runs as a lightweight sibling process for low-latency Actions Ring rendering without device I/O overhead.

Frequently Asked Questions

Why does OpenLogi use three separate processes instead of threads?

OpenLogi uses process isolation rather than threads to enforce privilege boundaries and crash containment. The Agent requires elevated permissions for hardware access on macOS and Linux, while the GUI and Overlay should run with standard user privileges. Additionally, if the GUI crashes, the Agent continues managing devices independently because it operates in a separate memory space.

How do the GUI and Overlay communicate with the Agent?

Both processes connect to the Agent through a local socket using tarpc, a RPC framework for Rust. The IPC contract in crates/openlogi-ipc/src/ipc.rs defines the message types, ensuring type-safe communication across process boundaries. The socket path (such as /tmp/openlogi.sock) is shared at startup, allowing each process to establish its client connection.

What happens if the Agent process crashes while devices are connected?

If the Agent crashes, all HID device connections terminate because the Agent is the sole process managing /dev/hidraw descriptors and HID++ protocol state. The GUI detects the disconnect through the IPC layer and can display a reconnection dialog. However, the Overlay will also lose its subscription channel and stop rendering until the Agent restarts and restores the IPC socket.

Can the Overlay run without the GUI active?

Yes, the Overlay runs as a sibling process rather than a child process of the GUI. It can connect directly to the Agent's IPC socket independently. This design allows the Actions Ring to appear when hotkeys trigger, even if the configuration window is closed, because the Agent maintains the active profile state and can launch or signal the Overlay directly.

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 →