How Modly's Settings Store Persists and Synchronizes Configuration
Modly persists configuration in a JSON file inside Electron's user data directory, using getSettings and setSettings functions in 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, 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 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:
- Builds default locations for models, workspace, workflows, extensions, and dependencies
- Reads
settings.jsonif present, merging its values over the defaults - Migrates legacy keys—for example, converting an old
outputsDirto 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:
// 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:
// 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, the main process exposes two channels:
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:
// 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()
// 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:
- Main process writes to
settings.json - Main process returns new config to the calling renderer
- 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, python-bridge.ts, and 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. This is acceptable for local desktop applications where:
- The
userDatadirectory 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 retrieve this token via getSettings to authenticate downstream API calls, while model-downloader.ts accesses it for gated model downloads.
Summary
- Persistence location: JSON file in Electron's
userDatadirectory, managed byelectron/main/settings-store.ts - Core API:
getSettings()for read,setSettings(patch)for atomic update-and-write - Synchronization: IPC handlers in
electron/main/ipc-handlers.tsroute all access; updates broadcast to all windows - Resilience: Default values applied automatically; legacy key migration built-in
- Consumer modules:
python-setup.ts,python-bridge.ts, andmodel-downloader.tsdepend 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 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. 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.
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 →