How Modly's Settings Store Persists User Preferences and Handles Version Migration
Modly stores user preferences in a JSON file within the Electron userData directory, using atomic read-merge-write operations with automatic key remapping to migrate legacy configurations like outputsDir to workspaceDir.
Modly is an open-source desktop application built on Electron that manages AI models and generation workflows. Its settings persistence layer in electron/main/settings-store.ts demonstrates a lightweight, file-based approach to configuration management with built-in backward compatibility for evolving data schemas.
Where Modly Stores User Preferences
By default, Modly persists configuration to a settings.json file located in the Electron userData directory. This path varies by operating system:
- Windows:
%APPDATA%/Modly/settings.json - macOS:
~/Library/Application Support/Modly/settings.json - Linux:
~/.config/Modly/settings.json
The getSettings(userData) function constructs this path and overlays user-defined values onto a base configuration object containing sensible defaults for directories like modelsDir, extensionsDir, dependenciesDir, and workspaceDir.
How Settings Are Read and Written
Modly uses synchronous filesystem operations for immediate, deterministic configuration access in the main process.
Reading Settings with Defaults
When getSettings(userData) executes, it:
- Builds the default configuration with platform-appropriate paths.
- Attempts to read
settings.jsonviareadFileSync. - Parses the JSON and deep-merges user values over defaults.
- Returns the merged object (or defaults if the file is missing or corrupt).
import { getSettings } from './settings-store';
import { app } from 'electron';
const userData = app.getPath('userData');
const settings = getSettings(userData);
console.log('Models directory:', settings.modelsDir);
console.log('Workspace directory:', settings.workspaceDir);
console.log('HF token present:', !!settings.hfToken);
Updating Settings Atomically
The setSettings(userData, patch) function provides partial update semantics:
import { setSettings } from './settings-store';
import { app } from 'electron';
const userData = app.getPath('userData');
// Merge a single field without touching others
const updated = setSettings(userData, {
modelsDir: '/mnt/external/models'
});
console.log('Updated configuration:', updated);
Under the hood, setSettings:
- Retrieves current settings via
getSettings. - Applies the partial patch using object spreading.
- Serializes to pretty-printed JSON with
JSON.stringify(obj, null, 2). - Writes atomically back to
settings.json.
This ensures no data loss if multiple updates occur in rapid succession.
Version Migration: Handling Legacy Keys
Modly's version migration system transparently upgrades old configuration formats without user intervention. The primary migration path handles the rename from outputsDir (v0.x) to workspaceDir (current).
Migration Logic in Practice
When getSettings detects the legacy outputsDir key, it executes this transformation:
- Copies
outputsDirvalue toworkspaceDir. - Deletes the deprecated
outputsDirkey. - Persists the migrated structure back to disk.
Before migration:
{
"outputsDir": "/home/user/modly-outputs",
"modelsDir": "/home/user/models"
}
After first read:
{
"workspaceDir": "/home/user/modly-outputs",
"modelsDir": "/home/user/models"
}
This one-step migration preserves user data across application updates. The implementation in electron/main/settings-store.ts uses simple key detection rather than version numbering, making migrations stateless and idempotent.
Settings Structure and Consumption
The settings object exposes these primary fields throughout Modly's main process:
| Field | Purpose | Example Consumer |
|---|---|---|
modelsDir |
Local model storage path | model-downloader.ts |
extensionsDir |
Extension/plugin directory | Extension loader |
dependenciesDir |
Python environment path | python-bridge.ts |
workspaceDir |
Output/working directory | Generation pipeline |
hfToken |
Optional Hugging Face API token | Authenticated downloads |
IPC Handler Integration
Main process modules access settings directly, while renderer process communication flows through IPC handlers in electron/main/ipc-handlers.ts:
// Example pattern from ipc-handlers.ts
ipcMain.handle('settings:get', () => {
return getSettings(app.getPath('userData'));
});
ipcMain.handle('settings:set', (_, patch) => {
return setSettings(app.getPath('userData'), patch);
});
Python Bridge Authentication
The python-bridge.ts module retrieves the optional hfToken for authenticated Hugging Face Hub operations:
import { getSettings } from './settings-store';
function spawnPythonProcess() {
const settings = getSettings(app.getPath('userData'));
const env = {
...process.env,
HF_TOKEN: settings.hfToken || '',
MODELS_DIR: settings.modelsDir,
};
// Spawn Python with injected configuration
}
Error Handling and Edge Cases
Modly's settings store implements defensive programming for common failure modes:
- Missing file: Returns complete defaults without error.
- Malformed JSON: Catches parse exceptions, logs warning, returns defaults.
- Partial corruption: Valid fields merge with defaults; only corrupt keys are lost.
- Permission errors: Propagate to caller (typically logged in main process).
This fail-soft behavior ensures the application remains usable even with filesystem or configuration issues.
Summary
- Modly persists settings to
<userData>/settings.jsonusing synchronous JSON operations. getSettings(userData)merges file contents with embedded defaults and executes automatic migration fromoutputsDirtoworkspaceDir.setSettings(userData, patch)provides atomic partial updates with pretty-printed serialization.- The
electron/main/settings-store.tsmodule serves as the single source of truth for configuration across IPC handlers, Python bridge, and model management components. - Version migration is transparent and lossless, requiring no user action during upgrades.
Frequently Asked Questions
What happens if I manually edit settings.json while Modly is running?
Modly reads settings on demand rather than caching, so your changes take effect on the next getSettings call. However, setSettings overwrites the entire file, so concurrent manual edits may be lost. Close the application before hand-editing to avoid conflicts.
Can I relocate the settings.json file to a custom location?
Not through configuration—the userData path is determined by Electron's app.getPath('userData'). To use a custom location, you would need to modify electron/main/settings-store.ts before building from source.
Does Modly support configuration backups or revision history?
No automatic backups are implemented. The settings store writes directly to settings.json without history. For critical deployments, external backup solutions (filesystem snapshots, version control) are recommended.
How does Modly handle migrations for schemas beyond the outputsDir rename?
Currently, only the outputsDir → workspaceDir migration is defined. The architecture supports additional migrations by extending the detection logic in getSettings. Future schema changes would follow the same pattern: detect legacy key, migrate value, remove old key, persist.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →