Skip to content

IPC Protocol Dictionary

1. Channel Topology

flowchart TB
  R[Renderer] --> P[preload.ts]
  P -->|ipcRenderer.send/invoke| M[main/index.ts ipcMain]
  M -->|HTTP + internal token| B[backend routes]

2. Channel Dictionary

ChannelIPC TypeParamsReturn SchemaMain Handler Behavior
app:close-windowsendnonenoneCloses focused window or main window
i18n:get-localeinvokenonePromise<string>Returns current resolved locale
i18n:set-localeinvokelocale: stringPromise<string>Resolves/persists in-memory locale and updates title
app:get-runtime-user-nameinvokenonePromise<string>Returns OS username fallback chain
app:get-version-infoinvokenonePromise<{ appName: string; version: string; buildVersion: string; buildTime: string; commit: string; electron: string; chromium: string; node: string; v8: string; os: string }>Returns About metadata including app version/build plus runtime technical information
app:get-pending-launch-working-directoryinvokenonePromise<string | null>Returns current pending context-launch working directory parsed from CLI
app:get-downloads-pathinvokenonePromise<string>Returns the OS downloads directory for local save defaults
app:create-sftp-temporary-fileinvokefileName: stringPromise<string>Creates a unique local destination under the Cosmosh SFTP temp root for backend download/open flows
app:open-sftp-temporary-fileinvokelocalPath: stringPromise<boolean>Opens an existing file under the Cosmosh SFTP temp root with the OS default application
app:start-sftp-temporary-file-watchinvokelocalPath: stringPromise<string>Starts a debounced watcher for one existing file under the Cosmosh SFTP temp root and returns a watch id
app:stop-sftp-temporary-file-watchinvokewatchId: stringPromise<boolean>Stops a previously created SFTP temp-file watcher
app:show-sftp-open-with-dialoginvokelocalPath: stringPromise<boolean>Windows only: validates a temp file path and opens the system Open With picker through the shell openas verb
app:list-sftp-open-with-applicationsinvokelocalPath: stringPromise<Array<{ id: string; name: string; path: string; bundleIdentifier?: string; iconDataUrl?: string }>>macOS only: validates a temp file path and returns NSWorkspace applications that can open it
app:open-sftp-file-with-applicationinvokelocalPath: string, applicationPath: stringPromise<boolean>macOS only: validates the temp file and selected app against the available application list, then opens the file with that app
app:sftp-temporary-file-changedevent (main -> renderer){ watchId: string; localPath: string; size: number; modifiedAt: string }nonePushes one debounced change event for a watched SFTP temp file owned by the renderer webContents
app:get-database-security-infoinvokenonePromise<{ runtimeMode: 'development' | 'production'; resolverMode: 'development-fixed-key' | 'safe-storage' | 'master-password-fallback'; safeStorageAvailable: boolean; databasePath: string; securityConfigPath: string; hasEncryptedDbMasterKey: boolean; hasMasterPasswordHash: boolean; hasMasterPasswordSalt: boolean; hasMasterPasswordEnv: boolean; fallbackReady: boolean }>Returns non-sensitive database encryption bootstrap diagnostics for Settings → Advanced
app:launch-working-directoryevent (main -> renderer)cwd: stringnonePushes context-launch working directory when a second instance is invoked
app:menu-actionevent (main -> renderer)action: 'open-about' | 'open-settings' | 'new-tab' | 'close-current-tab' | 'close-right-tabs' | 'show-tab-switcher'noneDispatches validated app-menu commands from the macOS system menu to renderer tab/state handlers
app:open-devtoolsinvokenonePromise<boolean>Opens devtools for the current main window when available
app:toggle-devtoolsinvokenonePromise<boolean>Toggles detached DevTools for the current main window (open when closed, close when open)
app:reload-webviewinvokenonePromise<boolean>Reloads the active renderer webContents and bypasses cache for deterministic debug refresh
app:restart-backend-runtimeinvokenonePromise<boolean>Restarts backend runtime in-place during development without full app restart
app:show-in-file-managerinvoketargetPath?: stringPromise<boolean>Opens file/folder in OS file manager
app:open-external-urlinvoketargetUrl: stringPromise<boolean>Opens trusted HTTP(S) URL with system default browser
app:set-windows-system-menu-symbol-colorinvokesymbolColor: stringPromise<boolean>Applies token-driven Windows title bar system-menu symbol color to current main window overlay
app:show-save-file-dialoginvokedefaultPath?: stringPromise<{ canceled: boolean; filePath?: string }>Opens a native save dialog and returns the selected local file path when accepted
app:import-private-keyinvokenonePromise<{ canceled: boolean; content?: string }>Opens native file picker and returns UTF-8 private key content when selected
app:get-process-performance-statsinvokenonePromise<{ sampledAt: number; cpuPercent: number | null; mainProcessMemory: { rssBytes: number; heapTotalBytes: number; heapUsedBytes: number; externalBytes: number; arrayBuffersBytes: number }; rendererProcessMemory: { residentSetBytes: number; privateBytes: number; sharedBytes: number } | null; backendProcess: { pid: number; cpuPercent: number | null; memoryRssBytes: number | null } | null }>Samples main process CPU + memory, resolves renderer process memory from active window, and includes backend child-process CPU/RSS memory for debug monitoring overlay
app:export-main-heap-snapshotinvokenonePromise<{ ok: boolean; filePath?: string; message?: string }>Writes a V8 heap snapshot for the main process into app user-data debug snapshot directory
backend:test-pinginvokenonePromise<ApiTestPingResponse | ApiErrorResponse>Calls backend health test endpoint
backend:settings-getinvokenonePromise<ApiSettingsGetResponse | ApiErrorResponse>GET persisted application settings
backend:settings-updateinvokepayload: ApiSettingsUpdateRequestPromise<ApiSettingsUpdateResponse | ApiErrorResponse>PUT application settings snapshot
backend:audit-list-eventsinvokequery?: ApiAuditEventListQueryPromise<ApiAuditEventListResponse | ApiErrorResponse>GET audit event list with filter + pagination
backend:audit-get-event-by-idinvokeeventId: stringPromise<ApiAuditEventDetailResponse | ApiErrorResponse>GET single audit event detail
backend:ssh-list-serversinvokenonePromise<ApiSshListServersResponse | ApiErrorResponse>GET SSH server list
backend:ssh-create-serverinvokepayload: ApiSshCreateServerRequestPromise<ApiSshCreateServerResponse | ApiErrorResponse>POST create SSH server
backend:ssh-update-serverinvokeserverId: string, payload: ApiSshUpdateServerRequestPromise<ApiSshUpdateServerResponse | ApiErrorResponse>PUT update SSH server
backend:ssh-get-server-credentialsinvokeserverId: stringPromise<ApiSshGetServerCredentialsResponse | ApiErrorResponse>GET decrypted credentials
backend:ssh-list-foldersinvokenonePromise<ApiSshListFoldersResponse | ApiErrorResponse>GET folder list
backend:ssh-create-folderinvokepayload: ApiSshCreateFolderRequestPromise<ApiSshCreateFolderResponse | ApiErrorResponse>POST create folder
backend:ssh-update-folderinvokefolderId: string, payload: ApiSshUpdateFolderRequestPromise<ApiSshUpdateFolderResponse | ApiErrorResponse>PUT update folder
backend:ssh-list-tagsinvokenonePromise<ApiSshListTagsResponse | ApiErrorResponse>GET tag list
backend:ssh-create-taginvokepayload: ApiSshCreateTagRequestPromise<ApiSshCreateTagResponse | ApiErrorResponse>POST create tag
backend:ssh-list-keychainsinvokenonePromise<ApiSshListKeychainsResponse | ApiErrorResponse>GET keychain list
backend:ssh-create-keychaininvokepayload: ApiSshCreateKeychainRequestPromise<ApiSshCreateKeychainResponse | ApiErrorResponse>POST create keychain
backend:ssh-update-keychaininvokekeychainId: string, payload: ApiSshUpdateKeychainRequestPromise<ApiSshUpdateKeychainResponse | ApiErrorResponse>PUT update keychain
backend:ssh-get-keychain-credentialsinvokekeychainId: stringPromise<ApiSshGetKeychainCredentialsResponse | ApiErrorResponse>GET decrypted keychain credentials
backend:ssh-create-sessioninvokepayload: ApiSshCreateSessionRequestPromise<ApiSshCreateSessionResponse | ApiSshCreateSessionHostVerificationRequiredResponse | ApiErrorResponse>POST create SSH shell session
backend:ssh-trust-fingerprintinvokepayload: ApiSshTrustFingerprintRequestPromise<ApiSshTrustFingerprintResponse | ApiErrorResponse>POST trust host fingerprint
backend:ssh-close-sessioninvokesessionId: stringPromise<{ success: boolean }>DELETE SSH session
backend:ssh-delete-serverinvokeserverId: stringPromise<{ success: boolean }>DELETE SSH server
backend:ssh-delete-folderinvokefolderId: stringPromise<{ success: boolean }>DELETE SSH folder
backend:ssh-delete-keychaininvokekeychainId: stringPromise<{ success: boolean }>DELETE SSH keychain
backend:port-forward-list-rulesinvokenonePromise<ApiPortForwardListRulesResponse | ApiErrorResponse>GET persisted SSH port-forwarding rules and merge in-memory runtime status
backend:port-forward-create-ruleinvokepayload: ApiPortForwardCreateRuleRequestPromise<ApiPortForwardCreateRuleResponse | ApiErrorResponse>POST create a stopped port-forwarding rule
backend:port-forward-update-ruleinvokeruleId: string, payload: ApiPortForwardUpdateRuleRequestPromise<ApiPortForwardUpdateRuleResponse | ApiErrorResponse>PUT update a stopped port-forwarding rule
backend:port-forward-start-ruleinvokeruleId: stringPromise<ApiPortForwardStartRuleResponse | ApiErrorResponse>POST start one rule; may return shared SSH_HOST_UNTRUSTED payload for fingerprint trust retry
backend:port-forward-stop-ruleinvokeruleId: stringPromise<ApiPortForwardStopRuleResponse | ApiErrorResponse>POST stop one active rule; stopped rules are handled idempotently by backend
backend:port-forward-delete-ruleinvokeruleId: stringPromise<{ success: boolean }>DELETE one stopped port-forwarding rule
backend:sftp-create-sessioninvokepayload: ApiSftpCreateSessionRequestPromise<ApiSftpCreateSessionResponse | ApiSftpCreateSessionHostVerificationRequiredResponse | ApiErrorResponse>POST create SFTP file-system session
backend:sftp-list-directoryinvokesessionId: string, query?: ApiSftpListDirectoryQueryPromise<ApiSftpListDirectoryResponse | ApiErrorResponse>GET one SFTP directory listing
backend:sftp-get-entry-detailsinvokesessionId: string, payload: ApiSftpEntryDetailsRequestPromise<ApiSftpEntryDetailsResponse | ApiErrorResponse>POST fetch non-recursive metadata for selected SFTP entries
backend:sftp-read-fileinvokesessionId: string, query: ApiSftpReadFileQueryPromise<ApiSftpReadFileResponse | ApiErrorResponse>GET bounded UTF-8 file preview from one SFTP session
backend:sftp-download-fileinvokesessionId: string, payload: ApiSftpDownloadFileRequestPromise<ApiSftpDownloadFileResponse | ApiErrorResponse>POST stream one regular remote SFTP file into a local path selected by app utility IPC
backend:sftp-upload-fileinvokesessionId: string, payload: ApiSftpUploadFileRequestPromise<ApiSftpUploadFileResponse | ApiErrorResponse>POST stream one locally edited SFTP temp file back to the remote file after remote size/mtime conflict checks; overwrite: true is only sent after renderer conflict confirmation and remote conflicts return SFTP_UPLOAD_CONFLICT
backend:sftp-create-directoryinvokesessionId: string, payload: ApiSftpCreateDirectoryRequestPromise<ApiSftpCreateDirectoryResponse | ApiErrorResponse>POST create remote SFTP directory
backend:sftp-create-fileinvokesessionId: string, payload: ApiSftpCreateFileRequestPromise<ApiSftpCreateFileResponse | ApiErrorResponse>POST create empty remote SFTP file
backend:sftp-rename-entryinvokesessionId: string, payload: ApiSftpRenameRequestPromise<ApiSftpRenameResponse | ApiErrorResponse>POST rename or move remote SFTP entry
backend:sftp-copy-entryinvokesessionId: string, payload: ApiSftpCopyRequestPromise<ApiSftpCopyResponse | ApiErrorResponse>POST copy remote SFTP file or directory tree
backend:sftp-delete-entryinvokesessionId: string, payload: ApiSftpDeleteRequestPromise<ApiSftpDeleteResponse | ApiErrorResponse>POST delete remote SFTP file, symlink, or directory tree
backend:sftp-batch-operationinvokesessionId: string, payload: ApiSftpBatchOperationRequestPromise<ApiSftpBatchOperationResponse | ApiErrorResponse>POST ordered batch copy, move, or delete across SFTP entries
backend:sftp-close-sessioninvokesessionId: stringPromise<{ success: boolean }>DELETE SFTP session
backend:local-terminal-list-profilesinvokenonePromise<ApiLocalTerminalListProfilesResponse | ApiErrorResponse>GET local terminal profile list
backend:local-terminal-create-sessioninvokepayload: ApiLocalTerminalCreateSessionRequestPromise<ApiLocalTerminalCreateSessionResponse | ApiErrorResponse>POST local terminal session (Main may inject one-shot cwd from launch context)
backend:local-terminal-close-sessioninvokesessionId: stringPromise<{ success: boolean }>DELETE local terminal session

