# How OpenLogi Handles Configuration Schema Versioning: TOML Migration Strategy Explained

> OpenLogi manages configuration schema versioning with TOML files. Discover its automatic upgrade strategy using sequential migration pipelines for seamless legacy config handling.

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

---

**OpenLogi stores a `schema_version` integer in every [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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:

```rust
// 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`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) file declares its version at the top level before device-specific settings:

```toml

# 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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs). Then, add a new conditional block in [`crates/openlogi-core/src/config/file.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/file.rs) and the migration test suite, maintaining a single source of truth for the format revision number.