How Python Backend and Electron Processes Communicate via IPC in LifeTrace

LifeTrace uses Electron's IPC channel exclusively for UI-to-main-process messaging, while all business logic communication between the Electron main process and the Python FastAPI backend occurs over a local HTTP REST API.

The freeu-group/lifetrace repository implements a clean separation between the user interface and core logic by combining an Electron frontend with a Python FastAPI backend. Understanding how these two distinct runtimes communicate is essential for developers extending the application's functionality or debugging cross-process issues.

Architecture Overview: IPC vs HTTP

LifeTrace employs a two-layer communication strategy that keeps the Electron IPC separate from the HTTP REST API. The data flow follows this path:

  1. Renderer Process (React/Next.js) ↔ Main Process (Node.js): Uses Electron's ipcRenderer and ipcMain modules for UI-level events like notifications and window controls.
  2. Main ProcessPython Backend (FastAPI/uvicorn): Uses standard HTTP requests to REST endpoints exposed by the Python server.

This design keeps the runtimes loosely coupled, allowing the Python backend to run independently for testing or alternative clients.

Electron IPC Layer (Renderer to Main)

The renderer process never communicates directly with Python. Instead, it sends messages to the main process via the preload script bridge defined in free-todo-frontend/electron/preload.ts.

In free-todo-frontend/electron/ipc-handlers.ts, the main process registers handlers using ipcMain.handle and ipcMain.on to receive UI events:

// In ipc-handlers.ts (main process)
ipcMain.handle(
  "show-notification",
  async (_event, data: NotificationData) => {
    logger.info(`Received notification request: ${data.id}`);
    showSystemNotification(data, windowManager);
  },
);

The renderer invokes these handlers through the exposed electronAPI:

// In a React component (renderer)
window.electronAPI?.showNotification({
  id: "msg-1",
  title: "Todo captured",
  content: "Your screenshot was processed",
  timestamp: new Date().toISOString(),
});

HTTP Communication Bridge (Main to Python)

All business logic requests flow from the main process to the Python backend via HTTP. The BackendServer class in free-todo-frontend/electron/backend-server.ts manages the Python process lifecycle and provides the base URL for requests.

When the main process needs to send data—such as a screenshot for OCR—it constructs an HTTP POST request to the FastAPI endpoint. This pattern is implemented in free-todo-frontend/electron/ipc-handlers-todo-capture.ts:

// Main-process handler (ipc-handlers-todo-capture.ts)
ipcMain.handle("capture-and-extract-todos", async (_event, panelBounds) => {
  // 1️⃣ Capture screen with desktopCapturer
  // 2️⃣ Convert image → base64
  // 3️⃣ Build backend URL from BackendServer
  const backendUrl = getBackendUrl();          // from backend-server.ts
  const apiUrl = `${backendUrl}/api/floating-capture/extract-todos`;
  // 4️⃣ POST base64 image to FastAPI using net.request
  const response = await sendToBackend(apiUrl, base64Data, true);
  return response;
});

The sendToBackend function uses Electron’s net.request API (a wrapper around Node’s HTTP client) to transmit data to the Python server.

Python FastAPI Backend Endpoints

The Python side exposes standard REST endpoints using FastAPI. In lifetrace/routers/floating_capture.py, the endpoint receiving the screenshot data is defined as:


# lifetrace/routers/floating_capture.py

@router.post("/floating-capture/extract-todos")
async def extract_todos(payload: ExtractPayload):
    # payload.image_base64 contains the screenshot

    # ... run OCR / LLM extraction ...

    return {
        "success": True,
        "extracted_todos": [...],
        "created_count": 3,
    }

Additional business logic endpoints are located in lifetrace/routers/todo.py and other router modules, all following the same HTTP request/response pattern.

Backend Lifecycle and Port Management

The BackendServer class in free-todo-frontend/electron/backend-server.ts handles the complexity of starting and connecting to the Python process.

When the Electron app launches, it first attempts to detect an already-running backend via the detectRunningBackendPort() method, which probes the /health endpoint on potential ports:

// BackendServer.detectRunningBackendPort() implementation
async detectRunningBackendPort(): Promise<number | null> {
  // Probes /health endpoint to find existing Python server
  // Returns port if found, null otherwise
}

If no server is found, BackendServer.start() spawns a new Python process:

// BackendServer.start() (partial)
this.resolveBackendPaths();               // finds entry script / venv
await ensurePythonRuntime(...);           // creates venv if needed
this.port = await portManager.findAvailablePort(...);
const spawnCommand = this.backendRuntime === "pyinstaller"
    ? this.backendEntryScript
    : this.venvPythonPath;
const spawnArgs = this.backendRuntime === "pyinstaller"
    ? backendArgs
    : [this.backendEntryScript, ...backendArgs];
this.process = spawn(spawnCommand, spawnArgs, { cwd: this.backendSourceDir });

The Python server runs independently on the dynamically allocated port, exposing the REST API that the main process consumes via HTTP.

Summary

  • Electron IPC is UI-only: The renderer process communicates with the main process via ipcRenderer and ipcMain handlers defined in free-todo-frontend/electron/ipc-handlers.ts for window management and notifications, not for business logic.
  • HTTP bridges the gap: All data exchange between the Electron main process and the Python FastAPI backend occurs over local HTTP REST APIs, not through IPC channels.
  • Port discovery is dynamic: The BackendServer class in free-todo-frontend/electron/backend-server.ts handles spawning the Python process and discovers or assigns ports via health-check probes to the /health endpoint.
  • Python exposes REST endpoints: FastAPI routers in lifetrace/routers/ (such as floating_capture.py) receive HTTP POST requests containing base64-encoded screenshots and return extracted data as JSON.

Frequently Asked Questions

Does LifeTrace use Electron's IPC for Python communication?

No. Electron's IPC (ipcMain and ipcRenderer) is used exclusively for communication between the renderer process (React/Next.js UI) and the main process (Node.js). The main process communicates with the Python FastAPI backend via standard HTTP requests to local REST endpoints, not through IPC channels.

How does the app find the Python backend port?

When the Electron app starts, the BackendServer class attempts to locate an already-running Python server by calling detectRunningBackendPort(). This method probes the /health endpoint on a range of potential ports. If a running instance is found, Electron connects to it; otherwise, BackendServer.start() spawns a new Python process on a dynamically assigned free port.

Can the Python backend run independently of Electron?

Yes. The Python FastAPI backend is designed to run as a standalone server using uvicorn. Because the Electron main process communicates via HTTP REST APIs, you can start the Python server manually (e.g., python -m lifetrace.server) and interact with it using curl, a web browser, or any other HTTP client without launching the Electron frontend.

What protocol does the main process use to send screenshots to Python?

The main process sends screenshots to the Python backend using HTTP POST requests. In free-todo-frontend/electron/ipc-handlers-todo-capture.ts, the sendToBackend function uses Electron's net.request API (a Node.js HTTP client wrapper) to POST base64-encoded image data to endpoints like /api/floating-capture/extract-todos defined in lifetrace/routers/floating_capture.py.

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 →