Main Process (index.ts)
Window management, IPC handlers, auth lifecycle, and deep linking
packages/desktop/src/main/index.ts is the Electron main-process entry point. It owns the OS window, the IPC bus, and coordinates all managers.
Window Creation
createWindow() creates the application BrowserWindow:
- macOS — native titlebar with traffic-light buttons;
titleBarStyle: 'hiddenInset'. - Windows / Linux — frameless window; the renderer draws custom titlebar controls.
- Minimum size: 1024 × 700 px.
- Loads the renderer via a local HTTP server (needed for Clerk cookie support —
file://URLs cannot hold cookies).
Local HTTP Server
startLocalServer(staticDir) binds to a random available port and serves the renderer's built static files. The BrowserWindow navigates to http://localhost:{port}.
Auth Lifecycle
Auth state is persisted to userData/auth.json as a JSON object with fields:
| Field | Type | Description |
|---|---|---|
token | string | Cosmo JWT |
userId | string | Internal user ID |
orgId | string | Active org ID |
userEmail | string | User email |
userName | string | Display name |
orgName | string | Org display name |
orgSlug | string | URL-safe org slug |
role | string | OWNER / ADMIN / MEMBER |
plan | string? | Active billing plan |
onboardingCompleted | boolean? | Whether onboarding is done |
validateStoredAuth(auth) makes a live call to GET /api/v1/auth/me. If the server returns non-OK, tryRefreshToken is called. On network error (offline), auth is trusted.
tryRefreshToken(auth) calls POST /api/v1/auth/refresh to obtain a new token.
wipeAppData() clears auth state, the chat-sessions file, the cosmo-data directory, and browser session/cache. It does not call app.relaunch() or app.exit() — the renderer receives an auth:changed: null event and navigates to the login screen.
Deep Linking
The app registers as a handler for the cosmo:// custom URL scheme. handleDeepLink(url) parses incoming cosmo:// URLs:
| URL | Action |
|---|---|
cosmo://auth?token=... | Stores the token from web auth flow |
After a successful deep link, auth:changed is emitted to the renderer so AuthContext can update immediately.
IPC Handlers
All IPC handlers are registered in main/index.ts using ipcMain.handle. The renderer calls them via the preload bridge.
Window Controls
| Channel | Handler |
|---|---|
window:minimize | Minimise the window |
window:maximize | Toggle maximise |
window:close | Close the window |
window:is-maximized | Returns boolean |
Auth
| Channel | Handler |
|---|---|
auth:get | Returns stored AuthState or null |
auth:login | Opens browser to web sign-in page |
auth:logout | Calls wipeAppData() |
Chat
| Channel | Handler |
|---|---|
chat:list-sessions | Returns sessions from ChatManager |
chat:create-session | Creates a new session |
chat:get-messages | Returns messages for a session |
chat:delete-session | Deletes a session |
chat:send-message | Streams a response (events via chat:event) |
chat:stop | Aborts in-flight generation |
chat:available-models | Returns model list from server |
chat:confirm-tool | Resolves a pending tool confirmation |
chat:extract-onboarding | Extracts memory from onboarding session |
Onboarding
| Channel | Handler |
|---|---|
onboarding:get-user-profile | Returns stored user profile |
onboarding:save-user-profile | Saves user profile + cloud push |
onboarding:get-org-status | Returns org onboarding data |
onboarding:save-org | Saves org data + cloud push |
onboarding:reset | Wipes all onboarding data |
onboarding:save-asset | Stores brand asset file |
onboarding:resolve-asset-url | Resolves asset:// ref to file:// URL |
onboarding:seed-onboarding | Calls POST /api/v1/auth/seed-onboarding |
onboarding:get-sample-tasks | Calls GET /api/v1/tasks to return org tasks |
Connectors
| Channel | Handler |
|---|---|
connectors:available | Fetches provider catalog from server |
connectors:list | Lists installed connector instances |
connectors:add | Starts OAuth or API-key flow, saves instance |
connectors:remove | Deletes a connector instance |
Updates
| Channel | Handler |
|---|---|
updates:get-status | Returns current UpdateStatus |
updates:check | Triggers an immediate update check |
updates:download | Starts downloading an available update |
updates:install | Quits and installs the downloaded update |
Tasks, Workspaces, Teams, Artifacts
Thin wrappers that forward calls to SyncClient:
| Channel | Endpoint |
|---|---|
workspaces:list | GET /api/v1/workspaces |
workspaces:get | GET /api/v1/workspaces/:id |
teams:list | GET /api/v1/teams |
teams:get | GET /api/v1/teams/:id |
teams:list-members | GET /api/v1/teams/:id/members |
artifacts:list | ArtifactManager.list() |
artifacts:get | ArtifactManager.read() |
Billing
| Channel | Handler |
|---|---|
billing:open-manage | Opens web billing portal in browser |
billing:get-usage | GET /api/v1/metering/user |
billing:get-subscription | GET /api/v1/billing/subscription |
billing:open-portal | POST /api/v1/billing/portal → opens URL |
Utility
| Channel | Handler |
|---|---|
get-app-version | Returns app.getVersion() |
open-external | Opens URL in system browser |
open-web-page | Opens app web URL in browser |
platformis not an IPC channel — it is exposed aswindow.cosmoDesktop.platform(a directprocess.platformproperty on the preload object).
Events Emitted to Renderer
The main process pushes events to the renderer window using webContents.send:
| Channel | Payload | Trigger |
|---|---|---|
auth:changed | StoredAuth | null | Deep link login / logout |
chat:event | ChatEvent | Each streaming chunk |
tasks:changed | orgId: string | Task created/updated/deleted |
teams:changed | orgId: string | Team membership changed |
workspaces:changed | orgId: string | Workspace created/deleted |
artifacts:changed | orgId, workspaceId | Artifact updated |
artifacts:created | orgId, workspaceId, artifactId, title | New artifact created by AI |
updates:status | UpdateStatus | Update state change |
sync:remote-change | ChangeEvent | SSE change from cloud pushed to renderer |
posthog:capture-feedback | FeedbackCaptureData | AI feedback submission |
window:maximized-changed | boolean | Window resize |