# What APIs Does OpenLogi Expose? A Complete Guide to the Rust-Based Logitech Control Interface

> Discover the APIs OpenLogi exposes: Tarpc IPC, a Rust library for hardware abstraction, and a CLI for scripting. Control your Logitech devices programmatically.

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

---

**OpenLogi exposes three distinct programmatic interfaces: a Tarpc-based IPC service for remote device control, a core Rust library for type-safe hardware abstraction, and a command-line interface for scripting and automation.**

OpenLogi is an open-source Rust application for managing Logitech HID++ devices. The project provides well-defined API surfaces that allow external code to enumerate peripherals, configure DPI settings, control RGB lighting, and monitor real-time state changes. This guide examines the concrete implementations in the AprilNEA/OpenLogi repository, including the `Agent` trait definition, the protocol versioning strategy, and the client-server architecture.

## Tarpc IPC Service: The Primary Public API

The main entry point for external applications is the **Tarpc IPC service**, defined in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs). This binary-to-binary Remote Procedure Call (RPC) contract enables GUI clients, scripts, and third-party tools to communicate with the background agent through strongly-typed methods.

### The Agent Trait and Protocol Versioning

The service centers on the `Agent` trait, annotated with `#[tarpc::service]`, which currently operates at **protocol version 29** (`PROTOCOL_VERSION = 29`). The contract guarantees backward compatibility through an append-only policy—new methods are added to the end of the trait without reordering existing definitions, as the on-wire enum indexes are determined by trait method order.

Key methods exposed by the `Agent` trait include:

- `protocol_version()` – Returns the wire-protocol integer for client-server handshake validation
- `status()` – Retrieves `AgentStatus` containing hook installation state and accessibility permissions
- `inventory()` – Returns `Vec<DeviceInventory>` describing all connected Logitech devices
- `set_dpi(route, dpi)` and `read_dpi(route)` – Configure pointer sensitivity using `DeviceRoute` and `Dpi` types
- `set_light(route, command)` – Transmit `LightCommand` instructions for brightness and power control
- `start_pairing(selector)`, `pair_device(address)`, `cancel_pairing()` – Orchestrate Bolt/Unifying receiver pairing workflows
- `snapshot()` – Atomically fetch complete agent state (status, inventory, and pairing state) in a single RPC call
- `observe(since)` – Long-polling endpoint for reactive state updates; blocks until agent state changes past the provided `Generation`
- `observe_action_ring(since)` – Monitor Actions Ring invocations for custom overlay implementations
- `declare_client(kind)` – Register client type (`Gui`, `Cli`, or `Overlay`) required for macOS dormancy gating

The trait generates both a client struct (`openlogi_ipc::client::AgentClient`) and server implementation via Tarpc macros. The client implementation resides in [`crates/openlogi-ipc/src/client.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/client.rs).

### Connecting to the Agent via IPC Client

To consume the API, instantiate `AgentClient` with a `LocalSocketTransport` (Unix domain socket on Linux/macOS, named pipe on Windows):

```rust
use openlogi_ipc::client::AgentClient;
use openlogi_ipc::transport::LocalSocketTransport;
use tarpc::client::Config;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let transport = LocalSocketTransport::new().await?;
    let client = Config::default()
        .set_timeout(Some(std::time::Duration::from_secs(5)))
        .build(transport)
        .await?;

    // Verify protocol compatibility before operations
    let version = client.protocol_version().await?;
    assert_eq!(version, openlogi_ipc::PROTOCOL_VERSION);

    // Retrieve current agent status
    let status = client.status().await?;
    println!("Hook installed: {}", status.hook_installed);
    Ok(())
}

```

### Modifying Device Settings

The API supports granular hardware control. For example, changing DPI requires constructing the appropriate core types and invoking the RPC method:

```rust
use openlogi_core::hid::{DeviceRoute, Dpi};
use openlogi_ipc::client::AgentClient;

async fn set_device_dpi(
    client: &AgentClient, 
    route: DeviceRoute, 
    dpi: u16
) -> anyhow::Result<()> {
    let dpi_val = Dpi::new(dpi);
    client.set_dpi(route, dpi_val).await?;
    Ok(())
}

```

The `set_dpi` method is defined in the `Agent` trait at [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs) lines 60-61, while the `Dpi` type resides in [`crates/openlogi-core/src/hid/dpi.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/hid/dpi.rs).

### Reactive State Observation

For applications requiring real-time updates without polling overhead, the `observe` method implements long-polling semantics:

```rust
use openlogi_ipc::{AgentClient, Observation, Generation};

async fn watch_state(mut client: AgentClient) -> anyhow::Result<()> {
    let mut gen: Generation = 0;
    loop {
        // Blocks until state changes, then returns immediately
        let obs: Observation = client.observe(gen).await?;
        println!("New generation {}: {:?}", obs.generation, obs.snapshot.status);
        gen = obs.generation;
    }
}

```

The `Observation` struct and `observe` method are defined in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs). This pattern is used by the official GUI to maintain synchronization with the agent.

## Core Rust Library: Platform-Agnostic Types

Beyond the IPC layer, OpenLogi exposes **openlogi_core**, a pure Rust library containing serializable data structures and hardware abstractions. Located in [`crates/openlogi-core/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/lib.rs), this library contains no asynchronous I/O or networking code—only types that can be statically linked by other Rust crates or used to implement alternative clients.

Key exported types include:

- `openlogi_core::device::DeviceInventory` – Describes HID++ capabilities and firmware versions
- `openlogi_core::hid::DeviceRoute` – Hardware addressing scheme for targeting specific devices
- `openlogi_core::hid::Dpi` – Wrapper type for sensor resolution values
- `openlogi_core::hid::LightCommand` – Enum representing on/off toggles and brightness levels
- `openlogi_core::binding::{ActionRingIcon, ActionRingSlot}` – Visual definitions for the Actions Ring overlay

All types implement `serde::Serialize` and `serde::Deserialize`, enabling seamless transmission over the IPC channel or persistence in TOML configuration files.

## Command-Line Interface: Scripting API

The **CLI binary** (`openlogi`) provides a ready-to-use consumer of the IPC service, implemented with `clap` in [`crates/openlogi-cli/src/cmd/mod.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-cli/src/cmd/mod.rs). Each sub-command parses arguments and forwards them to the IPC client, exposing functionality to shell scripts and automation tools.

Available commands include:

- `openlogi list` – Prints a JSON array of `DeviceInventory` objects by calling `client.inventory()` (see [`cmd/list.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/cmd/list.rs))
- `openlogi light <device> on|off|brightness <value>` – Maps to `set_light` RPC calls defined in [`cmd/light.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/cmd/light.rs)
- `openlogi diag dpi <device>` – Queries current DPI via `read_dpi`

Example usage enumerating connected hardware:

```bash
$ openlogi list

# => [{"name": "MX Master 3", "route": {...}, " DPI": 1000}, ...]

```

## Summary

- OpenLogi exposes three distinct API layers: a **Tarpc IPC service** for real-time bidirectional communication, a **Core Rust library** for hardware type definitions, and a **CLI** for shell scripting.
- The `Agent` trait in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs) defines the public RPC surface at **protocol version 29**, maintaining backward compatibility through append-only method signatures.
- Core types including `DeviceInventory`, `Dpi`, and `LightCommand` are defined in `openlogi_core` and exposed via [`crates/openlogi-core/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/lib.rs).
- The CLI implementation in `crates/openlogi-cli/src/cmd/` wraps IPC calls into sub-commands like `list`, `light`, and `diag`.
- All APIs support comprehensive device lifecycle management: enumeration, DPI configuration, lighting control, pairing workflows, and reactive state observation.

## Frequently Asked Questions

### What protocol version does OpenLogi use for IPC communication?

OpenLogi currently uses **protocol version 29**, defined as the constant `PROTOCOL_VERSION` in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs). The wire protocol maintains backward compatibility only when new methods are appended to the `Agent` trait; reordering methods would break the on-wire enum indexes, so the project follows strict append-only versioning.

### Can I use OpenLogi from programming languages other than Rust?

Direct integration requires speaking the Tarpc protocol over local sockets, which is technically feasible but non-trivial. The most accessible approach for non-Rust code is the **CLI interface**, which provides JSON output and accepts plain-text arguments. For deeper integration, you would need to implement the Tarpc client protocol (using the definitions in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs)) in your target language.

### How do I monitor device state changes without polling continuously?

Use the `observe(since)` method available on `AgentClient`. This implements server-sent long-polling where the RPC call blocks until the agent's internal `Generation` counter increments, then returns an `Observation` containing the complete state snapshot. The official GUI uses this mechanism in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs) to achieve reactive updates without network chatter.

### Where are the DPI and lighting control types defined?

The hardware abstraction types reside in the core library. `Dpi` is defined in [`crates/openlogi-core/src/hid/dpi.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/hid/dpi.rs), while `LightCommand` and `DeviceRoute` are located in `crates/openlogi-core/src/hid/`. These types are re-exported through `openlogi_core::hid` and are used by both the IPC service implementation and client consumers.