Electron Integration

In Novyse, the desktop experience is powered by an Electron shell that hosts the React Native (Expo) web-build application. Because React Native code runs within a sandboxed renderer process, it cannot directly access system resources, execute raw database queries, or run local server tasks.

To bridge this gap, Novyse uses a dual-communication architecture:

  1. Inter-Process Communication (IPC): A secure, type-safe API bridge exposed via Electron's contextBridge and ipcRenderer.
  2. Local HTTP Server: A lightweight, dynamically-bound Express server running in the Electron main process to handle high-bandwidth tasks like file streaming, uploads, downloads, and rendering secure embeds.

1. Electron IPC Bridge API

The frontend application interacts with Electron through the window.electron global object, which is securely exposed in desktop/src/preload.ts via contextBridge.

Property / MethodCommunication TypePayload / SignatureDescription
platformPropertystring ("windows" | "macos" | "linux" | "unknown")Exposes the host operating system to the web app for OS-specific UI elements.
rpc.requestMethod (Async)(method: string, ...args: any[]) => Promise<any>Invokes registered main-process IPC handlers asynchronously.
getLocalServerUrlMethod (Sync)() => stringReturns the loopback URL of the local Express server.
sendCaptchaSuccessMethod (One-way)(token: string) => voidSends the solved Cloudflare Turnstile token back to the main process.
onNotificationClickSubscription(callback: (data: any) => void) => () => voidListens for native notification click events to route the user (returns an unsubscribe function).
window.minimizeMethod (One-way)() => voidMinimizes the desktop window.
window.toggleMaximizeMethod (One-way)() => voidToggles maximize / restore state of the desktop window.
window.closeMethod (One-way)() => voidCloses the application.
window.isMaximizedMethod (Async)() => Promise<boolean>Queries whether the window is currently maximized.
window.onStateChangedSubscription(callback: (state: { isMaximized: boolean }) => void) => () => voidListens for changes in the window maximize state.
system.getInstallSourceMethod (Async)() => Promise<string>Retrieves where the app was installed from (e.g., Flathub, system package, installer).
updater.checkMethod (Async)() => Promise<any>Triggers a check for available application updates.
updater.downloadMethod (Async)() => Promise<any>Begins downloading the available update package.
updater.installMethod (Async)() => Promise<any>Installs the downloaded update and restarts the application.
updater.onStatusSubscription(callback: (status: any) => void) => () => voidListens for real-time progress and status updates from the auto-updater.
shortcuts.registerMethod (One-way)(keys: string[], global: boolean) => voidRegisters local or global system-wide keyboard shortcuts.
shortcuts.unregisterMethod (One-way)(keys: string[], global: boolean) => voidUnregisters specific keyboard shortcuts.
shortcuts.unregisterAllMethod (One-way)() => voidClears all registered keyboard shortcuts.
shortcuts.onTriggeredSubscription(callback: (keys: string[]) => void) => () => voidListens for keyboard shortcut trigger events.

2. IPC Main Process Handlers (RPC)

These handlers are registered in desktop/src/rpc/index.ts using ipcMain.handle. They receive commands from rpc.request and perform native/system operations.

IPC Channel NamePayload Request StructureSuccess Response StructureDescription
executeDbQuery{ method: "exec" | "all" | "get" | "run", query: string, params?: any[] }{ success: boolean, data?: any, error?: string }Runs SQL operations against the local SQLite database via better-sqlite3.
secureStoreSet{ key: string, value: string }{ success: boolean, error?: string }Encrypts a value using Electron's safeStorage and writes it to the app storage directory.
secureStoreGet{ key: string }{ success: boolean, value?: string, error?: string }Reads and decrypts an encrypted value from disk.
secureStoreDelete{ key: string }{ success: boolean, error?: string }Deletes a stored encrypted key-value pair file from disk.
openFileDialog{ allowedFileTypes?: string, allowsMultipleSelection?: boolean }{ success: boolean, assets: Array<{ uri, name, size, mimeType }>, error?: string }Displays the OS native file chooser dialog.
openFile{ fileRef: string }{ success: boolean, error?: string }Opens a file stored in the local cache using the host system's default program.
showNotification{ title: string, body?: string, subtitle?: string, data?: any, icon?: string }{ success: boolean, error?: string }Dispatches native OS notifications. Preprocesses and resizes custom avatar icons with sharp.
system:set-open-on-startup{ openAtLogin: boolean }{ success: boolean, error?: string }Configures auto-start. Generates a .desktop file on Linux or calls setLoginItemSettings on Windows/macOS.
system:get-open-on-startupNone{ success: boolean, openAtLogin: boolean, error?: string }Checks if auto-start settings are currently active on the host machine.

3. Local HTTP Server Endpoints

To avoid JSON-serialization overhead for large files and bypass Chromium sandbox restrictions, a local Express server runs on localhost under a dynamically-bound port (port 0). It is started via startLocalServer() in desktop/src/server/index.ts.

EndpointMethodRequest Body / PayloadDescription
/captchaGETNoneServes a local HTML page executing the Cloudflare Turnstile CAPTCHA implementation.
/files/downloadPOST{ url: string, key: string }Downloads a file from a remote URL directly to the local cache storage (FILES_DIR) and saves its MIME type.
/files/copyPOST{ sourcePath: string, key: string }Copies a file from a local host path to the application's internal cache storage.
/files/:keyPOSTBinary Data (octet-stream)Accepts raw binary streams (up to 500MB) to upload/save files to the local cache under a unique key.
/files/:keyGETNone (URL Param :key)Serves a locally cached file back to the renderer process with HTTP caching headers and correct MIME type.
/files/:keyHEADNone (URL Param :key)Checks for existence of the file in the local cache without transferring contents. Returns 200 or 404.
/embed/htmlPOSTPlain Text (HTML content)Registers raw HTML snippets in-memory and returns a temporary local URL (e.g. http://localhost:port/embed/html/embed_1).
/embed/html/:idGETNone (URL Param :id)Serves the registered HTML snippet. Useful for embedding sandboxed third-party players (like YouTube or Kick) within the app.
    Electron Integration - Novyse Architecture | Novyse