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:
- Hostname verification — the URL must resolve to
github.com(line 35) - Path structure verification — the URL must contain at least
owner/repoformat (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
nodesarray: Ensures the extension declares at least one processing node - Entry file existence: Verifies
processor.js(default) orgenerator.pyis 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.tsas a 12-step IPC workflow triggered byextensions: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
.tsentry points usingesbuildbefore final deployment - Language-specific setup runs
pip installfor Python extensions andnpm installfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →