# IPC channels reference

> Complete IPC_CHANNELS catalog grouped by domain (auth, session, claude, browser, git, mcp, qmd, ssh, voice, analytics) with invoke vs push event semantics.

- Repository: Parcha-ai/build
- GitHub: https://github.com/Parcha-ai/build
- Human docs: https://grok-wiki.com/public/docs/parcha-ai-build-bea5702b371b
- Complete Markdown: https://grok-wiki.com/public/docs/parcha-ai-build-bea5702b371b/llms-full.txt

## Source Files

- `src/shared/constants/channels.ts`
- `src/main/preload.ts`
- `src/main/ipc/claude.ipc.ts`
- `src/main/ipc/browser.ipc.ts`
- `src/main/ipc/analytics.ipc.ts`
- `CLAUDE.md`

---

---
title: "IPC channels reference"
description: "Complete IPC_CHANNELS catalog grouped by domain (auth, session, claude, browser, git, mcp, qmd, ssh, voice, analytics) with invoke vs push event semantics."
---

Build names every main↔renderer boundary in `IPC_CHANNELS` (`src/shared/constants/channels.ts`). Handlers register from `src/main/index.ts` into `src/main/ipc/*.ts` (and a few inline handlers in `index.ts`). The renderer reaches main only through `contextBridge.exposeInMainWorld('electronAPI', …)` in `src/main/preload.ts`—no direct `ipcRenderer` access in React code.

## IPC patterns

| Pattern | Renderer | Main | Returns payload? | Typical use |
|--------|----------|------|------------------|-------------|
| **Invoke** | `ipcRenderer.invoke(channel, …args)` | `ipcMain.handle(channel, handler)` | Yes (`Promise`) | CRUD, settings, start/stop work |
| **Push (main → renderer)** | `ipcRenderer.on(channel, handler)` via preload `on*` helpers | `webContents.send(channel, payload)` or `broadcastToAll` | No | Streaming, status, dialogs |
| **Send (renderer → main)** | `ipcRenderer.send(channel, …args)` | `ipcMain.on(channel, handler)` | No | High-frequency input (terminal, webview registration) |

<Note>
Preload `on*` methods return an unsubscribe function. Always call it on unmount to avoid duplicate listeners.
</Note>

```mermaid
sequenceDiagram
  participant R as Renderer (React)
  participant P as preload.ts
  participant M as ipcMain handler
  participant S as Service layer

  R->>P: electronAPI.claude.sendMessage(...)
  P->>M: invoke claude:send-message
  M->>S: ClaudeService.stream...
  loop streaming
    S->>R: push claude:stream-chunk
  end
  S->>R: push claude:stream-end
  M-->>P: Promise resolved
  P-->>R: invoke completes
```

**Bidirectional flows** pair invoke + push on different channels—for example `claude:permission-request` (push) with `claude:permission-response` (invoke).

## Handler registration map

| Registrar | File |
|-----------|------|
| `registerAuthHandlers` | `auth.ipc.ts` |
| `registerSessionHandlers` | `session.ipc.ts` |
| `registerClaudeHandlers` | `claude.ipc.ts` |
| `registerGitHandlers` | `git.ipc.ts` |
| `registerBrowserHandlers` | `browser.ipc.ts` |
| `registerSSHHandlers` | `ssh.ipc.ts` |
| `registerMcpHandlers` | `mcp.ipc.ts` |
| `registerQmdHandlers` | `qmd.ipc.ts` |
| `registerVoiceHandlers` | `voice.ipc.ts` |
| `registerRealtimeHandlers` | `realtime.ipc.ts` |
| `registerAudioHandlers` | `audio.ipc.ts` |
| `registerAnalyticsHandlers` | `analytics.ipc.ts` |
| `registerQueueHandlers` | `queue.ipc.ts` |
| App, settings, docker | `settings.ipc.ts` |
| GStack | inline in `index.ts` |

---

## Auth

| Channel constant | String | Semantics | Preload API |
|------------------|--------|-----------|-------------|
| `AUTH_LOGIN` | `auth:login` | Invoke | `auth.login()` |
| `AUTH_LOGOUT` | `auth:logout` | Invoke | `auth.logout()` |
| `AUTH_GET_USER` | `auth:get-user` | Invoke | `auth.getUser()` |
| `AUTH_GET_REPOS` | `auth:get-repos` | Invoke | `auth.getRepos()` |
| `AUTH_CHECK_PROVIDERS` | `auth:check-providers` | Invoke | `auth.checkProviders()` |
| `AUTH_STATUS` | `auth:status` | — | *Declared in `IPC_CHANNELS`; no `ipcMain.handle` or preload wrapper found* |

**Push (not in `IPC_CHANNELS`):**

| Channel | Semantics | Preload API |
|---------|-----------|-------------|
| `auth:oauth-callback` | Main pushes OAuth code after `claudette://` callback | `auth.onOAuthCallback()` |

---

## Session

| Channel constant | String | Semantics | Preload API |
|------------------|--------|-----------|-------------|
| `SESSION_CREATE` | `session:create` | Invoke | `sessions.create(config)` |
| `SESSION_START` | `session:start` | Invoke | `sessions.start(sessionId)` |
| `SESSION_STOP` | `session:stop` | Invoke | `sessions.stop(sessionId)` |
| `SESSION_DELETE` | `session:delete` | Invoke | `sessions.delete(sessionId)` |
| `SESSION_LIST` | `session:list` | Invoke | `sessions.list()` |
| `SESSION_GET` | `session:get` | Invoke | `sessions.get(sessionId)` |
| `SESSION_UPDATE` | `session:update` | Invoke | `sessions.update(sessionId, updates)` |
| `SESSION_REWIND_FORK` | `session:rewind-fork` | Invoke | `sessions.rewindAndFork(...)` |
| `SESSION_CREATE_FORK` | `session:create-fork` | Invoke | `sessions.createFork(...)` |
| `SESSION_GET_FORK_GROUP` | `session:get-fork-group` | Invoke | `sessions.getForkGroup(sessionId)` |
| `SESSION_SCAN_REMOTE` | `session:scan-remote` | Invoke | `sessions.scanRemoteTranscripts(sessionId)` |
| `SESSION_STATUS_CHANGED` | `session:status-changed` | Push (`SessionService` → `broadcastToAll`) | `sessions.onStatusChanged()` |
| `SESSION_LIST_UPDATED` | `session:list-updated` | Push (full list refresh) | `sessions.onListUpdated()` |

---

## Claude (agent harness)

### Invoke (renderer → main)

| Channel constant | String | Preload API |
|------------------|--------|-------------|
| `CLAUDE_SEND_MESSAGE` | `claude:send-message` | `claude.sendMessage(...)` |
| `CLAUDE_RESUME_REMOTE_TURN` | `claude:resume-remote-turn` | `claude.resumeRemoteTurn(...)` |
| `CLAUDE_GET_MESSAGES` | `claude:get-messages` | `claude.getMessages(...)` |
| `CLAUDE_HAS_BUILD_TRANSCRIPT` | `claude:has-build-transcript` | `claude.hasBuildTranscript(...)` |
| `CLAUDE_GET_MODELS` | `claude:get-models` | `claude.getModels()` |
| `CLAUDE_CANCEL` | `claude:cancel` | `claude.cancel(sessionId)` |
| `CLAUDE_PERMISSION_RESPONSE` | `claude:permission-response` | `claude.respondToPermission(...)` |
| `CLAUDE_QUESTION_RESPONSE` | `claude:question-response` | `claude.respondToQuestion(...)` |
| `CLAUDE_PLAN_APPROVAL_RESPONSE` | `claude:plan-approval-response` | `claude.respondToPlanApproval(...)` |
| `CLAUDE_INJECT_MESSAGE` | `claude:inject-message` | `claude.injectMessage(...)` |
| `CLAUDE_HAS_ACTIVE_QUERY` | `claude:has-active-query` | `claude.hasActiveQuery(...)` |
| `CLAUDE_SET_PERMISSION_MODE` | `claude:set-permission-mode` | `claude.setPermissionMode(...)` |
| `CLAUDE_BTW_ASK` | `claude:btw-ask` | `claude.askBtw(...)` |
| `CLAUDE_RC_START` / `CLAUDE_RC_STOP` | `claude:rc-start` / `claude:rc-stop` | `claude.startRc` / `stopRc` |
| `CLAUDE_REWIND_PREVIEW` / `CLAUDE_REWIND_EXECUTE` | `claude:rewind-preview` / `claude:rewind-execute` | `claude.rewindPreview` / `rewindExecute` |
| `AUTO_RESUME_SAVE_STATE` | `auto-resume:save-state` | `claude.saveAutoResumeState(...)` |
| `AUTO_RESUME_GET_STATE` | `auto-resume:get-state` | `claude.getAutoResumeState()` |
| `AUTO_RESUME_CLEAR_STATE` | `auto-resume:clear-state` | `claude.clearAutoResumeState()` |

