# How Modly's Settings Store Persists and Synchronizes Configuration

> Learn how Modly's settings store persists configuration using a JSON file and synchronous operations. Understand the getSettings and setSettings functions for reliable data management.

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

---

**Modly persists configuration in a JSON file inside Electron's user data directory, using `getSettings` and `setSettings` functions in [`settings-store.ts`](https://github.com/lightningpixel/modly/blob/main/settings-store.ts) that read, merge, and write the file with synchronous operations, while IPC handlers broadcast updates across all renderer processes.**

Modly is an open-source desktop application that manages AI model workflows through an Electron-based interface. Its configuration system balances simplicity with reliability—no external database required. In [`electron/main/settings-store.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/settings-store.ts), Modly implements a straightforward **settings store** that handles persistence, migration, and real-time synchronization across windows. This article breaks down exactly how the system works, with source-level details from the lightningpixel/modly repository.

## Core Persistence Mechanism: JSON File Storage

Modly stores all user configuration in a single [`settings.json`](https://github.com/lightningpixel/modly/blob/main/settings.json) file located in Electron's `userData` directory. This location—retrieved via `app.getPath('userData')`—is platform-appropriate (e.g., `~/Library/Application Support/Modly` on macOS, `%APPDATA%/Modly` on Windows).

### The Settings Store API

The module exports two primary functions that form the complete read/write interface:

| Function | Purpose |
|----------|---------|
| `getSettings(userData)` | Loads existing config, merges with defaults, migrates legacy keys |
| `setSettings(userData, patch)` | Applies partial updates, writes back to disk, returns fresh config |

### Reading Configuration with `getSettings`

When called, `getSettings` performs three operations:

1. **Builds default locations** for models, workspace, workflows, extensions, and dependencies
2. **Reads [`settings.json`](https://github.com/lightningpixel/modly/blob/main/settings.json)** if present, merging its values over the defaults
3. **Migrates legacy keys**—for example, converting an old `outputsDir` to the current structure

The function always returns a complete `AppSettings` object, ensuring downstream code never receives partial configuration.

Behind the scenes in [`settings-store.ts`](https://github.com/lightningpixel/modly/blob/main/settings-store.ts):

```typescript
// Simplified logic from electron/main/settings-store.ts
export function getSettings(userData: string): AppSettings {
  const defaults = buildDefaultSettings(userData);
  const settingsPath = path.join(userData, 'settings.json');
  
  if (fs.existsSync(settingsPath)) {
    const stored = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
    // Merge stored over defaults, with legacy key migration
    return { ...defaults, ...migrateLegacyKeys(stored) };
  }
  
  return defaults;
}

```

### Writing Configuration with `setSettings`

Updates follow a patch-based approach. The function accepts a `Partial<AppSettings>` object, merges it with current values, and writes the result:

```typescript
// From electron/main/settings-store.ts
export function setSettings(userData: string, patch: Partial<AppSettings>): AppSettings {
  const current = getSettings(userData);
  const updated = { ...current, ...patch };
  
  const settingsPath = path.join(userData, 'settings.json');
  fs.writeFileSync(settingsPath, JSON.stringify(updated, null, 2)); // Pretty-printed
  
  return updated;
}

```

**Critical implementation detail:** `writeFileSync` ensures atomic, blocking writes. Any subsequent `getSettings` call—even from another process immediately after—reads the latest state from disk.

## Cross-Process Synchronization via IPC

Renderer processes never touch the filesystem directly. Instead, Modly uses Electron's **inter-process communication (IPC)** to route all configuration access through the main process.

### IPC Handler Registration

In [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts), the main process exposes two channels:

```typescript
import { ipcMain, app } from 'electron'
import { getSettings, setSettings } from './settings-store'

ipcMain.handle('getSettings', () => {
  const userData = app.getPath('userData')
  return getSettings(userData)
})

ipcMain.handle('setSettings', (_event, patch: Partial<AppSettings>) => {
  const userData = app.getPath('userData')
  return setSettings(userData, patch)  // Persists and returns fresh config
})

```

### Renderer-Side Usage

React components and other renderer code invoke these handlers through `ipcRenderer`:

```typescript
// Reading current configuration
import { ipcRenderer } from 'electron'

async function loadSettings() {
  const settings = await ipcRenderer.invoke('getSettings')
  console.log('Models directory:', settings.modelsDir)
  console.log('HF token present:', !!settings.hfToken)
}

loadSettings()

```

```typescript
// Updating a configuration value
async function updateHuggingFaceToken(newToken: string) {
  const updated = await ipcRenderer.invoke('setSettings', { 
    hfToken: newToken 
  })
  // updated contains the complete, synchronized configuration
  return updated
}

```

### Broadcasting Updates to All Windows

The synchronization design ensures **all open windows receive configuration changes immediately**. When `setSettings` is invoked:

1. Main process writes to [`settings.json`](https://github.com/lightningpixel/modly/blob/main/settings.json)
2. Main process returns new config to the calling renderer
3. Main process **broadcasts** the same config to all other renderer processes

This prevents state drift—if a user updates their Hugging Face token in one window, a settings panel open in another window reflects the change without manual refresh.

Downstream modules like [`python-setup.ts`](https://github.com/lightningpixel/modly/blob/main/python-setup.ts), [`python-bridge.ts`](https://github.com/lightningpixel/modly/blob/main/python-bridge.ts), and [`model-downloader.ts`](https://github.com/lightningpixel/modly/blob/main/model-downloader.ts) consume these settings through the same `getSettings` interface, ensuring consistent paths and credentials across the entire application stack.

## Default Configuration and Migration Strategy

Modly's settings store anticipates fresh installs and evolving schema requirements through built-in defaults and migration.

### Default Paths Structure

`buildDefaultSettings(userData)` generates platform-appropriate locations:

| Setting | Default Location |
|---------|----------------|
| `modelsDir` | `<userData>/models` |
| `workflowsDir` | `<userData>/workflows` |
| `extensionsDir` | `<userData>/extensions` |
| `dependenciesDir` | `<userData>/dependencies` |
| `workspaceDir` | `<userData>/workspace` |

### Legacy Key Migration

The migration layer in `getSettings` handles schema evolution. For example, if a user upgrades from a version that used `outputsDir`, the store transparently maps this to the current structure without data loss or manual intervention.

## Security and Token Handling

Sensitive values like the **Hugging Face token** (`hfToken`) are stored as plain strings in [`settings.json`](https://github.com/lightningpixel/modly/blob/main/settings.json). This is acceptable for local desktop applications where:

- The `userData` directory has appropriate filesystem permissions
- No network service or external process is granted read access
- The user controls their own machine

Modules such as [`python-bridge.ts`](https://github.com/lightningpixel/modly/blob/main/python-bridge.ts) retrieve this token via `getSettings` to authenticate downstream API calls, while [`model-downloader.ts`](https://github.com/lightningpixel/modly/blob/main/model-downloader.ts) accesses it for gated model downloads.

## Summary

- **Persistence location:** JSON file in Electron's `userData` directory, managed by [`electron/main/settings-store.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/settings-store.ts)
- **Core API:** `getSettings()` for read, `setSettings(patch)` for atomic update-and-write
- **Synchronization:** IPC handlers in [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts) route all access; updates broadcast to all windows
- **Resilience:** Default values applied automatically; legacy key migration built-in
- **Consumer modules:** [`python-setup.ts`](https://github.com/lightningpixel/modly/blob/main/python-setup.ts), [`python-bridge.ts`](https://github.com/lightningpixel/modly/blob/main/python-bridge.ts), and [`model-downloader.ts`](https://github.com/lightningpixel/modly/blob/main/model-downloader.ts) depend on the same store

Modly's settings store demonstrates that sophisticated configuration management does not require external dependencies—the combination of synchronous JSON I/O and Electron's IPC provides durability, consistency, and real-time synchronization.

## Frequently Asked Questions

### Where does Modly store its configuration file?

Modly writes [`settings.json`](https://github.com/lightningpixel/modly/blob/main/settings.json) to Electron's `userData` directory, obtained via `app.getPath('userData')`. On macOS this is typically `~/Library/Application Support/Modly`; on Windows, `%APPDATA%/Modly`; and on Linux, `~/.config/Modly`. The exact path varies by platform and installation method.

### How do I programmatically update Modly settings from a plugin or extension?

Extensions should use the IPC interface rather than direct filesystem access. Invoke `ipcRenderer.invoke('setSettings', { key: value })` with a partial settings object; the main process handles persistence and broadcasts the change. Direct file manipulation risks corrupting the JSON or causing synchronization issues with open windows.

### What happens if the settings.json file is deleted or corrupted?

On the next `getSettings` call, Modly regenerates the file using `buildDefaultSettings`, which creates fresh default paths for models, workflows, extensions, dependencies, and workspace. The application continues normally, though user-specific values like API tokens must be re-entered. No external recovery process is required.

### Does Modly encrypt sensitive settings like API tokens?

No—tokens such as `hfToken` are stored as plaintext strings in [`settings.json`](https://github.com/lightningpixel/modly/blob/main/settings.json). Modly relies on operating system filesystem permissions to protect the `userData` directory. Users requiring additional security should implement full-disk encryption or store tokens in their system's keychain separately from Modly's configuration.