Kopia Desk follows Electron’s recommended three-layer process model. Each layer has a strict, well-defined responsibility: the main process owns all OS access, the preload script acts as a typed bridge between OS and UI, and the renderer runs in a fully isolated context with zero Node.js access. This separation is not cosmetic — it is what allowsDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Pachanga12/Kopia_Desk_Beta_1/llms.txt
Use this file to discover all available pages before exploring further.
lib/core.js to be tested with a plain node --test invocation and what prevents the renderer from ever touching the file system directly.
Three-Layer Architecture
main.js
Main process — full Node.js environment. Owns disk I/O, IPC handlers, dialog windows, and process spawning. Imports all logic from
lib/core.js.preload.js
Preload script — runs with limited Electron access. Exposes exactly the functions the renderer needs as
window.kopiaAPI via contextBridge.renderer/app.js
Renderer — pure browser environment. No Node.js, no
require. All OS operations are calls to window.kopiaAPI.*.Why lib/core.js Is Separate
All scanning, hashing, exclusion filtering, path safety checks, drive detection, and journal logic live in lib/core.js — a standalone CommonJS module with no Electron import. main.js imports it and wires each exported function to an IPC channel, but adds no duplicate logic.
Because lib/core.js carries no Electron dependency, the full test suite runs without launching a browser window:
Security Settings in BrowserWindow
TheBrowserWindow is created with two security-critical flags:
contextIsolation: true means the renderer’s JavaScript context is entirely separate from the preload context — even if an attacker injects script into the renderer, it cannot reach Node APIs. nodeIntegration: false removes the require function from the renderer entirely.
IPC Channel Naming Convention
All channels follow theresource:action pattern, making intent obvious at a glance:
| Channel | Direction | What it does |
|---|---|---|
drives:list | invoke | Lists connected drives via PowerShell Get-Volume |
dialog:select-folder | invoke | Opens a native directory picker for the source folder |
dialog:select-restore-target | invoke | Opens a native directory picker for the restore destination |
folders:quick-list | invoke | Returns user shell folders (Pictures, Documents, etc.) that exist on the machine |
config:default-excludes | invoke | Returns the default exclusion pattern list |
fs:scan-directory | invoke | Recursively scans a folder, applying exclusions |
fs:hash-file | invoke | Full SHA-256 stream hash of a file |
fs:quick-hash | invoke | Head + tail 64 KB hash for change detection |
backup:copy-files | invoke | Copies a batch of files with progress events |
backup:copy-versions | invoke | gzip-compresses old versions before overwriting |
backup:plan-concurrency | invoke | Detects drive type and returns adaptive concurrency |
manifest:load | invoke | Reads the saved manifest JSON for a source folder |
manifest:save | invoke | Writes manifest, creating a .prev.json backup first |
sources:remember | invoke | Persists the local path of a source folder for the Compare tab |
sources:known-paths | invoke | Retrieves all previously remembered source paths for a destination |
journal:peek | invoke | Reports interrupted backup without modifying anything |
journal:check | invoke | Cleans partial files after user confirmation |
log:save | invoke | Saves a JSON operation report to .kopia-data/logs/ |
restore:scan | invoke | Diffs manifest vs local folder for the Compare tab |
restore:full-list | invoke | Lists all backup contents for a source without comparing (Restore tab) |
restore:copy-files | invoke | Restores files from backup to a chosen directory |
restore:list-sources | invoke | Lists all source folders available in the backup |
settings:load / settings:save | invoke | Persists user preferences to userData |
progress push event, which backup:copy-files and restore:scan emit for each file processed, carrying { phase, current, total, file, percent }.
Data Flow for a Backup Operation
Renderer calls window.kopiaAPI
app.js calls window.kopiaAPI.scanDirectory(dirPath, excludePatterns).Preload forwards over IPC
preload.js calls ipcRenderer.invoke('fs:scan-directory', dirPath, excludePatterns) and returns the resulting Promise.Main process handles the channel
ipcMain.handle('fs:scan-directory', ...) in main.js validates that the path exists, then calls scanDirectoryRecursive imported from lib/core.js.core.js does the async I/O
scanDirectoryRecursive uses fs.promises.readdir and processes every directory level with Promise.all, then returns a { [relativePath]: fileInfo } map.File Structure
lib/core.js exports every function with module.exports. main.js destructures only what it needs at the top of the file. No logic is duplicated between the two.