### Push (main → renderer)

| Channel constant | String | Preload listener | Emitted from |
|------------------|--------|------------------|--------------|
| `CLAUDE_STREAM_CHUNK` | `claude:stream-chunk` | `onStreamChunk` | `claude.ipc.ts` (`sendToSender`) |
| `CLAUDE_THINKING_CHUNK` | `claude:thinking-chunk` | `onThinkingChunk` | `claude.ipc.ts` |
| `CLAUDE_STREAM_END` | `claude:stream-end` | `onStreamEnd` | `claude.ipc.ts` |
| `CLAUDE_STREAM_ERROR` | `claude:stream-error` | `onStreamError` | `claude.ipc.ts` |
| `CLAUDE_TOOL_CALL` | `claude:tool-call` | `onToolCall` | `claude.ipc.ts` |
| `CLAUDE_TOOL_RESULT` | `claude:tool-result` | `onToolResult` | `claude.ipc.ts` |
| `CLAUDE_SYSTEM_INFO` | `claude:system-info` | `onSystemInfo` | `claude.ipc.ts` |
| `CLAUDE_PERMISSION_REQUEST` | `claude:permission-request` | `onPermissionRequest` | `claude.service.ts` |
| `CLAUDE_QUESTION_REQUEST` | `claude:question-request` | `onQuestionRequest` | `claude.service.ts` |
| `CLAUDE_PLAN_APPROVAL_REQUEST` | `claude:plan-approval-request` | `onPlanApprovalRequest` | `claude.service.ts` |
| `CLAUDE_PLAN_CONTENT` | `claude:plan-content` | `onPlanContent` | `claude.service.ts` |
| `CLAUDE_COMPACTION_STATUS` | `claude:compaction-status` | `onCompactionStatus` | `claude.service.ts` |
| `CLAUDE_COMPACTION_COMPLETE` | `claude:compaction-complete` | `onCompactionComplete` | `claude.service.ts` |
| `CLAUDE_CONTEXT_USAGE` | `claude:context-usage` | `onContextUsage` | `claude.ipc.ts` / service |
| `CLAUDE_PERMISSION_MODE_CHANGED` | `claude:permission-mode-changed` | `onPermissionModeChanged` | `claude.service.ts` |
| `CLAUDE_TASK_NOTIFICATION` | `claude:task-notification` | `onTaskNotification` | `claude.service.ts` |
| `CLAUDE_TASK_PROGRESS` | `claude:task-progress` | `onTaskProgress` | `claude.service.ts` |
| `CLAUDE_TASK_UPDATED` | `claude:task-updated` | `onTaskUpdated` | `claude.service.ts` |
| `CLAUDE_BTW_RESPONSE` | `claude:btw-response` | `onBtwResponse` | `claude.service.ts` |
| `CLAUDE_RC_STARTED` / `CLAUDE_RC_STOPPED` | `claude:rc-started` / `claude:rc-stopped` | `onRcStarted` / `onRcStopped` | `claude.service.ts` |
| `CLAUDE_WAKEUP_FIRED` | `claude:wakeup-fired` | `onWakeupFired` | `index.ts` (`broadcastToAll`) |
| `CLAUDE_AUTO_ROUTE_DECISION` | `claude:auto-route-decision` | `onAutoRouteDecision` | `claude.service.ts` (Auto Build routing) |
| `CLAUDE_BACKGROUND_TASK_OUTPUT` | `claude:background-task-output` | `onBackgroundTaskOutput` | *Preload wired; no main sender located* |

