How OpenLogi Handles Configuration Schema Versioning: TOML Migration Strategy Explained

OpenLogi stores a schema_version integer in every config.toml file and automatically upgrades legacy configurations through a sequential migration pipeline when loading.

OpenLogi persists user preferences in structured TOML configuration files that must evolve alongside the application. The project implements a robust OpenLogi configuration schema versioning system that detects outdated formats and applies one-time migrations to preserve user settings across updates without manual intervention.

The Schema Version Field and Data Structure

At the core of the versioning system lies the Config struct declared in crates/openlogi-core/src/config.rs at line 103. This structure contains a mandatory schema_version field that defines which revision of the configuration format the file conforms to. Alongside the struct, the codebase defines a compile-time constant SCHEMA_VERSION representing the current format revision. Developers bump this constant whenever introducing breaking changes to the configuration structure.

When the application initializes, it invokes ConfigFile::load from crates/openlogi-core/src/config/file.rs to parse the TOML header. The loader first extracts the declared version to determine which migration path—if any—the configuration requires.

Loading and Validation Logic

The validation sequence in crates/openlogi-core/src/config/file.rs (line 255) enforces strict boundaries on acceptable versions. The loader rejects files declaring a schema_version of zero or any version number greater than the current SCHEMA_VERSION constant. If validation fails, the function returns a descriptive error message generated at line 85, preventing the application from loading potentially corrupted or incompatible configurations.

This safety check ensures that:

  • Legacy files trigger automatic migrations
  • Future files (from newer app versions) are rejected to prevent data corruption
  • Corrupted files with version zero fail fast with clear diagnostics

Sequential Migration Pipeline

Once validation passes, the migration engine begins transforming the configuration. The process follows three distinct phases:

  1. Obsolete Field Removal: At line 261 in crates/openlogi-core/src/config/file.rs, the loader strips deprecated fields that are no longer supported for the detected schema version. This prevents deserialization errors from unknown keys.

  2. Versioned Transformations: The engine applies migrations in sequential order using conditional checks against the loaded version:

    • Schema ≤ 3: Legacy transformations for original format versions (line 266)
    • Schema ≤ 4: Updates specific to revision 4 (line 273)
    • Schema ≤ 6: Accumulated changes up to version 6 (line 278)
  3. Version Bump: After applying all necessary transformations, the loader overwrites the schema_version field with the current SCHEMA_VERSION constant at line 282. This guarantees that any freshly loaded configuration reports the latest schema and avoids redundant migrations on subsequent launches.

Edge Case Handling and Test Coverage

The test suite in crates/openlogi-core/src/config/tests.rs rigorously validates the versioning logic across three critical scenarios:

  • Zero Version Rejection: Line 1288 contains assertions verifying that files with schema_version = 0 trigger immediate errors, protecting against malformed or uninitialized configurations.
  • Future Version Blocking: At line 1256, tests confirm that configurations declaring versions higher than SCHEMA_VERSION are rejected with appropriate error messages.
  • Legacy Migration Verification: Line 1194 demonstrates that version 1 files are correctly accepted, migrated through the pipeline, and upgraded to the current schema format.

These tests ensure the system gracefully handles configuration drift while maintaining backward compatibility.

Practical Implementation Example

Developers interacting with the configuration system can leverage the automatic migration capabilities through the public API:

// Load a configuration file, automatically handling schema upgrades.
use openlogi_core::config::ConfigFile;

let (cfg, original_version) = ConfigFile::load(path_to_config)?;

// `cfg.schema_version` is now equal to the current `SCHEMA_VERSION`.
println!("Config upgraded from v{original_version} to v{}", cfg.schema_version);

A typical config.toml file declares its version at the top level before device-specific settings:


# Example of a current config.toml (schema_version = 7)

schema_version = 7
selected_device = "my_mouse"

[devices."direct:046d:c08d:unit:6be9d300"]
dpi = 1600
invert_scroll = false

Summary

  • OpenLogi configuration schema versioning relies on an integer schema_version field in TOML files paired with a compile-time SCHEMA_VERSION constant.
  • The ConfigFile::load function in crates/openlogi-core/src/config/file.rs validates versions against current and historical bounds before processing.
  • Sequential migrations in the loading pipeline upgrade legacy schemas (≤3, ≤4, ≤6) while removing obsolete fields.
  • After migration, the system persists the configuration with the updated version number, ensuring idempotent loading on subsequent starts.
  • Comprehensive test coverage in crates/openlogi-core/src/config/tests.rs validates zero-version rejection, future-version blocking, and successful legacy upgrades.

Frequently Asked Questions

What happens if the schema_version field is missing from the TOML file?

The ConfigFile::load parser treats missing version fields as an error condition. According to the validation logic in crates/openlogi-core/src/config/file.rs, the loader requires an explicit version declaration to determine the appropriate migration path. Files without this field fail to load with a deserialization error pointing to the missing required field.

How do I add a new migration for schema version 8?

First, increment the SCHEMA_VERSION constant in crates/openlogi-core/src/config.rs. Then, add a new conditional block in crates/openlogi-core/src/config/file.rs following the existing pattern (e.g., if loaded_version <= 7 { /* transformation logic */ }). Finally, add unit tests in crates/openlogi-core/src/config/tests.rs to verify that version 7 files upgrade correctly to version 8.

Can OpenLogi downgrade configurations to older schema versions?

No, the migration system is strictly forward-only. As implemented in crates/openlogi-core/src/config/file.rs at line 282, the loader always writes the current SCHEMA_VERSION to the configuration after loading. There is no mechanism to export or convert configurations to previous format revisions, ensuring that the on-disk representation always matches the application's expectations.

Where does OpenLogi define the current schema version constant?

The SCHEMA_VERSION constant is defined at the top of crates/openlogi-core/src/config.rs, adjacent to the Config struct declaration at line 103. This placement ensures the constant is visible to both the configuration loading logic in file.rs and the migration test suite, maintaining a single source of truth for the format revision number.

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 →