3. Schema Sources

  • API payload types come from @cosmosh/api-contract, generated from packages/api-contract/openapi/cosmosh.openapi.yaml.
  • Backend, Main IPC proxy, and renderer HTTP callers must use API_PATHS and related generated contract exports from @cosmosh/api-contract instead of hard-coded route strings.
  • IPC-only payloads that are not generated from OpenAPI, including AppMenuAction, SftpOpenWithApplication, and SftpTemporaryFileWatchChange, are defined in packages/api-contract/src/ipc.ts and consumed by main, preload, and renderer type declarations.

3.1 SSH Visual Metadata Fields

The following SSH entity payloads now include visual metadata for persistent icon/color customization:

  • ApiSshCreateServerRequest / ApiSshUpdateServerRequest: optional iconKey, optional colorKey.
  • ApiSshCreateFolderRequest / ApiSshUpdateFolderRequest: optional iconKey, optional colorKey.
  • ApiSshListServersResponse: each server item includes iconKey and colorKey.
  • ApiSshListFoldersResponse: each folder item includes iconKey and colorKey.

colorKey is constrained to the predefined palette enum in the API contract.

SSH security policy fields in current contract:

  • ApiSshCreateServerRequest / ApiSshUpdateServerRequest: strictHostKey, enableSshCompression, and renderer-only disableCharacterWidthCompatibilityMode booleans.
  • ApiSshListServersResponse: each server item includes persisted strictHostKey, enableSshCompression, and disableCharacterWidthCompatibilityMode.
  • ApiSshCreateSessionRequest: optional strictHostKey and enableSshCompression overrides used for one session attempt.
  • Character width compatibility is not sent to SSH session creation or terminal WS messages; renderer applies it when creating xterm instances.