**Message queue (string literals, not in `IPC_CHANNELS`):**

| Channel | Semantics | Preload |
|---------|-----------|---------|
| `queue:enqueue` / `remove` / `edit` / `moveToFront` / `clear` / `getState` | Invoke | `electronAPI.queue.*` |
| `queue:send-next` | Push (main asks renderer to send next queued message) | `claude.onQueueSendNext()` |
| `queue:state-changed` | Push | `claude.onQueueStateChanged()` |

---

## Browser

| Channel constant | String | Semantics | Preload API |
|------------------|--------|-----------|-------------|
| `BROWSER_CAPTURE_SNAPSHOT` | `browser:capture-snapshot` | Invoke | `browser.captureSnapshot` |
| `BROWSER_NAVIGATE_TO` | `browser:navigate-to` | Invoke | `browser.navigateTo` |
| `BROWSER_GET_SNAPSHOT` | `browser:get-snapshot` | Invoke | `browser.getSnapshot` |
| `BROWSER_CLEAR_STORAGE` | `browser:clear-storage` | Invoke | `browser.clearStorage` |
| `BROWSER_INJECT_INSPECTOR` | `browser:inject-inspector` | Invoke (preload) | `browser.injectInspector` — *no `ipcMain.handle` found* |
| `BROWSER_NAVIGATE` | `browser:navigate` | Push | `browser.onNavigate()` ← `browser.service.ts` |
| `BROWSER_ELEMENT_SELECTED` | `browser:element-selected` | Push (preload listener) | `browser.onElementSelected()` — *no main sender found* |
| `BROWSER_UPDATE` | `browser:update` | Push (Stagehand screenshot/URL) | `browser.onBrowserUpdate()` |
| `BROWSER_OPEN_PANEL` | `browser:open-panel` | Push | `browser.onBrowserOpenPanel()` |

**Send / on pairs (webview bridge, not in `IPC_CHANNELS`):**

| Channel | Direction | Role |
|---------|-----------|------|
| `browser:register-webview` / `browser:unregister-webview` | Renderer → main (`send`) | CDP target registration (`browser.service.ts`) |
| `browser:capture-snapshot` | Main → renderer (`send`) | Async capture request |
| `browser:snapshot-captured` | Renderer → main (`send`) | Snapshot payload reply |
| `browser:action` / `browser:action-result` | Round-trip automation | Stagehand/CDP |
| `browser:automation-event` | Main → renderer | `browser.onAutomationEvent()` |

---

## Git

| Channel constant | String | Semantics | Preload API |
|------------------|--------|-----------|-------------|
| `GIT_STATUS` | `git:status` | Invoke | `git.getStatus` |
| `GIT_LOG` | `git:log` | Invoke | `git.getLog` |
| `GIT_BRANCHES` | `git:branches` | Invoke | `git.getBranches` |
| `GIT_CHECKOUT` | `git:checkout` | Invoke | `git.checkout` |
| `GIT_DIFF` | `git:diff` | Invoke | `git.getDiff` |
| `GIT_COMMIT` | `git:commit` | Invoke | `git.commit` |
| `GIT_PUSH` / `GIT_PULL` | `git:push` / `git:pull` | Invoke | `git.push` / `pull` |
| `GIT_CLONE` | `git:clone` | Invoke | `git.clone` |
| `GIT_REMOTE_BRANCH` | `git:remote-branch` | Invoke | `git.getRemoteBranch` (SSH) |
| `GIT_WATCH_BRANCH` / `GIT_UNWATCH_BRANCH` | `git:watch-branch` / `git:unwatch-branch` | Invoke | `git.watchBranch` / `unwatchBranch` |
| `GIT_BRANCH_CHANGED` | `git:branch-changed` | Push | `git.onBranchChanged()` |

