# How Modly's Settings Store Persists User Preferences and Handles Version Migration

> Discover how Modly's settings store persists user preferences via JSON files and handles version migration with automatic key remapping. Learn about atomic read-merge-write operations for seamless updates.

- Repository: [lightningpixel/modly](https://github.com/lightningpixel/modly)
- Tags: internals
- Published: 2026-08-20

---

**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`](https://github.com/lightningpixel/modly/blob/main/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`](https://github.com/lightningpixel/modly/blob/main/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`](https://github.com/lightningpixel/modly/blob/main/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).

```typescript
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**:

```typescript
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`](https://github.com/lightningpixel/modly/blob/main/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:**

```json
{
  "outputsDir": "/home/user/modly-outputs",
  "modelsDir": "/home/user/models"
}

```

**After first read:**

```json
{
  "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`](https://github.com/lightningpixel/modly/blob/main/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`](https://github.com/lightningpixel/modly/blob/main/model-downloader.ts) |
| `extensionsDir` | Extension/plugin directory | Extension loader |
| `dependenciesDir` | Python environment path | [`python-bridge.ts`](https://github.com/lightningpixel/modly/blob/main/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`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts):

```typescript
// 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`](https://github.com/lightningpixel/modly/blob/main/python-bridge.ts) module retrieves the optional `hfToken` for authenticated Hugging Face Hub operations:

```typescript
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`](https://github.com/lightningpixel/modly/blob/main/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`](https://github.com/lightningpixel/modly/blob/main/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`](https://github.com/lightningpixel/modly/blob/main/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.