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:

  1. Builds the default configuration with platform-appropriate paths.
  2. Attempts to read settings.json via readFileSync.
  3. Parses the JSON and deep-merges user values over defaults.
  4. 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:

  1. Copies outputsDir value to workspaceDir.
  2. Deletes the deprecated outputsDir key.
  3. 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.json using synchronous JSON operations.
  • getSettings(userData) merges file contents with embedded defaults and executes automatic migration from outputsDir to workspaceDir.
  • setSettings(userData, patch) provides atomic partial updates with pretty-printed serialization.
  • The electron/main/settings-store.ts module 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:

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 →