| layout | single | ||
|---|---|---|---|
| title | IPC Contracts | ||
| sidebar |
|
This document describes the IPC payload contracts used between the renderer and main processes. Schemas are defined in code and should be treated as the source of truth.
- Input validation schemas:
src/main/ipc/validationSchemas.js - Event payload schemas:
src/shared/ipcEventSchemas.js - IPC handlers and channel registration:
src/main/ipc/* - Renderer event validation:
src/renderer/store/middleware/ipcMiddleware.js
Most IPC handlers use createHandler() or withValidation() to validate inputs. When Zod is
available, validationSchemas.js defines the expected shapes for:
- Settings payloads
- File operations (single + batch)
- Analysis inputs (single + batch)
- Suggestions and feedback payloads
- Embeddings search and query inputs
If Zod is not available, a fallback validator performs basic checks for critical fields.
Event payloads are validated in ipcMiddleware.js using schemas in ipcEventSchemas.js. If a
schema exists for a channel, an invalid payload is logged and dropped (not dispatched). Channels
without a schema pass through unvalidated. npm run verify:ipc-event-schemas (part of ci) fails
when a push event sent from main has no schema.
The Live Activity panel streams sanitized log records from src/main/services/liveLogFeed.js.
| Channel | Direction | Payload |
|---|---|---|
system:live-log:subscribe |
invoke (renderer -> main) | No input. Returns { success: true, entries } (backlog). |
system:live-log:unsubscribe |
invoke (renderer -> main) | No input. Returns { success: true }. |
system:live-log:batch |
event-only (main -> renderer) | { entries, dropped }, pushed at most every 250 ms, only when there is something to send |
Both invoke channels act on the calling window (event.sender). Each entry is
{ id, time, level, context, msg, dataText, activity }, where activity is
{ kind, icon, tone, text, file?, files?, redactedText? } or null and tone is
info | success | warn | error. A Problem activity lists every file name its message mentions in
files (the first is also file), and redactedText is its line with each name replaced by "a
file", which the panel's redact mode shows.
Unlike the events above, system:live-log:batch is validated only in main, by safeSend
against liveLogBatchSchema; the renderer receives it through the preload bridge and does not
re-check it. safeSend blocks a batch that fails, so the feed clamps every field to the schema's
bounds before sending, and no single log line can get a batch blocked:
- At most 200 entries per batch.
- Entry
msg2000 characters,dataText1500,context120,time40. - Activity
kind64 characters,icon32,text2000,redactedText2000,file2000;filesat most 8 names of 2000 characters each (non-string items and empty names are dropped). An unknowntonebecomesinfo; an activity without a stringkindandtextbecomesnull. droppedis a non-negative integer: entries discarded since this window's previous batch, including any batch whose send was refused.
test/ipcEventSchemas.liveLog.test.js pins these bounds to the feed's exported constants. Entries
in the backlog may reappear in the next batch; consumers dedupe by id.
- Add/modify a schema in
src/main/ipc/validationSchemas.jsorsrc/shared/ipcEventSchemas.js. - Update handlers to reference the schema (or to normalize inputs).
- Update tests if the payload shape changes.
- Ensure new payloads are documented here.
- Use normalized paths and IDs (see
src/shared/pathSanitization.js). - Keep query strings trimmed and bounded in length.
- Prefer the structured error envelope
{ success: false, error: { code, message, details } }produced bycreateErrorResponse(); the original error name lands inerror.details.errorType. Renderer code must normalizeerrorto a string (toErrorMessage) before rendering it.
Some channels mutate shared state or hold expensive resources (multi-GB model loads, full-library
scans) and are serialized via CONCURRENCY_LIMITED_CHANNELS, defined in
src/shared/ipcEnforcement.js (the source of truth) and consumed by the preload bridge. A second
invoke() against the same channel while one is in-flight rejects synchronously with
Operation already in progress: <channel>.
| Channel | Why serialized |
|---|---|
embeddings:full-rebuild |
Multi-GB model + index reload |
embeddings:reanalyze-all |
Library-wide AI work; pins inference slots |
embeddings:rebuild-files |
Index rebuild against current state |
embeddings:rebuild-folders |
Same as above for folders index |
embeddings:compute-clusters |
Long full-corpus pass |
embeddings:rebuild-bm25 |
Full reindex |
embeddings:find-duplicates |
Pairwise scan over all vectors |
embeddings:trash-duplicates |
Mutates store + filesystem |
embeddings:reindex-file-hashes |
Full hash-rebuild walk |
embeddings:clear-clusters |
Mutates cluster state |
embeddings:rename-cluster |
Mutates cluster state |
embeddings:diagnose-search |
Walks the full retrieval pipeline; holds a model slot |
analysis:analyze-batch |
Batch IO + pipeline coordination |
analysis:analyze-media |
Per-file ffmpeg + Whisper slot pinning |
organize:auto |
LLM analysis + atomic moves over the same files |
organize:batch |
Atomic batch moves; overlapping runs could double-move or race |
llama:download-model |
Multi-GB download |
llama:delete-model |
Filesystem mutation against active models dir |
llama:update-config |
Reloads model in-place |
llama:test-connection |
Triggers cold model load if needed; coalesced in LlamaService |
ai-provider:test-provider |
Round-trips a real LLM call |
system:dismiss-degraded-flag |
Mutates the shared degradedState map |
system:install-vc-runtime |
One elevated VC++ Redistributable install at a time |
Downloads-triage channels (start, deep-scan, act) share a single lock key
(DOWNLOADS_TRIAGE_MUTATION_LOCK_KEY) so any one of the three in-flight blocks the others.