---

## MCP

| Channel constant | String | Semantics | Preload API |
|------------------|--------|-----------|-------------|
| `MCP_GET_SERVERS` | `mcp:get-servers` | Invoke | `mcp.getServers(sessionId, projectPath?)` |
| `MCP_GET_RAW_CONFIG` | `mcp:get-raw-config` | Invoke | `mcp.getRawConfig(serverId)` |
| `MCP_GET_MARKETPLACE` | `mcp:get-marketplace` | Invoke | `mcp.getMarketplace()` |
| `MCP_INSTALL_SERVER` | `mcp:install-server` | Invoke | `mcp.install(serverId, authValues)` |
| `MCP_INSTALL_SERVER_RAW` | `mcp:install-server-raw` | Invoke | `mcp.installRaw(...)` |
| `MCP_UNINSTALL_SERVER` | `mcp:uninstall-server` | Invoke | `mcp.uninstall(serverId)` |

Installing or changing MCP config can trigger SSH remote sync inside `mcp.ipc.ts` (no separate push channel).

---

## QMD (semantic search)

| Channel constant | String | Semantics | Preload API |
|------------------|--------|-----------|-------------|
| `QMD_GET_STATUS` | `qmd:get-status` | Invoke | `qmd.getStatus()` |
| `QMD_ENSURE_INDEXED` | `qmd:ensure-indexed` | Invoke | `qmd.ensureIndexed(projectPath)` |
| `QMD_CREATE_COLLECTION` | `qmd:create-collection` | Invoke | `qmd.createCollection(...)` |
| `QMD_GENERATE_EMBEDDINGS` | `qmd:generate-embeddings` | Invoke | `qmd.generateEmbeddings(...)` |
| `QMD_SEARCH` | `qmd:search` | Invoke | `qmd.search(query, options?)` |
| `QMD_GET_PROJECT_PREFERENCE` | `qmd:get-project-preference` | Invoke | `qmd.getProjectPreference` |
| `QMD_SET_PROJECT_PREFERENCE` | `qmd:set-project-preference` | Invoke | `qmd.setProjectPreference` |
| `QMD_SHOULD_PROMPT` | `qmd:should-prompt` | Invoke | `qmd.shouldPrompt` |
| `QMD_AUTO_INSTALL` | `qmd:auto-install` | Invoke | `qmd.autoInstall()` |
| `QMD_INDEXING_PROGRESS` | `qmd:indexing-progress` | Push | `qmd.onIndexingProgress()` |
| `QMD_PROMPT_RESPONSE` | `qmd:prompt-response` | Push (opt-in indexing UI) | `qmd.onPromptRequest()` |

<Warning>
Despite the name `QMD_PROMPT_RESPONSE`, main uses this channel to **request** that the renderer show the QMD opt-in prompt (`claude.service.ts` → `webContents.send`). Renderer answers via invoke handlers, not by sending on this channel.
</Warning>

---

## SSH