3.2 SSH Port Forwarding Contract

Port forwarding payloads are generated from the OpenAPI source and consumed by backend, main, preload, and renderer wrappers:

  • ApiPortForwardListRulesResponse
  • ApiPortForwardCreateRuleRequest / ApiPortForwardCreateRuleResponse
  • ApiPortForwardUpdateRuleRequest / ApiPortForwardUpdateRuleResponse
  • ApiPortForwardStartRuleResponse
  • ApiPortForwardStopRuleResponse

Rule type is local, remote, or dynamic.

Type-specific fields:

  • Local: localBindHost, localBindPort, targetHost, targetPort
  • Remote: remoteBindHost, remoteBindPort, targetHost, targetPort
  • Dynamic: localBindHost, localBindPort

Runtime status is returned as runtime.status and is not persisted. Start can return SSH_HOST_UNTRUSTED; renderer must trust the fingerprint through backend:ssh-trust-fingerprint before retrying.

3.3 Terminal WebSocket Contract (Renderer ↔ Backend)

Although terminal stream messages are not Electron IPC channels, they are part of the same cross-process contract surface and must be versioned together.

  • Client to server (/ws/ssh/{sessionId} and /ws/local-terminal/{sessionId}):
    • input, resize, ping, close, history-delete
    • completion-request with requestId, linePrefix, cursorIndex, optional workingDirectoryHint, optional limit, optional fuzzyMatch, optional source filters (includeHistory, includeBuiltInCommands, includePathSuggestions, includePasswordSuggestions), and trigger (typing or manual)
  • Server to client:
    • ready, output, telemetry, history, pong, error, exit
    • completion-response with requestId, replacePrefixLength, and ranked completion items

Completion item contract notes:

  • items[].source includes history, inshellisense, and runtime-computed runtime.
  • items[].kind includes existing command-spec/history categories plus runtime categories (path, secret).
  • Runtime categories are used for path candidates and interactive secret-fill actions while preserving the same completion-response envelope.

Current implementation note:

  • Completion messages are handled in SshSessionService and LocalTerminalSessionService via shared normalization in terminal/shared.ts and shared ranking engine in terminal/completion/engine.ts.

4. Change Rules

When adding/modifying a channel, update in one commit:

  1. packages/main/src/preload.ts
  2. packages/main/src/index.ts
  3. packages/renderer/src/vite-env.d.ts
  4. relevant renderer transport/service wrappers
  5. this file (docs/developer/core/ipc-protocol.md)

5. Channel Addition Template

Use this checklist when introducing a new channel:

  1. Channel name: domain:action-name
  2. IPC type: invoke or send
  3. Params schema: explicit type in bridge and renderer declarations
  4. Return schema: success and error shape
  5. Main behavior: backend proxy or privileged local action
  6. Security notes: token/header handling, permission boundary, exposure limits
  7. Docs sync: update EN + ZH protocol pages in same change set