# How Modly Syncs Built‑in Extensions to the User Data Directory

> Learn how Modly syncs built-in extensions to the user data directory by comparing version markers and atomically copying files. Discover the syncBuiltinExtensions() function.

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

---

**Modly synchronizes built‑in extensions by comparing version markers between the application bundle and the user data directory, then atomically copying files from `resources/extensions` to the user data path using the `syncBuiltinExtensions()` function defined in [`electron/main/builtin-sync.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/builtin-sync.ts).**

The `lightningpixel/modly` repository implements a robust extension management system that ensures users always receive the latest bundled functionality without manual intervention. Understanding how Modly syncs built‑in extensions to the user data directory reveals a careful balance between seamless updates, data integrity, and error resilience in Electron applications.

## The Synchronization Architecture

The sync process operates between two distinct locations: the **application bundle** (read‑only source) and the **user data directory** (writable target). When the Electron main process initializes, it resolves these paths using native Electron APIs and Node.js utilities to establish the source and destination for the copy operation.

- **Source Directory**: Located at `process.resourcesPath/extensions` inside the packaged application, this folder contains the canonical versions of all built‑in extensions shipped with the app.
- **Target Directory**: Resolved via `app.getPath('userData')`, typically resulting in a path like `~/Modly/extensions` (platform‑dependent), where the synchronized copies reside and where the extension loader expects to find available extensions.

## Step‑by‑Step Implementation

The core logic resides in [`electron/main/builtin-sync.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/builtin-sync.ts), which exports an async function that orchestrates the entire process.

### Locating Source and Target Directories

The function first resolves absolute paths for both the source and target directories. It uses Electron’s `app.getPath('userData')` to determine the user‑specific data location and constructs the extensions subdirectory. The source path points to the `resources/extensions` folder embedded within the application bundle.

```typescript
// electron/main/builtin-sync.ts
import { app } from 'electron';
import { promises as fs } from 'fs';
import path from 'path';
import { logger } from './logger';

export async function syncBuiltinExtensions() {
  const userData = app.getPath('userData');
  const targetDir = path.join(userData, 'extensions');
  const sourceDir = path.join(process.resourcesPath, 'extensions');

  await fs.mkdir(targetDir, { recursive: true });
  // ... version check and copy logic
}

```

### Version Validation Strategy

To avoid unnecessary writes and detect updates, the routine performs a lightweight version check using a [`builtin-version.txt`](https://github.com/lightningpixel/modly/blob/main/builtin-version.txt) file present in both the source and target directories. The function reads both files and compares their contents.

If the versions match, the process exits early, preserving existing files. If they differ—indicating an application update or first‑time launch—the target directory is cleared and rebuilt from the source to ensure consistency.

```typescript
  const versionFile = 'builtin-version.txt';
  const [srcVersion, tgtVersion] = await Promise.all([
    fs.readFile(path.join(sourceDir, versionFile), 'utf8').catch(() => ''),
    fs.readFile(path.join(targetDir, versionFile), 'utf8').catch(() => ''),
  ]);

  if (srcVersion !== tgtVersion) {
    logger.info('Updating built‑in extensions...');
    await fs.rm(targetDir, { recursive: true, force: true });
    await copyDirectory(sourceDir, targetDir);
  }

```

### Recursive File Copy Operations

When an update is required, the system performs a recursive copy using Node.js `fs.promises` APIs. A helper function iterates over directory entries, preserving the folder structure and file permissions while copying each item from the source to the target.

```typescript
async function copyDirectory(src: string, dst: string) {
  const entries = await fs.readdir(src, { withFileTypes: true });
  await fs.mkdir(dst, { recursive: true });
  
  for (const entry of entries) {
    const srcPath = path.join(src, entry.name);
    const dstPath = path.join(dst, entry.name);
    if (entry.isDirectory()) {
      await copyDirectory(srcPath, dstPath);
    } else {
      await fs.copyFile(srcPath, dstPath);
    }
  }
}

```

### Error Handling and Logging

All operations are wrapped in try‑catch blocks (implied by the error handling description) that pipe failures to the centralized logger at [`electron/main/logger.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/logger.ts). If any step fails—such as permission errors or missing source files—the sync aborts cleanly without modifying the existing user data, preventing corruption of the user’s extension environment.

## Integration with the Main Process

The synchronization triggers early in the application lifecycle. In [`electron/main/index.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/index.ts), the `syncBuiltinExtensions()` function is awaited during the `app.whenReady()` event, ensuring extensions are fully deployed before the UI initializes.

```typescript
// electron/main/index.ts
import { app } from 'electron';
import { syncBuiltinExtensions } from './builtin-sync';

app.whenReady().then(async () => {
  await syncBuiltinExtensions(); // Ensures extensions are up‑to‑date
  createMainWindow();
});

```

This guarantees that the extension system is ready before any renderer process or user interaction occurs.

## Safety and Path Validation

While [`builtin-sync.ts`](https://github.com/lightningpixel/modly/blob/main/builtin-sync.ts) handles the copying logic, the repository includes [`electron/main/extension-path-guard.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/extension-path-guard.ts) to validate all extension paths and prevent directory traversal attacks. This module works in tandem with the sync process to ensure that no operation escapes the designated user data directory, adding a security layer to file system operations.

## Summary

- **Modly** copies built‑in extensions from `process.resourcesPath/extensions` to `app.getPath('userData')/extensions` during startup.
- The `syncBuiltinExtensions()` function in [`electron/main/builtin-sync.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/builtin-sync.ts) compares [`builtin-version.txt`](https://github.com/lightningpixel/modly/blob/main/builtin-version.txt) files to determine if an update is necessary.
- Version mismatches trigger a complete replacement of the target directory to ensure consistency with the bundled application version.
- Failures are logged via [`electron/main/logger.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/logger.ts) and abort cleanly without corrupting existing user data.
- The process is invoked in [`electron/main/index.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/index.ts) before the main window creation to ensure extensions are available immediately.

## Frequently Asked Questions

### What is the source directory for built‑in Modly extensions?

Built‑in extensions are stored in the `resources/extensions` folder inside the Electron application bundle, accessed at runtime via `process.resourcesPath`. This read‑only location serves as the authoritative source for all synchronization operations.

### How does Modly determine when to update synced extensions?

The system compares a [`builtin-version.txt`](https://github.com/lightningpixel/modly/blob/main/builtin-version.txt) file located in both the source and target directories. If the version strings differ, Modly assumes the bundled extensions are newer and performs a full re‑sync to update the user data directory.

### What happens if the extension sync fails during startup?

The error handling in [`electron/main/builtin-sync.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/builtin-sync.ts) catches file system errors and logs them via the internal logger. The process aborts without modifying the existing extensions folder, ensuring that a failed update does not corrupt the user’s environment or remove working extensions.

### Where is the extension synchronization logic located?

All synchronization logic is encapsulated in [`electron/main/builtin-sync.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/builtin-sync.ts). The entry point [`electron/main/index.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/index.ts) imports and executes this module during the Electron `app.whenReady()` lifecycle event, while [`electron/main/logger.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/logger.ts) provides the logging infrastructure used during the sync process.