How OpenLogi Separates GUI, Agent, and Overlay Components in a Multi-Process Architecture
OpenLogi employs a strict three-process design where the GUI (openlogi-desktop), Agent (openlogi-agent), and Overlay (openlogi-overlay) operate as isolated IPC clients and servers, ensuring device I/O remains centralized in the agent while UI components communicate exclusively through a shared Tarpc contract.
The AprilNEA/OpenLogi repository implements a Rust-based device management system that splits functionality across three distinct processes. This separation prevents UI crashes from disrupting hardware sessions and allows the background service to persist independently of user-facing applications. Understanding this architecture is essential for developers extending the codebase or troubleshooting process-specific failures.
The Three-Process Model
OpenLogi's architecture documentation in AGENTS.md (lines 38-44) specifies that three processes ship in every install: the GUI, the Agent, and the Overlay. Each component maintains strict isolation boundaries enforced by crate dependencies and IPC contracts.
GUI Process (openlogi-desktop)
The GUI process serves as the settings application where users edit TOML configuration files and issue commands. Located in crates/openlogi-desktop, this process acts strictly as an IPC client that never accesses hardware directly. All configuration changes travel over a local-socket Tarpc connection to the agent, making the GUI entirely stateless regarding device I/O.
Agent Process (openlogi-agent)
The agent functions as the always-on background daemon that owns all HID++ device sessions and input hooks. Implemented in crates/openlogi-agent/src/main.rs, it handles platform-specific event taps (CGEventTap on macOS, evdev/uinput on Linux, WH_MOUSE_LL on Windows) and manages device operations like button remapping and DPI adjustments. As the IPC server defined in crates/openlogi-ipc/src/ipc.rs, it listens on Unix sockets or Windows named pipes, serving as the single source of truth for hardware state.
Overlay Process (openlogi-overlay)
The overlay renders the cursor-centered "Actions Ring" UI on demand without depending on the desktop crate. Strict isolation rules in .claude/rules/gui.md (lines 11-14) prevent the overlay from importing openlogi-desktop or any desktop-only UI code. This minimal process, residing in crates/openlogi-overlay, acts as a pure IPC client that draws the ring and forwards user selections back to the agent for execution.
Inter-Process Communication via Tarpc
All three processes share the Tarpc-based IPC contract defined in crates/openlogi-ipc/src/ipc.rs. The agent initializes the server in its main.rs entry point, while both GUI and overlay instantiate openlogi_ipc::client::Client to invoke RPC methods like set_action and get_status.
Server initialization in the agent:
// crates/openlogi-agent/src/main.rs
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialise logging, device I/O, etc.
let ipc_server = openlogi_ipc::server::Server::new();
// Bind a Unix socket (or Windows pipe) and run the server
ipc_server.serve().await?;
Ok(())
}
Client usage from the overlay:
// crates/openlogi-overlay/src/main.rs
use openlogi_ipc::client::Client;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Connect to the agent's IPC socket
let mut client = Client::connect().await?;
// Ask the agent to execute the selected action
client.set_action(Action::ShowActionsRing).await?;
Ok(())
}
Process Lifetimes and Spawning Mechanics
Each process follows distinct lifecycle rules enforced by the operating system. On macOS, the agent runs as a launchd service specified in launch_agent/mod.rs, remaining alive regardless of GUI state. Linux installations use a systemd user unit (systemctl --user enable --now openlogi-agent.service) to maintain the daemon.
The overlay follows an on-demand lifecycle controlled by the GUI. When users trigger the "Show Actions Ring" shortcut, the desktop process spawns the overlay binary via std::process::Command and terminates it upon dismissal:
// crates/openlogi-desktop/src/ui.rs (simplified)
fn show_actions_ring() {
// Launch the binary as a separate process
std::process::Command::new("openlogi-overlay")
.spawn()
.expect("failed to start overlay");
}
This design ensures the GUI can restart without dropping device sessions, while the overlay's lightweight nature allows instant visibility changes without loading the full device stack.
Summary
- The agent (
openlogi-agent) owns all hardware I/O and runs as a persistent system service via launchd or systemd. - The GUI (
openlogi-desktop) operates as a stateless IPC client that modifies settings through RPC calls rather than direct device access. - The overlay (
openlogi-overlay) functions as an isolated, on-demand process that strictly avoids dependencies on the desktop crate. - All components communicate through the shared
openlogi-ipcTarpc contract, with the agent serving as the sole server.
Frequently Asked Questions
Why does OpenLogi use three separate processes instead of threads?
Separating components into processes prevents a UI crash from terminating active device sessions and allows the agent to run with different privileges or persist as a system service. This isolation ensures that restarting the settings GUI or dismissing the overlay never interrupts background input handling or hardware polling.
How does the overlay communicate without importing the desktop crate?
The overlay relies exclusively on the openlogi-ipc crate for communication, enforced by GUI rules in .claude/rules/gui.md that explicitly forbid importing openlogi-desktop. This architectural constraint ensures the overlay remains a lightweight, independent binary that only renders UI and forwards actions to the agent via Tarpc.
What happens to device I/O when the GUI is closed?
Device I/O continues uninterrupted because the agent runs as a separate system service (launchd on macOS or systemd on Linux). The GUI merely acts as a configuration client; closing it only terminates the settings interface while the background daemon maintains all active device sessions and input hooks.
Which file defines the RPC methods available to GUI and overlay?
The IPC contract resides in crates/openlogi-ipc/src/ipc.rs, which defines the Tarpc service interface containing methods like set_action and get_status. Both the agent implementation and client stubs import this shared definition, ensuring type-safe communication across process boundaries.
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 →