# OpenLogi TOML Config Schema: How to Write Configuration Files by Hand

> Learn the OpenLogi TOML config schema to hand-write your configuration files. Understand the top-level structure, global settings, device blocks, and keyboard bindings for OpenLogi.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: how-to-guide
- Published: 2026-09-06

---

**OpenLogi uses a single TOML configuration file where the top-level structure mirrors the `Config` struct in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs), requiring `schema_version = 7` alongside global settings, device blocks keyed by physical identifiers, and optional keyboard bindings.**

All user preferences in the AprilNEA/OpenLogi repository are serialized into a flat [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) file that the application automatically migrates from older schema versions on load. Understanding the exact schema definitions in the core Rust source allows you to hand-craft or edit configurations without the GUI.

## Top-Level Document Structure

The root of every OpenLogi TOML config must declare the schema version and can contain five optional top-level keys:

```toml
schema_version = 7                      # Required: must match current SCHEMA_VERSION

selected_device = "receiver:aabbccdd:slot:1"  # Optional: default device to display

[app_settings]                          # Global preferences

[devices]                               # Map of physical device configurations

[keyboard]                              # Global keyboard shortcuts

```

According to the source code in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs), the `Config` struct defines these fields with `schema_version` as a mandatory `u32`, while `app_settings` maps to the `AppSettings` struct and `devices` is a `BTreeMap<String, DeviceConfig>` keyed by stable physical identifiers like `"receiver:..."` or `"unit:..."`.

## Global Application Settings [app_settings]

The `[app_settings]` section controls application-wide behavior and maps directly to the `AppSettings` struct defined in [`crates/openlogi-core/src/settings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/settings.rs). All fields are optional; missing values fall back to `AppSettings::default()`.

Common configuration options include:

```toml
[app_settings]
launch_at_login = true                # Start OpenLogi when the user logs in

check_for_updates = false             # Disable automatic update checks

show_in_menu_bar = true               # Display tray icon

capture_mouse_events = true           # Enable mouse-event capture

smooth_scroll = false                 # Disable smooth scrolling

vertical_scroll_sensitivity = 14      # Scroll speed range: 1-30

auto_download_assets = true           # Download missing assets automatically

asset_source = "automatic"            # Asset fetch strategy

language = "en"                       # UI language ISO code

thumbwheel_sensitivity = 14           # Thumb-wheel speed

appearance = "system"                # UI theme: system / light / dark

ui_scale = "normal"                   # UI scale: normal / larger / larger-2x

device_view_mode = "grid"             # Device view layout: grid or list

app_icon = "openlogi"                 # macOS icon variant: openlogi or prism

```

Additional booleans such as `auto_install_updates` control silent update installation behavior.

## Per-Device Configuration [devices."<identifier>"]

Device-specific settings live in tables keyed by stable physical identifiers. The `DeviceConfig` struct in [`crates/openlogi-core/src/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/device.rs) defines the available fields.

### Device Identifiers

Keys follow the pattern `"receiver:<id>:slot:<n>"` for Logitech receivers or `"unit:<uuid>"` for direct USB connections:

```toml
[devices."receiver:aabbccdd:slot:1"]
custom_name = "Office mouse"
dpi = 1600
dpi_presets = [800, 1600, 3200]
thumbwheel_sensitivity = 20
invert_scroll = false
scroll_resolution = "high"

```

### Common Device Fields

The `DeviceConfig` struct supports these primary fields:

- **custom_name** – Human-readable label displayed in the UI.
- **dpi** – Integer sensor DPI value.
- **dpi_presets** – Array of preset DPI integers for cycling.
- **thumbwheel_sensitivity** – Integer override for wheel speed (1-30).
- **invert_scroll** – Boolean to reverse scroll direction.
- **scroll_resolution** – String enum: `"high"` or `"low"`.

### Button Bindings [devices."<id>".bindings]

Button mappings are defined under the `bindings` sub-table. Simple actions use string values, while complex actions use inline tables:

```toml
[devices."receiver:aabbccdd:slot:1".bindings]
Back = "BrowserBack"
Forward = "BrowserForward"
MiddleClick = { HoldShortcut = "Ctrl+Space" }           # Hold while button is pressed

DpiToggle = { short = "ShowDesktop", long = "MissionControl" }  # Short vs long press

Thumbwheel = "AppExpose"

```

### Gesture Button Configuration

Gesture buttons (like the Logitech MX Master gesture puck) require a nested table defining actions per direction:

```toml
[devices."receiver:aabbccdd:slot:1".bindings.GestureButton]
Click = "MissionControl"
Up = "MissionControl"
Down = "AppExpose"
Left = "PreviousDesktop"
Right = "NextDesktop"

```

### Per-Application Overrides

Override bindings when specific applications are foreground using the `per_app_bindings` table. Keys are bundle identifiers (macOS) or `exe:<filename>` (Windows):

```toml
[devices."receiver:aabbccdd:slot:1".per_app_bindings."com.microsoft.VSCode"]
Back = "Undo"

```

### Action Ring [devices."<id>".action_ring]

Configure the circular "Actions Ring" UI overlay:

