OpenLogi Architecture: A Deep Dive into the Three-Tier Rust System
OpenLogi is a three-tier native Rust application that separates the GUI, Agent, and shared orchestration logic into distinct IPC-connected processes, with device I/O and input hooks isolated in a privileged background service.
This architectural design enables secure, cross-platform control of Logitech HID++ devices while keeping the user interface unprivileged. According to the repository's AGENTS.md, the codebase follows strict modular separation with fifteen specialized crates under the crates/ directory, each handling a single responsibility from device discovery to input synthesis.
The Three-Tier Architecture
OpenLogi divides its runtime into three distinct tiers that communicate over local sockets, ensuring that high-privilege operations remain isolated from the user interface.
Tier 1: The GUI Process
The GUI is a pure IPC client built with GPUI that presents the user interface but never performs direct device I/O. It polls the Agent for state updates and renders the Actions Ring overlay. The desktop GUI implementation lives in crates/openlogi-desktop/src/main.rs, while shared UI primitives like ring geometry and locale negotiation reside in crates/openlogi-ui.
Tier 2: The Agent Process
The Agent is a background server that owns the input hook and all device I/O. Running as openlogi-agent, this binary manages the hotkey runtime, HID++ writes, DPI cycling, and session state. It exposes a tarpc-based API over a local Unix socket for the GUI and Overlay to consume. The entry point is located at crates/openlogi-agent/src/main.rs.
Tier 3: Shared Orchestration
Shared orchestration contains the core logic relied upon by both the GUI and Agent. This includes pure data types (TOML configs, device models, action catalogs), the IPC contract definitions, and platform-specific abstraction layers for input capture and injection.
Modular Crate Structure
The implementation follows a workspace-based architecture where each crate under crates/ has a single, well-defined responsibility as documented in the repository's architecture table in AGENTS.md.
Core Data and Device I/O
crates/openlogi-core: Contains pure data types with no I/O or async code, including TOML configuration schemas, device models, and action catalogs.crates/openlogi-hidpp: A vendored fork of the HID++ protocol crate handling low-level Logitech device communication.crates/openlogi-hid: Manages device discovery and HID++ writes using theasync-hidcrate.crates/openlogi-assets: Handles the device-render registry and cached asset fetching for UI icons and device images.
Agent and IPC
crates/openlogi-agent-core: Implements shared agent orchestration including the hook runtime, DPI cycling logic, and Actions-Ring session state management.crates/openlogi-ipc: Defines the tarpc-based IPC contract insrc/ipc.rs, using a local-socket transport with a versioned, append-only wire format for backward compatibility.crates/openlogi-hook: Contains OS-specific input capture implementations gated by#[cfg(target_os = …)]attributes.crates/openlogi-inject: Handles OS-specific input synthesis (CGEvent on macOS, uinput on Linux, SendInput on Windows).
User Interface
crates/openlogi-desktop: The main desktop GUI application using the GPUI framework.crates/openlogi-overlay: A cursor-centered Actions-Ring client that operates as a pure IPC consumer.crates/openlogi-ui: Shared UI code including ring geometry calculations, icon management, and GPUI asset sources.
Inter-Process Communication with tarpc
The GUI, Agent, and Overlay coordinate through a tarpc RPC framework running over a local Unix socket. The protocol definition in crates/openlogi-ipc/src/ipc.rs uses a versioned, append-only wire format, allowing newer clients to interoperate with older agents without breaking compatibility.
This design ensures that the GUI process runs without elevated privileges. All device I/O and input hooking occur within the Agent process, which communicates state changes back to the UI layer through the typed RPC interface.
Cross-Platform Abstraction
Platform-specific code is isolated using conditional compilation attributes. The crates/openlogi-hook crate selects the appropriate backend at compile time:
- macOS: CGEventTap API for global event monitoring
- Linux: evdev and uinput interfaces
- Windows: WH_MOUSE_LL low-level mouse hook
Similarly, input synthesis in crates/openlogi-inject provides platform-native implementations for generating keystrokes and mouse events, ensuring consistent behavior across operating systems while respecting each platform's security model.
Practical Examples
Listing Devices via CLI
The command-line interface provides diagnostic commands without requiring the GUI:
# List all discovered Logitech HID++ devices
openlogi list
This subcommand is implemented in crates/openlogi-cli/src/main.rs using clap for argument parsing.
Querying the Agent via IPC
Applications can programmatically interact with the agent using the IPC client:
use openlogi_ipc::client::OpenLogiClient;
use openlogi_ipc::protocol::DeviceInfo;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Connect to the local socket created by the agent
let mut client = OpenLogiClient::connect().await?;
// Retrieve current device list from the agent
let devices: Vec<DeviceInfo> = client.get_devices().await?;
for d in devices {
println!("{} – {}", d.serial_number, d.model);
}
Ok(())
}
The client implementation resides in crates/openlogi-ipc/src/client.rs.
Creating Custom UI Actions
The UI layer provides programmatic control over the Actions Ring:
use openlogi_ui::action::Action;
use openlogi_ui::ring::Ring;
fn main() {
// Define a shell command action
let my_action = Action::ShellCommand("xdotool key ctrl+alt+T".into());
// Add to the ring and display
let mut ring = Ring::new();
ring.add_action(my_action);
ring.show();
}
These primitives are defined in crates/openlogi-ui/src/lib.rs.
Summary
- OpenLogi implements a three-tier architecture separating GUI, Agent, and shared orchestration into distinct processes.
- Fifteen specialized crates under
crates/follow single-responsibility principles, isolating device I/O, input hooks, and UI components. - tarpc IPC over local Unix sockets enables secure communication between unprivileged GUI and privileged Agent processes.
- Platform abstraction uses compile-time conditional compilation to support macOS, Linux, and Windows with native input APIs.
- The versioned wire protocol ensures backward compatibility between different versions of the GUI and Agent.
Frequently Asked Questions
How does OpenLogi handle device communication securely?
The Agent process runs with necessary permissions to access HID devices and install system-wide input hooks, while the GUI operates as an unprivileged IPC client. This separation prevents the user interface from directly accessing hardware or requiring elevated privileges, reducing the attack surface. All communication occurs through the typed tarpc interface defined in crates/openlogi-ipc/src/ipc.rs.
What programming language and frameworks does OpenLogi use?
OpenLogi is written entirely in Rust. The GUI uses GPUI (a Rust UI framework), while IPC relies on tarpc for RPC communication. Device I/O utilizes async-hid for asynchronous HID operations, and the CLI uses clap for command-line argument parsing.
Can OpenLogi run on macOS, Linux, and Windows?
Yes. The codebase uses #[cfg(target_os = …)] attributes to gate platform-specific implementations. Input capture uses CGEventTap on macOS, evdev/uinput on Linux, and WH_MOUSE_LL on Windows. Input synthesis similarly adapts to each platform's native APIs, ensuring consistent functionality across operating systems.
How does the IPC protocol ensure backward compatibility?
The wire format is versioned and append-only, as specified in the AGENTS.md documentation. This design allows newer GUI clients to communicate with older Agent processes without breaking existing functionality. The tarpc-based contract in crates/openlogi-ipc maintains strict type safety while supporting this evolutionary approach to API changes.
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 →