How Modly Installs Extensions from GitHub URLs: Cloning, Validation, and Atomic Deployment Workflow

Modly's extension installation from GitHub URLs uses a 12-step Electron IPC workflow that downloads tarballs, validates manifests, stages extensions atomically, and runs language-specific setup routines.

The lightningpixel/modly repository implements a complete extension installation pipeline that allows users to install models, Python processes, and Node.js processes directly from public GitHub repositories. This workflow is orchestrated by the Electron main process through the extensions:installFromGitHub IPC handler defined in electron/main/ipc-handlers.ts.

URL Parsing and Initial Validation

The installation workflow begins with strict URL validation to prevent malicious source domains. The handler receives the raw GitHub URL and performs two critical checks:

  1. Hostname verification — the URL must resolve to github.com (line 35)
  2. Path structure verification — the URL must contain at least owner/repo format (line 37)
// From electron/main/ipc-handlers.ts (lines 35-37)
const parsed = new URL(rawUrl);
if (parsed.hostname !== 'github.com') throw new Error('Invalid hostname');
const parts = parsed.pathname.split('/').filter(Boolean);
if (parts.length < 2) throw new Error('Invalid repository path');

Any URL failing these checks is immediately rejected before network requests are made.

Downloading and Extracting the Repository Tarball

Once validated, Modly fetches the repository's HEAD tarball using the GitHub REST API. This approach avoids git dependencies and enables streaming download progress.

Tarball Download with Progress Streaming

// From electron/main/ipc-handlers.ts (lines 43-56)
const tarballUrl = `https://api.github.com/repos/${owner}/${repo}/tarball/HEAD`;
const response = await axios.get(tarballUrl, {
  responseType: 'stream',
  onDownloadProgress: (progressEvent) => {
    const percent = Math.round((progressEvent.loaded / progressEvent.total) * 80);
    emitProgress({ step: 'download', percent });
  }
});

The download streams to a temporary file while emitting progress events from 0% → 80%. The 80% cap reserves headroom for extraction and setup phases.

Tarball Extraction with Path Stripping

GitHub tarballs wrap contents in a top-level folder named {owner}-{repo}-{sha}. Modly strips this automatically:

// From electron/main/ipc-handlers.ts (line 64)
await tar.x({
  file: tarPath,
  cwd: extractDir,
  strip: 1  // Removes the top-level GitHub wrapper folder
});

Manifest.json Validation and Security Hardening

Every extension must declare a manifest.json. Modly validates this file through multiple security layers before accepting the installation.

Required Manifest Validation

The validateInstallManifest function in electron/main/extension-install-utils.ts enforces:

  • Required fields: id, type (process|model), valid entry files
  • Non-empty nodes array: Ensures the extension declares at least one processing node
  • Entry file existence: Verifies processor.js (default) or generator.py is present
// From electron/main/ipc-handlers.ts (lines 70-81)
if (!fs.existsSync(path.join(extractDir, 'manifest.json'))) {
  throw new Error('manifest.json not found');
}
const manifestRaw = await fs.readJson(manifestPath);
const manifest = validateInstallManifest(manifestRaw, extractDir);

Safe Extension ID Sanitization

The raw id from the manifest is passed through assertSafeExtensionId to prevent directory traversal attacks:

// From electron/main/ipc-handlers.ts (lines 84-86)
const safeId = assertSafeExtensionId(manifest.id);
manifest.id = safeId;
activeExtensionInstalls.add(safeId);

Source Trust Override

The source field is overwritten with the canonical GitHub URL, ensuring trust derives from the repository origin rather than any arbitrary upstream value:

// From electron/main/ipc-handlers.ts (lines 89-91)
manifest.source = `https://github.com/${owner}/${repo}`;

Atomic Staging and Deployment

Modly uses a staging → atomic swap pattern to prevent partial installations and enable crash recovery.

Staging with Incomplete-Install Markers

// From electron/main/ipc-handlers.ts (lines 106-108)
await fs.ensureDir(stagingDir);
await fs.copy(extractDir, stagingDir);
await fs.writeFile(path.join(stagingDir, '.modly-incomplete'), '');

The incomplete-install marker protects against crashes during setup. Modly's startup reconciler detects these markers and triggers cleanup or recovery.

Compile TypeScript Entries (Process Extensions)

For .ts entry points, Modly compiles to JavaScript using esbuild:

// From electron/main/ipc-handlers.ts (lines 110-122)
if (manifest.entry.endsWith('.ts')) {
  const compiledPath = path.join(stagingDir, 'main.js');
  esbuild.buildSync({
    entryPoints: [path.join(stagingDir, manifest.entry)],
    outfile: compiledPath,
    bundle: true,
    platform: 'node',
    format: 'cjs'
  });
  manifest.entry = 'main.js';
}

Atomic Swap with Backup

The final deployment uses atomic rename operations. Existing extensions are backed up before replacement:

// From electron/main/ipc-handlers.ts (lines 128-144)
const backupDir = path.join(extensionsDir, `.modly-backup-${safeId}-${Date.now()}`);
if (await fs.pathExists(targetDir)) {
  await fs.move(targetDir, backupDir);
}
await fs.move(stagingDir, targetDir);  // Atomic operation

If rename fails (e.g., file lock), the staging folder is removed and the install aborts.

Language-Specific Setup Phase

After atomic deployment, Modly runs type-specific setup routines:

Python Process Setup

// From electron/main/ipc-handlers.ts (lines 148-158)
const venvPath = path.join(extensionDir, '.venv');
await runCommand('python', ['-m', 'venv', venvPath], { cwd: extensionDir });
await runCommand(path.join(venvPath, 'bin/pip'), ['install', '.'], {
  cwd: extensionDir,
  onLine: (line) => emitProgress({ step: 'setup', message: line })
});

Node.js Process Setup

// From electron/main/ipc-handlers.ts (lines 160-183)
if (await fs.pathExists(path.join(extensionDir, 'package.json'))) {
  await runCommand('npm', ['install'], {
    cwd: extensionDir,
    onLine: (line) => emitProgress({ step: 'setup', message: line })
  });
}

Each line of setup output is streamed as a progress event to the renderer.

Failure Recovery and Cleanup

If any setup phase fails, Modly performs automatic rollback:

// From electron/main/ipc-handlers.ts (lines 194-200)
if (backupDir && await fs.pathExists(backupDir)) {
  await fs.remove(targetDir);
  await fs.move(backupDir, targetDir);
} else {
  await fs.remove(targetDir);
}
// Incomplete marker preserved for startup reconciler

The incomplete-install marker remains, allowing crash-consistent recovery on next startup.

Frontend Integration: Triggering and Monitoring Installs

Trigger Installation from Renderer

// From src/shared/stores/extensionsStore.ts
import { useExtensionsStore } from '@/stores/extensionsStore'

function installFromGitHub(url: string) {
  extensionsStore.installFromGitHub(url).then(result => {
    if (result.success) {
      console.log('Extension installed:', result.extensionId);
    } else {
      console.error('Install failed:', result.error);
    }
  });
}

Listen for Progress Events

// Using the IPC channel defined in electron/preload/electron-api.ts (lines 244-251)
useEffect(() => {
  const handler = (data: InstallProgress) => {
    // { step: 'download'|'extract'|'validate'|'setup'|'done', percent?: number, message?: string }
    console.log('Progress:', data.percent, '% -', data.step);
  };
  window.electron.extensions.onInstallProgress(handler);
  return () => window.electron.extensions.offInstallProgress(handler);
}, []);

Example Valid manifest.json

{
  "id": "my-awesome-extension",
  "type": "process",
  "entry": "main.ts",
  "nodes": [{ "id": "node1", "name": "Primary Node" }]
}

Security Architecture Summary

Layer Implementation File Reference
URL validation Hostname and path parsing ipc-handlers.ts:35-37
Manifest schema validateInstallManifest function extension-install-utils.ts
Path traversal prevention assertSafeExtensionId sanitization ipc-handlers.ts:84-86
Source trust Canonical URL overwrite ipc-handlers.ts:89-91
Atomic deployment Staging + rename with backup ipc-handlers.ts:106-144
Crash recovery Incomplete-install markers extension-path-guard.ts

Summary

  • Modly's GitHub URL installation is implemented in electron/main/ipc-handlers.ts as a 12-step IPC workflow triggered by extensions:installFromGitHub
  • Security layers include URL hostname validation, manifest schema enforcement, extension ID sanitization, and source trust override
  • Atomic deployment uses staging directories with incomplete-install markers, enabling crash-consistent recovery
  • TypeScript compilation happens automatically for .ts entry points using esbuild before final deployment
  • Language-specific setup runs pip install for Python extensions and npm install for Node.js extensions with streamed progress
  • Automatic rollback restores previous versions or removes partial installs when setup fails

Frequently Asked Questions

What happens if the GitHub repository is private or doesn't exist?

The workflow fails during the tarball download phase. The GitHub API returns 404 or 403, which axios propagates as an error. The IPC handler catches this, emits a failure progress event, and cleans up temporary files without modifying the extension directory.

Can extensions specify their own source URL in manifest.json?

No. Modly explicitly overwrites the source field with the canonical https://github.com/<owner>/<repo> URL. This prevents extensions from claiming trust relationships with arbitrary upstream sources they don't control.

How does Modly handle installation interruptions like app crashes?

The incomplete-install marker file (.modly-incomplete) remains in the staging or target directory. On next startup, Modly's reconciler scans for these markers and either completes interrupted atomic swaps or removes corrupted installations, restoring from .modly-backup-* folders if available.

Why does Modly use tarballs instead of git clone?

Tarball downloads eliminate git binary dependencies, reduce download size (no .git history), enable precise HEAD snapshots, and support streaming progress reporting through axios. The GitHub tarball API also handles authentication and rate limiting consistently.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →