# OpenLogi Architecture: A Deep Dive into the Three-Tier Rust System

> Explore the OpenLogi architecture, a three-tier Rust system. Discover how it separates GUI, Agent, and orchestration logic for robust device management.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: architecture
- Published: 2026-08-21

---

**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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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 the `async-hid` crate.
- **`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 in [`src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/src/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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:

```bash

# List all discovered Logitech HID++ devices

openlogi list

```

This subcommand is implemented in [`crates/openlogi-cli/src/main.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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:

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/client.rs).

### Creating Custom UI Actions

The UI layer provides programmatic control over the Actions Ring:

```rust
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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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.