| Channel constant | String | Semantics | Preload API |
|------------------|--------|-----------|-------------|
| `SSH_TEST_CONNECTION` | `ssh:test-connection` | Invoke | `ssh.testConnection` |
| `SSH_CREATE_SESSION` | `ssh:create-session` | Invoke | `ssh.createSession` |
| `SSH_LIST_RESUME_CANDIDATES` | `ssh:list-resume-candidates` | Invoke | `ssh.listResumeCandidates` |
| `SSH_SYNC_SETTINGS` | `ssh:sync-settings` | Invoke | *(no preload wrapper)* |
| `SSH_RUN_WORKTREE_SCRIPT` | `ssh:run-worktree-script` | Invoke | *(no preload wrapper)* |
| `SSH_GET_SAVED_CONFIG` / `SSH_SAVE_CONFIG` | `ssh:get-saved-config` / `ssh:save-config` | Invoke | `ssh.getSavedConfig` / `saveConfig` |
| `SSH_CHECK_CONNECTION` | `ssh:check-connection` | Invoke | `ssh.checkConnection` |
| `SSH_TELEPORT_SESSION` | `ssh:teleport-session` | Invoke | `ssh.teleportSession` |
| `SSH_DOWNLOAD_SESSION` | `ssh:download-session` | Invoke | `ssh.downloadSession` |
| `SSH_RECONNECT` | `ssh:reconnect` | Invoke | `ssh.reconnect` |
| `SSH_HAS_ACTIVE_REMOTE_PROCESS` | `ssh:has-active-remote-process` | Invoke | `ssh.hasActiveRemoteProcess` |
| `SSH_HAS_RECOVERABLE_REMOTE_PROCESS` | `ssh:has-recoverable-remote-process` | Invoke | `ssh.hasRecoverableRemoteProcess` |
| `SSH_BROWSE_REMOTE_FILES` | `ssh:browse-remote-files` | Invoke | `ssh.browseRemoteFiles` |
| `SSH_SETUP_PROGRESS` | `ssh:setup-progress` | Push | `ssh.onSetupProgress()` |
| `SSH_DOWNLOAD_PROGRESS` | `ssh:download-progress` | Push | `ssh.onDownloadProgress()` |
| `SSH_CONNECTION_LOST` | `ssh:connection-lost` | Push | `ssh.onConnectionLost()` |

---

## Voice and audio

Voice uses ElevenLabs conversational AI (`voice.ipc.ts`). Audio covers batch STT/TTS (`audio.ipc.ts`). Realtime OpenAI transcription lives in `realtime.ipc.ts`.

### Voice (`VOICE_*`)

| Type | Channels | Preload |
|------|----------|---------|
| Invoke | `VOICE_CONNECT`, `DISCONNECT`, `SEND_AUDIO`, `SEND_TEXT`, `END_INPUT`, `CLEAR_AUDIO_BUFFER`, `CONTEXT_UPDATE`, `TOOL_RESULT`, `UPDATE_AGENT_PROMPT`, `USER_ACTIVITY`, `GET_SIGNED_URL`, `GET_CONVERSATION_TOKEN` | `electronAPI.voice.*` |
| Push | `VOICE_CONNECTED`, `DISCONNECTED`, `RECONNECTING`, `USER_TRANSCRIPT`, `AGENT_RESPONSE`, `AUDIO_CHUNK`, `INTERRUPTION`, `ERROR`, `TOOL_CALL` | matching `on*` methods |

### Audio + realtime (related)

| Domain | Invoke examples | Push examples |
|--------|-----------------|---------------|
| `AUDIO_*` | `AUDIO_TRANSCRIBE`, `AUDIO_TTS_STREAM`, key/settings getters | `AUDIO_TTS_CHUNK`, `COMPLETE`, `ERROR` |
| `REALTIME_*` | `REALTIME_CONNECT`, `SEND_AUDIO`, `COMMIT_AUDIO`, … | `REALTIME_TRANSCRIPTION_DELTA`, `CONNECTED`, `SPEECH_STARTED`, … |

---

## Analytics

