Skip to content

IPC Contract

Yash Aryan edited this page Aug 8, 2026 · 1 revision

IPC Contract

The renderer never imports Node modules. All privileged work goes through window.api, defined by:

  1. app/src/types/api.d.ts — TypeScript contract (AnyLmApi)
  2. app/preload.tscontextBridge implementation
  3. app/src/main/ipc.tsipcMain handlers

A rename or signature drift fails bun run typecheck if all three stay honest.

Patterns

Request/response

// preload
toolsList: () => ipcRenderer.invoke("tools:list"),

// main
ipcMain.handle("tools:list", async () => toolsRegistry.list()),

Push events (chat / confirms)

Main webContents.send("chat:chunk", payload)
Preload exposes onChatChunk(cb) wrapping ipcRenderer.on
Renderer registers once and updates UI

Tool confirms and ask-user use the same push + reply pattern.

Major API groups (non-exhaustive)

Inspect api.d.ts for the full list. Groups include:

  • Auth / session / request passthroughs
  • Projects, threads, chats, references
  • Chat start/stop + stream listeners
  • Tools & skills CRUD + connectors
  • Workspace pick/clear
  • Settings get/set
  • Proxy status
  • Ollama status / models / setup
  • Artifacts / documents
  • Updates
  • Governance warnings listeners

Rules for contributors

  1. Never re-introduce nodeIntegration.
  2. Don’t expose raw ipcRenderer to the renderer.
  3. Validate/normalize arguments in main — preload is not a trust boundary against a compromised renderer, but clean typing still matters.
  4. Keep channel names stable or bump carefully; no schema registry beyond TypeScript.
  5. Prefer structured payloads already used in domain types over ad-hoc bags.

Testing IPC logic

  • Unit-test pure helpers extracted from ipc.ts when possible.
  • test/electron-mock.ts stubs Electron for bun test.
  • Full Electron e2e is minimal — manual smoke for stream/confirm paths.

Clone this wiki locally