# How Modly Auto-Updater Manages Updates for Packaged Applications Across Operating Systems

> Learn how Modly's auto-updater handles packaged application updates across Windows, Linux, and macOS. Discover platform-specific update strategies for seamless patching.

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

---

**Modly's auto-updater uses Electron Updater (`electron-updater`) from the main process with platform-specific logic that enables automatic patches on Windows and Linux while disabling updates entirely on macOS due to unsigned builds.**

Modly, an open-source desktop application built with Electron, implements a sophisticated cross-platform update mechanism that balances automation with user control. The system handles **patch updates** silently while requiring manual intervention for **major and minor releases**, with special handling for macOS limitations. All auto-updater logic lives in [`electron/main/updater.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/updater.ts) and integrates with the UI through [`src/shared/stores/appStore.ts`](https://github.com/lightningpixel/modly/blob/main/src/shared/stores/appStore.ts).

## Platform-Specific Update Behavior

Modly's update support varies dramatically by operating system due to code signing requirements.

### Windows and Linux: Full Auto-Updater Support

On Windows and Linux, `electron-updater` operates normally. The updater validates bundle signatures against signed builds, enabling seamless automatic updates. Users receive patch updates without interruption while major and minor releases trigger UI notifications.

### macOS: Updates Explicitly Disabled

macOS builds are **unsigned**—the project lacks an Apple Developer ID. Since `electron-updater` validates code signatures before applying any update, the updater would fail repeatedly on macOS. The code explicitly disables updates through the `updatesSupported` constant:

```typescript
// In electron/main/updater.ts
const updatesSupported = process.platform !== 'darwin';

```

This platform gate ensures macOS users manually download installers from GitHub Releases rather than experiencing broken auto-update attempts.

## Initializing the Auto-Updater

The `initAutoUpdater(getWindow)` function bootstraps the update system from [`electron/main/index.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/index.ts). It receives a callback returning the current `BrowserWindow` to enable renderer communication.

Configuration applied when `updatesSupported` is `true`:

| Setting | Value | Purpose |
|---------|-------|---------|
| `autoDownload` | `false` | Prevents automatic downloads; app controls when to fetch updates |
| `autoInstallOnAppQuit` | `true` | Silent installation when user quits the application |
| `disableWebInstaller` | `true` | Forces updates from GitHub release assets only |

```typescript
// From electron/main/index.ts
import { initAutoUpdater } from './updater';
import { createWindow } from './window';

const mainWindow = createWindow();
initAutoUpdater(() => mainWindow);

```

## Update Detection and Scheduling

Modly checks for updates immediately on application startup, then polls every **2 hours** using `setInterval`. The `autoUpdater.checkForUpdates()` method queries the configured update source (GitHub Releases) for new versions.

## Distinguishing Patch vs. Major/Minor Updates

When `electron-updater` emits the `'update-available'` event, Modly implements version parsing logic to categorize the update type:

**Patch updates** (same major.minor, different patch):
- Automatically downloaded via `autoUpdater.downloadUpdate()`
- Applied silently on next quit

**Major/minor updates** (different major or minor version):
- Renderer notified via `updater:major-minor-available` IPC channel
- UI prompts user to download new installer from GitHub

This separation prevents disruptive automatic updates that might require manual migration or new dependencies.

```typescript
// Simplified version comparison logic from updater.ts
const [currentMajor, currentMinor] = currentVersion.split('.').map(Number);
const [newMajor, newMinor] = newVersion.split('.').map(Number);

if (currentMajor === newMajor && currentMinor === newMinor) {
  // Patch: auto-download
  autoUpdater.downloadUpdate();
} else {
  // Major/minor: notify renderer
  mainWindow.webContents.send('updater:major-minor-available', { version: newVersion });
}

```

## Applying Downloaded Updates

Once a patch finishes downloading, the `'update-downloaded'` event triggers the installation sequence:

1. Main process sends `updater:applying` to renderer
2. Renderer sets `patchUpdateReady = true` in `useAppStore`
3. Short delay allows UI to render "Applying update..." screen
4. `autoUpdater.quitAndInstall(true, true)` executes with:
   - First `true`: quit silently
   - Second `true`: run installer after quit

## State Management and UI Integration

The shared store in [`src/shared/stores/appStore.ts`](https://github.com/lightningpixel/modly/blob/main/src/shared/stores/appStore.ts) exposes update state to the React renderer. Components subscribe to `patchUpdateReady` to display appropriate UI feedback.

### Renderer Event Listener Hook

```typescript
import { useEffect } from 'react';
import { useAppStore } from '../../shared/stores/appStore';

export function useUpdateListener() {
  const setPatchUpdateReady = useAppStore(state => state.setPatchUpdateReady);
  const showToast = useAppStore(state => state.showToast);

  useEffect(() => {
    const onMajorMinor = (_event, { version }) => {
      showToast(`New version ${version} available – download from GitHub`);
    };
    const onApplying = (_event, { version }) => {
      setPatchUpdateReady(true);
      showToast(`Applying patch ${version}…`);
    };

    window.electron.ipcRenderer.on('updater:major-minor-available', onMajorMinor);
    window.electron.ipcRenderer.on('updater:applying', onApplying);

    return () => {
      window.electron.ipcRenderer.removeListener('updater:major-minor-available', onMajorMinor);
      window.electron.ipcRenderer.removeListener('updater:applying', onApplying);
    };
  }, [setPatchUpdateReady, showToast]);
}

```

### React UI Component

```tsx
import { useAppStore } from '../../shared/stores/appStore';

export function PatchUpdateBanner() {
  const patchReady = useAppStore(state => state.patchUpdateReady);
  if (!patchReady) return null;
  return <div className="banner">Applying update… Please wait.</div>;
}

```

## Error Handling and Logging

All updater errors route through Modly's shared logger. The IPC channel in [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts) registers the `autoUpdater` instance for renderer communication, ensuring consistent error propagation across processes.

## Summary

- Modly's auto-updater is **disabled on macOS** (`process.platform !== 'darwin'`) due to unsigned builds that would fail signature validation
- **Windows and Linux** support automatic patch updates with `autoDownload: false` and `autoInstallOnAppQuit: true`
- **Patch updates** download and install silently; **major/minor releases** require manual GitHub download
- Update logic resides in [`electron/main/updater.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/updater.ts) with state shared via [`src/shared/stores/appStore.ts`](https://github.com/lightningpixel/modly/blob/main/src/shared/stores/appStore.ts)
- The renderer receives notifications through IPC channels `updater:major-minor-available` and `updater:applying`

## Frequently Asked Questions

### Why doesn't Modly's auto-updater work on macOS?

The macOS builds are unsigned because the project lacks an Apple Developer ID. Since `electron-updater` validates code signatures before applying updates, it would fail repeatedly on macOS. The updater is explicitly disabled on `darwin` platforms to prevent broken user experiences, forcing manual downloads from GitHub Releases instead.

### How does Modly decide between automatic patches and manual major updates?

The updater parses version strings into major, minor, and patch components when an `'update-available'` event fires. If the major and minor numbers match the current installation, it's classified as a patch and downloads automatically. Different major or minor numbers trigger a renderer notification, prompting the user to download a new installer from GitHub.

### Can I disable automatic updates in Modly?

Automatic updates require `updatesSupported` to be true (non-macOS platforms) and depend on `autoDownload: false` requiring explicit download calls. There is no user-facing toggle to fully disable the update check, though the 2-hour polling interval and manual download gate provide user control over when updates apply.

### Where does Modly download updates from?

The `disableWebInstaller: true` setting forces `electron-updater` to source updates exclusively from GitHub release assets. Modly does not use web installers or third-party update servers—all binaries must be present in the repository's Releases section with proper version tags.