Security audit workflow - vulnerability scan β verification
npx skills add Tomlord1122/tomtom-skill --skill "electron-architect"
Install specific skill from multi-skill repository
# Description
Electron desktop application architect. Use when designing Electron apps, implementing IPC communication, handling security best practices, or packaging for distribution.
# SKILL.md
name: electron-architect
description: Electron desktop application architect. Use when designing Electron apps, implementing IPC communication, handling security best practices, or packaging for distribution.
Electron Architecture Expert
Expert assistant for Electron desktop application architecture, Main/Renderer process design, IPC communication, security best practices, and application packaging.
Thinking Process
When activated, follow this structured thinking approach to design Electron applications:
Step 1: Application Requirements Analysis
Goal: Understand what the desktop application needs to accomplish.
Key Questions to Ask:
- What is the core functionality? (editor, dashboard, utility, media)
- What system resources are needed? (file system, network, hardware)
- What is the target platform? (macOS, Windows, Linux, or all)
- Are there offline requirements?
- What is the expected data sensitivity? (local files, credentials, user data)
Actions:
1. List all features requiring system access (files, network, native APIs)
2. Identify user interaction patterns (single window, multi-window, tray app)
3. Determine data persistence needs (local storage, SQLite, file system)
4. Map integration points (external APIs, local services, hardware)
Decision Point: You should be able to articulate:
- "This app needs access to [X] system resources"
- "The main user flows are [Y]"
- "Security sensitivity level is [Z]"
Step 2: Architecture Design (Security First)
Goal: Design a secure Main/Renderer architecture.
Thinking Framework - Security Principles:
1. Least Privilege: Renderer should have minimal capabilities
2. Defense in Depth: Multiple layers of protection
3. Explicit Communication: All IPC channels are explicit and validated
Architecture Decision Matrix:
| Capability Needed | Where to Implement | Security Consideration |
|---|---|---|
| UI Rendering | Renderer process | Treat as untrusted (like a browser) |
| File system access | Main process | Expose via validated IPC |
| Network requests | Main process preferred | Avoid renderer CORS issues |
| Native dialogs | Main process | User consent for file access |
| Crypto operations | Main process | Protect keys from renderer |
| Shell commands | Main process only | Never expose to renderer |
Decision Point: For each feature, answer:
- "Does this need Main process access?"
- "What is the minimal IPC surface needed?"
Step 3: IPC Design
Goal: Design safe, type-safe IPC communication.
Thinking Framework:
- "What data flows between Main and Renderer?"
- "Who initiates the communication?"
- "What validation is needed on each end?"
IPC Pattern Selection:
| Communication Need | Pattern | Direction |
|---|---|---|
| Request/response | invoke/handle | Renderer β Main |
| Fire and forget | send | Renderer β Main |
| Push notification | webContents.send | Main β Renderer |
| Two-way stream | MessagePort | Bidirectional |
IPC Security Checklist:
- [ ] All channels have explicit names
- [ ] Input validation on Main process handlers
- [ ] No arbitrary code execution from renderer input
- [ ] Sensitive operations require user confirmation
- [ ] Rate limiting for expensive operations
Type Safety Pattern:
// Define channel types in shared/
interface IpcChannels {
'file:open': { args: void; return: string | null };
'file:save': { args: { path: string; content: string }; return: boolean };
}
Step 4: Preload Script Design
Goal: Create a minimal, secure bridge between worlds.
Thinking Framework:
- "What is the absolute minimum the renderer needs?"
- "Am I exposing more than necessary?"
- "Is each exposed function validated?"
Preload Design Principles:
1. Minimal Surface: Only expose what's absolutely needed
2. No Raw IPC: Wrap ipcRenderer, don't expose directly
3. Type Definitions: Provide TypeScript types for renderer
4. One-Way Binding: Prefer invoke over send/on pairs
Anti-Patterns to Avoid:
// BAD: Exposes raw ipcRenderer
contextBridge.exposeInMainWorld('electron', { ipcRenderer });
// BAD: Arbitrary channel execution
contextBridge.exposeInMainWorld('api', {
send: (channel, data) => ipcRenderer.send(channel, data)
});
// GOOD: Explicit, limited API
contextBridge.exposeInMainWorld('api', {
openFile: () => ipcRenderer.invoke('dialog:openFile'),
saveFile: (content: string) => ipcRenderer.invoke('file:save', content)
});
Step 5: Window Management Strategy
Goal: Design appropriate window management for the application.
Thinking Framework:
- "How many windows does this app need?"
- "How do windows communicate?"
- "What happens when windows are closed?"
Window Patterns:
| App Type | Pattern |
|---|---|
| Single document | One main window |
| Multi-document | Window per document, shared state in Main |
| Dashboard + details | Parent-child windows |
| System utility | Tray app with popup |
Window Configuration Checklist:
- [ ] Appropriate webPreferences for each window type
- [ ] Window state persistence (position, size)
- [ ] Proper close/quit behavior (hide vs destroy)
- [ ] Deep linking / protocol handling
Step 6: Data Persistence Strategy
Goal: Design secure, reliable data storage.
Thinking Framework:
- "What data needs to persist?"
- "How sensitive is this data?"
- "Does data need to sync across devices?"
Storage Options:
| Data Type | Solution | Security |
|---|---|---|
| User preferences | electron-store | Plain or encrypted |
| Structured data | SQLite (better-sqlite3) | File-level encryption |
| Large files | File system | OS-level permissions |
| Credentials | system keychain (keytar) | OS secure storage |
Data Security Checklist:
- [ ] Sensitive data encrypted at rest
- [ ] Credentials in system keychain, not files
- [ ] Backup/export functionality
- [ ] Data migration strategy for updates
Step 7: Packaging and Distribution
Goal: Configure reliable cross-platform distribution.
Thinking Framework:
- "Which platforms are targets?"
- "How will updates be delivered?"
- "What signing/notarization is needed?"
Platform Checklist:
| Platform | Signing | Distribution |
|---|---|---|
| macOS | Developer ID + Notarization | DMG, PKG, or Mac App Store |
| Windows | Code signing certificate | NSIS, MSI, or Microsoft Store |
| Linux | Optional GPG | AppImage, deb, rpm, Snap |
Auto-Update Strategy:
- [ ] electron-updater configuration
- [ ] Update server (GitHub releases, S3, etc.)
- [ ] Staged rollouts for critical updates
- [ ] Rollback capability
Step 8: Testing Strategy
Goal: Ensure the application is reliable across platforms.
Testing Layers:
- Unit Tests: Business logic in Main process
- Integration Tests: IPC communication
- E2E Tests: Spectron/Playwright for UI flows
- Platform Tests: CI matrix for all target platforms
Testing Checklist:
- [ ] Test IPC handlers in isolation
- [ ] Test preload script type contracts
- [ ] E2E tests for critical user flows
- [ ] Platform-specific behavior tests
Usage
Scaffold New Project
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh [project-name] [ui-framework] [package-manager]
Arguments:
- project-name - Name of the project (default: my-electron-app)
- ui-framework - UI framework: vanilla, react, svelte, vue (default: vanilla)
- package-manager - Package manager: pnpm, npm, yarn (default: pnpm)
Examples:
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh my-app
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh my-app react
bash /mnt/skills/user/electron-architect/scripts/scaffold-project.sh my-app svelte pnpm
Security defaults:
- nodeIntegration: false
- contextIsolation: true
- sandbox: true
Documentation Resources
Official Documentation:
- Electron: https://www.electronjs.org/docs/latest/
- Electron Forge: https://www.electronforge.io/
- electron-builder: https://www.electron.build/
Project Structure
src/
βββ main/
β βββ main.ts # Main process entry
β βββ ipc/ # IPC handlers
β β βββ file-handlers.ts
β βββ services/ # Backend services
β β βββ database.ts
β βββ menu.ts # Application menu
βββ preload/
β βββ preload.ts # Context bridge
βββ renderer/ # UI (React/Svelte/Vue)
β βββ App.tsx
β βββ components/
βββ shared/
βββ types.ts # Shared type definitions
Security Configuration
BrowserWindow Settings
// main.ts - Secure configuration
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false, // Disable Node.js
contextIsolation: true, // Enable context isolation
sandbox: true, // Sandbox mode
preload: path.join(__dirname, 'preload.js'),
webSecurity: true, // Enforce same-origin
}
});
Preload Script Pattern
// preload.ts - Safe API exposure
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('electronAPI', {
// One-way: Renderer β Main
saveFile: (content: string) =>
ipcRenderer.invoke('file:save', content),
// One-way: Main β Renderer
onUpdateAvailable: (callback: (version: string) => void) =>
ipcRenderer.on('update-available', (_, version) => callback(version)),
// Request-response pattern
openFile: () => ipcRenderer.invoke('dialog:openFile'),
});
// Type declaration for renderer
declare global {
interface Window {
electronAPI: {
saveFile: (content: string) => Promise<boolean>;
onUpdateAvailable: (callback: (version: string) => void) => void;
openFile: () => Promise<string | null>;
}
}
}
Main Process Handlers
// main/ipc/file-handlers.ts
import { ipcMain, dialog } from 'electron';
import { readFile, writeFile } from 'fs/promises';
export function registerFileHandlers() {
ipcMain.handle('dialog:openFile', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'Text', extensions: ['txt', 'md'] }]
});
if (canceled) return null;
return readFile(filePaths[0], 'utf-8');
});
ipcMain.handle('file:save', async (_, content: string) => {
const { canceled, filePath } = await dialog.showSaveDialog({});
if (canceled || !filePath) return false;
await writeFile(filePath, content);
return true;
});
}
IPC Communication Patterns
Pattern 1: Invoke (Request-Response)
// Renderer
const data = await window.electronAPI.fetchData(id);
// Main
ipcMain.handle('fetch-data', async (event, id) => {
return await database.get(id);
});
Pattern 2: Send/On (Fire-and-Forget)
// Main β Renderer
mainWindow.webContents.send('notification', message);
// Renderer
window.electronAPI.onNotification((msg) => showToast(msg));
Pattern 3: Two-Way Events
// Renderer sends, awaits Main response
const result = await window.electronAPI.processFile(path);
Packaging Configuration
Electron Forge
{
"config": {
"forge": {
"packagerConfig": {
"asar": true,
"icon": "./assets/icon"
},
"makers": [
{ "name": "@electron-forge/maker-squirrel" },
{ "name": "@electron-forge/maker-dmg" },
{ "name": "@electron-forge/maker-deb" }
]
}
}
}
Present Results to User
When providing Electron solutions:
- Always follow security best practices
- Provide complete IPC communication examples
- Consider cross-platform compatibility
- Include TypeScript types for the API
- Note Electron version differences
Troubleshooting
"require is not defined"
- nodeIntegration is correctly disabled
- Use preload script with contextBridge
"Cannot access window.electronAPI"
- Check preload script path is correct
- Verify contextIsolation is true
- Ensure contextBridge.exposeInMainWorld is called
"IPC message not received"
- Verify channel names match exactly
- Check if handler is registered before window loads
- Use invoke for async responses
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.