```toml
[devices."receiver:aabbccdd:slot:1".action_ring]
enabled = true
haptics = true

[devices."receiver:aabbccdd:slot:1".action_ring.default.slots]
Top = { action = "Copy", icon = "Keyboard" }
Right = { action = { OpenApplication = { path = "/Applications/Safari.app", display_name = "Safari" } }, icon = "Applications" }
Bottom = { action = "ShowDesktop", label = "Desktop" }

```

Slots support `action`, `icon`, and optional `label` fields.

### Lighting [devices."<id>".lighting]

Control device RGB LEDs:

```toml
[devices."receiver:aabbccdd:slot:1".lighting]
enabled = true
color = "ff0000"
brightness = 80

```

### SmartShift Wheel [devices."<id>".smartshift]

Configure the SmartShift ratchet wheel behavior:

```toml
[devices."receiver:aabbccdd:slot:1".smartshift]
mode = "ratchet"          # Options: ratchet, freespin, or automatic

auto_disengage = 16       # Threshold for automatic mode

tunable_torque = 50       # Mechanical resistance level

```

## Global Keyboard Bindings [keyboard.bindings]

These shortcuts apply system-wide to all keyboards, independent of mouse devices. The syntax is defined in [`crates/openlogi-core/src/key_trigger.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/key_trigger.rs):

```toml
[keyboard.bindings]
f1 = "MissionControl"
"shift+command+f5" = "ShowDesktop"

```

Keys can be single identifiers or quoted combinations of modifiers (`shift`, `command`, `ctrl`, `option`) plus key names.

## Minimal Working Example

A complete, hand-written configuration derived from [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml):

```toml
schema_version = 7
selected_device = "receiver:aabbccdd:slot:1"

[app_settings]
launch_at_login = true
show_in_menu_bar = true
ui_scale = "normal"
appearance = "system"

[devices."receiver:aabbccdd:slot:1"]
custom_name = "Office mouse"
dpi = 1600
dpi_presets = [800, 1600, 3200]

[devices."receiver:aabbccdd:slot:1".bindings]
Back = "BrowserBack"
Forward = "BrowserForward"
MiddleClick = { HoldShortcut = "Ctrl+Space" }

[devices."receiver:aabbccdd:slot:1".action_ring]
enabled = true
haptics = true

[devices."receiver:aabbccdd:slot:1".action_ring.default.slots]
Top = { action = "Copy", icon = "Keyboard" }

[keyboard.bindings]
f1 = "MissionControl"

```

## Key Source Files

| File | Purpose |
|------|---------|
| [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs) | Defines the `Config`, `AppSettings`, `DeviceConfig`, and related structs that map directly to the TOML schema. |
| [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml) | Full example illustrating every supported section and field. |
| [`crates/openlogi-core/src/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/device.rs) | Contains per-device structures (`DeviceConfig`, `LinkConfig`, `LinkOverrides`) used in the `[devices]` map. |
| [`crates/openlogi-core/src/settings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/settings.rs) | Defines `AppSettings` and related enums (`AppIcon`, `Appearance`, `AssetSourcePreference`, etc.). |
| [`crates/openlogi-core/src/key_trigger.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/key_trigger.rs) | Describes the syntax for global keyboard shortcuts (`KeyTrigger`). |

## Summary

- **Schema version 7** is required in the `schema_version` field; OpenLogi automatically migrates configurations from versions 1-6 on load.
- **Device identifiers** use the format `"receiver:<hex>:slot:<n>"` or `"unit:<uuid>"` as defined in [`crates/openlogi-core/src/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/device.rs).
- **Button bindings** support simple strings for actions, inline tables for hold shortcuts, and sub-tables for gesture directions.
- **Global settings** under `[app_settings]` are optional and default to Rust struct defaults.
- **Per-application overrides** use bundle IDs or `exe:<filename>` keys under `per_app_bindings`.

## Frequently Asked Questions

### What is the current schema version for OpenLogi TOML configs?

The current `schema_version` is **7**, defined as a constant in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs). OpenLogi automatically migrates older configuration files from versions 1 through 6 to version 7 when the application starts, but manually writing configs should target version 7 to avoid migration warnings.

### How do I find the correct device identifier for the [devices] table?

Device identifiers are stable physical addresses generated by OpenLogi. They appear as strings like `"receiver:aabbccdd:slot:1"` for Logitech Unifying Receivers or `"unit:<uuid>"` for Bluetooth or Bolt devices. You can find your device's identifier by checking the UI dropdown or examining the automatically generated config file after initially pairing a device through the GUI.

### Can I configure different button actions for specific applications?

Yes. Use the `[devices."<id>".per_app_bindings."<bundle_id>"]` table where `<bundle_id>` is the macOS application bundle identifier (e.g., `"com.microsoft.VSCode"`) or `exe:<filename>` on Windows. Actions defined here override the default device bindings only when that application is the foreground window.

### Where are the default values defined if I omit fields in app_settings?

Default values are hardcoded in the `AppSettings::default()` implementation located in [`crates/openlogi-core/src/settings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/settings.rs). For example, `language` defaults to `"en"`, `appearance` defaults to `"system"`, and `ui_scale` defaults to `"normal"` when not explicitly specified in the TOML file.