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

OpenLogi uses a single TOML configuration file where the top-level structure mirrors the Config struct in 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 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:

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, 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. All fields are optional; missing values fall back to AppSettings::default().

Common configuration options include:

[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.""]

Device-specific settings live in tables keyed by stable physical identifiers. The DeviceConfig struct in 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:

[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."".bindings]

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

[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:

[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):

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

Action Ring [devices."".action_ring]

Configure the circular "Actions Ring" UI overlay:

[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."".lighting]

Control device RGB LEDs:

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

SmartShift Wheel [devices."".smartshift]

Configure the SmartShift ratchet wheel behavior:

[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:

[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:

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 Defines the Config, AppSettings, DeviceConfig, and related structs that map directly to the TOML schema.
docs/config.example.toml Full example illustrating every supported section and field.
crates/openlogi-core/src/device.rs Contains per-device structures (DeviceConfig, LinkConfig, LinkOverrides) used in the [devices] map.
crates/openlogi-core/src/settings.rs Defines AppSettings and related enums (AppIcon, Appearance, AssetSourcePreference, etc.).
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.
  • 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. 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. For example, language defaults to "en", appearance defaults to "system", and ui_scale defaults to "normal" when not explicitly specified in the TOML file.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →