# What Is the Purpose of the `links` Table in OpenLogi Configuration?

> Discover the purpose of the links table in OpenLogi configuration. Learn how it maps devices to transport routes and stores link capabilities for effective device management.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-09-01

---

**The `links` table in OpenLogi's configuration maps each physical device to its distinct transport routes and stores per-link capabilities and user overrides, enabling accurate device management across multiple connection methods.**

The **OpenLogi** project is an open-source control suite for Logitech devices that handles complex scenarios where a single physical device appears through multiple transports. The `links` table, defined in [`crates/openlogi-core/src/config/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/device.rs), serves as the architectural solution that makes this possible.

## Why Per-Link Configuration Is Necessary

Modern Logitech peripherals often connect through multiple pathways simultaneously. A **Logitech G502 LIGHTSPEED** mouse, for example, may appear:

- Via its proprietary **LIGHTSPEED receiver** (wireless dongle)
- Via a direct **USB cable** connection

Each transport exposes different **capabilities** and requires independent **user settings**. The Hi-Res Wheel might behave differently over wireless versus wired connections. DPI preferences, scroll inversion, lighting profiles, and SmartShift thresholds may need per-transport tuning.

Without the `links` table, OpenLogi would collapse these distinct identities into a single flat record, causing configuration conflicts and capability misreporting.

## The `links` Table Structure and Implementation

In [`crates/openlogi-core/src/config/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/device.rs), the `DeviceConfig` struct contains the `links` field at **line 71**:

```rust
pub struct DeviceConfig {
    // ... other fields ...
    pub links: BTreeMap<String, LinkConfig>,
}

```

The `BTreeMap` uses **canonical route identifiers** (`DeviceStableId::route_key`) as keys. Each `LinkConfig` entry, defined at **line 84** in the same file, contains:

- **`capabilities`** — Features actually observed on that specific route (e.g., `"wired"`, wireless-specific attributes)
- **`overrides`** — User-defined settings that shadow device-level defaults for this transport only

## Route Keys: The Stable Identifier

The route key solves a critical persistence problem: devices sleep, disconnect, and reconnect, but their transport routes remain stable. This allows OpenLogi to reconstruct device state without rediscovery on every launch.

Route key formats follow predictable patterns:
- `receiver:aabbccdd:slot:3` — Wireless receiver on channel 3
- `direct:046d:c08d` — Direct USB connection (vendor `046d`, product `c08d`)

## Runtime Population and Persistence

The `links` table operates in two phases:

1. **Discovery-time population** — When a device comes online, OpenLogi scans available transports and creates or updates `LinkConfig` entries for each detected route
2. **Persistence to TOML** — The in-memory `BTreeMap` serializes to [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) under `devices.<id>.links.<route_key>` paths

This dual-phase approach ensures that **offline configuration editing remains possible** — users can tweak settings for a sleeping device, and OpenLogi applies them on next connection.

### TOML Configuration Example

From [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml):

```toml
[devices."unit:6be9d300".links."receiver:aabbccdd:slot:3"]
capabilities = "wired"

[devices."unit:6be9d300".links."direct:046d:c08d".overrides]
dpi = 1200

```

Here, DPI is overridden **only** for the direct USB link, while the receiver link retains device-default DPI behavior.

## Practical Code Operations

### Creating Default Link Entries

```rust
// From openlogi-core configuration handling
let mut device = config.devices.get_mut("unit:6be9d300").unwrap();
device.links.entry("direct:046d:c08d".to_string())
    .or_default(); // Inserts default LinkConfig with empty overrides, no capabilities

```

### Applying Per-Link Overrides

```rust
// Override DPI exclusively on the receiver link
device.links.get_mut("receiver:aabbccdd:slot:3")
    .unwrap()
    .overrides.dpi = Some(Dpi::new(1600));

```

The `or_default()` pattern ensures idempotent entry creation — repeated discoveries of the same route don't duplicate configuration data.

## Integration with Desktop Application

The OpenLogi desktop application consumes `device.links` for UI rendering and runtime state management:

- **[`crates/openlogi-desktop/src/state/scroll.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/state/scroll.rs)** — Iterates over `device.links` to apply per-link scroll sensitivity settings
- **[`crates/openlogi-desktop/src/state/devices/tests.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/state/devices/tests.rs)** — Contains unit tests verifying correct `links` entry insertion and retrieval

When a device reconnects, the agent consults the persisted `links` table to reapply route-specific overrides automatically.

## Per-Link vs. Per-Device Settings Resolution

OpenLogi uses a simple shadowing hierarchy:

| Priority | Source | Applies When |
|----------|--------|--------------|
| 1 | `LinkOverrides` for active route | Per-link entry exists with explicit value |
| 2 | `DeviceConfig` defaults | No link-specific override defined |
| 3 | Factory/device-class defaults | No configuration present at any level |

This ensures that **common settings are configured once**, while **transport-specific exceptions** are stored precisely where they apply.

## Summary

- The `links` table in OpenLogi configuration **indexes physical devices by their transport routes**, solving the multi-connection peripheral problem
- Each entry stores **measured capabilities** and **user overrides** specific to one transport path
- **Route keys** (`receiver:...`, `direct:...`) provide stable identifiers that survive device sleep cycles
- The implementation in [`crates/openlogi-core/src/config/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/device.rs) uses a `BTreeMap<String, LinkConfig>` for ordered, persistent storage
- Runtime code in `openlogi-desktop` leverages `device.links` for per-transport UI rendering and automatic setting reapplication

## Frequently Asked Questions

### How does OpenLogi handle a device connected via both USB and wireless simultaneously?

OpenLogi creates **separate `LinkConfig` entries** for each active transport. The `links` table keys (`receiver:aabbccdd:slot:3`, `direct:046d:c08d`) distinguish the routes, and per-link overrides apply independently. The UI typically presents both connections, allowing users to configure settings for each pathway.

### What happens to `links` entries when a device is disconnected or asleep?

Entries **persist in [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml)** indefinitely. Since route keys are stable identifiers derived from hardware topology rather than runtime handles, OpenLogi can **reconstruct device state** on reconnection without requiring the device to be present during configuration loading.

### Can users manually edit the `links` table in [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml)?

Yes. The TOML structure `devices.<id>.links.<route_key>` with nested `capabilities` and `overrides` sections is designed for **human readability and editing**. Changes take effect on next agent startup or device reconnection.

### Where is the `LinkConfig` struct defined, and what fields does it contain?

`LinkConfig` is defined at **line 84** of [`crates/openlogi-core/src/config/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/device.rs). According to the OpenLogi source code, it contains a `capabilities` field recording observed features and an `overrides` field of type `LinkOverrides` storing user-defined DPI, scroll, lighting, and SmartShift settings.