| Channel constant | String | Semantics | Preload API |
|------------------|--------|-----------|-------------|
| `ANALYTICS_GET_SUMMARY` | `analytics:get-summary` | Invoke | `analytics.getSummary()` |
| `ANALYTICS_GET_SESSION_COST` | `analytics:get-session-cost` | Invoke | `analytics.getSessionCost(sessionId)` |
| `ANALYTICS_GET_TIER_CONFIG` | `analytics:get-tier-config` | Invoke | `analytics.getTierConfig()` |
| `ANALYTICS_SET_TIER_CONFIG` | `analytics:set-tier-config` | Invoke | `analytics.setTierConfig(config)` |
| `ANALYTICS_GET_HARNESS_INSIGHTS` | `analytics:get-harness-insights` | Invoke | `analytics.getHarnessInsights()` |
| `ANALYTICS_REFRESH_HISTORICAL_USAGE` | `analytics:refresh-historical-usage` | Invoke | `analytics.refreshHistoricalUsage(options?)` |
| `ANALYTICS_RUN_ROUTER_EVAL` | `analytics:run-router-eval` | Invoke | `analytics.runRouterEval(options?)` |
| `ANALYTICS_RECORD_HARNESS_SELECTION` | `analytics:record-harness-selection` | Invoke | `analytics.recordHarnessSelection(event)` |
| `ANALYTICS_TOKEN_EVENT` | `analytics:token-event` | Push (per-token usage during streams) | `analytics.onTokenEvent()` |

Token events are also emitted from `claude.service.ts` during agent turns, not only from `analytics.ipc.ts`.

---

## Other `IPC_CHANNELS` domains (compact)

| Prefix | Invoke-heavy | Push-heavy | Notes |
|--------|--------------|------------|-------|
| `settings:` / `app:` | `SETTINGS_*`, most `APP_*` | `APP_CMD_R_PRESSED`, `APP_SHORTCUT_TRIGGERED` | Registered in `settings.ipc.ts` + `index.ts` |
| `dev:` | Most dev session/worktree ops | `DEV_SETUP_PROGRESS` (*no sender found*) | Extra literals: `dev:init-git`, `dev:get-active-session`, … |
| `terminal:` | `TERMINAL_CREATE` | `TERMINAL_OUTPUT:{terminalId}` | Input uses **send**: `TERMINAL_INPUT`, `RESIZE`, `CLOSE` |
| `docker:` | `DOCKER_STATUS` | — | `DOCKER_CONTAINER_STATS` / `LOGS` declared, no handlers found |
| `fs:` | All four | — | Project-scoped file search |
| `extension:` | Scan/install skills | — | |
| `secure-keys:` | All four | — | Key interception before agent send |
| `memory:` | remember/recall/forget/list/sync | — | |
| `plugin:` | Marketplace CRUD | — | |
| `codex:` | `CODEX_RUN`, `CODEX_CANCEL` | `CODEX_STREAM_CHUNK`, `THINKING`, `TOOL_CALL`, `COMPLETE`, `ERROR` | Second-opinion harness |
| `gstack:` | All four | — | Handlers inline in `index.ts` |
| `openclaw:` | `OPENCLAW_CREATE_SESSION` | — | |

---

## Constants without active handlers

These appear in `IPC_CHANNELS` and sometimes in preload, but no matching `ipcMain.handle` / `ipcMain.on` was found in the repo:

- `AUTH_STATUS`
- `DOCKER_CONTAINER_STATS`, `DOCKER_CONTAINER_LOGS`
- `BROWSER_INJECT_INSPECTOR`, `BROWSER_REGISTER` (registration uses `browser:register-webview` instead)
- `AUTO_RESUME_TRIGGER`
- `DEV_SETUP_PROGRESS`
- `CLAUDE_BACKGROUND_TASK_OUTPUT` (listener only)

Treat them as reserved or incomplete wiring until a handler lands.

---

## Related pages

<CardGroup>
  <Card title="IPC and preload bridge" href="/ipc-bridge">
    How handlers map to window.electronAPI and contextBridge security.
  </Card>
  <Card title="Electron process model" href="/electron-architecture">
    Main vs renderer boundaries and service layout.
  </Card>
  <Card title="Manage coding sessions" href="/manage-sessions">
    Session lifecycle channels in product workflow.
  </Card>
  <Card title="Auto Build routing" href="/auto-build-routing">
    claude:auto-route-decision and router analytics.
  </Card>
  <Card title="MCP servers" href="/mcp-servers">
    MCP install channels and remote SSH sync.
  </Card>
  <Card title="Semantic search (QMD)" href="/semantic-search-qmd">
    QMD invoke/push indexing flow.
  </Card>
</CardGroup>
