---
title: Support Chat
---
# Support Chat

## What it is

In-app chat for Omniscio users to contact support directly from the toolbar.

## Where to find it

The chat icon in the toolbar opens a floating panel at the bottom-right. Operator mode is a setting rather than a separate screen.

## How it behaves

### How It Works

- **User mode** (default): A chat icon in the toolbar opens a floating panel (bottom-right). Users enter a display name on first use, then send messages that reach the operator's inbox.
- **Operator mode** (`supportChatOperator: true` in settings): The operator sees all conversations as inbox items and can reply from the same floating panel or the inbox.

### Scope and limits

- **Reachable from the localhost CLI — mark-read is the one exception.** The control server (`127.0.0.1:19519`) reaches Support Chat: `GET /support-chat/install-id` (this install's own conversation key), `GET /support-chat/messages?conversationId=` (a conversation's messages — a non-operator may read only its own and gets `403` otherwise; an operator reads any), `GET /support-chat/conversations` (operator only) and `POST /support-chat/send` (applies immediately; a non-operator send is pinned to its own conversation). The family is gated on `supportChatEnabled` and answers `403` when Support Chat is switched off. The one command that stays in-app is `SUPPORT_CHAT_MARK_READ` — it clears a conversation's unread cursor and is a deliberate `feature-scoped-surface` exemption in the CLI-parity manifest (it is Support-Chat-specific, not the generic inbox mark-read route).

### Key Files

- `src/shared/support-chat-types.ts` — shared types
- `src/main/services/support-chat-service.ts` — user path (`supportChat` relay: send / mark-read / poll) + operator path (`operatorConsole` relay: list / getMessages / mark-read / send, plus a ~15s conversation poll); NO admin SDK; `sendMessage` / `markRead` / `getMessages` split by `sender` / `reader`
- `src/main/services/support-chat/support-chat-relay-client.ts` — USER relay client (`relaySendMessage` / `relayMarkRead` / `relayPoll`)
- `src/main/services/operator-console/operator-console-client.ts` — OPERATOR relay client (`callOperatorConsoleFn`, admin-authed)
- `firebase/functions/src/support-chat-relay.ts` — the `supportChat` relay Cloud Function (`send` / `markRead` / `poll`)
- `firebase/functions/src/operator-console.ts` — the `operatorConsole` Cloud Function (broadcast + support-chat operator actions); `firebase/functions/src/operator-console-support-chat-validate.ts` — support-chat action validators
- `src/main/ipc/support-chat-handlers.ts` — IPC handlers
- `src/renderer/src/stores/support-chat-store.ts` — Zustand store
- `src/renderer/src/features/support-chat/SupportChatPanel.tsx` — floating panel UI

## For agents

### Architecture

- **Backend**: Firebase Firestore (the shares project), `support_conversations/{installId}/messages/{auto-id}` layout.
- **Keying**: Each install gets a stable `installId` (auto-generated, persisted in config.json). No Firebase Auth required.
- **User path — no shipped admin key**: a user's send, mark-read, AND message reads all go through the `supportChat` relay Cloud Function (ship-in-app deterrent token; the Firestore admin credential lives server-side). A packaged build's firebase-admin `onSnapshot` fails `7 PERMISSION_DENIED` (the share-mirror packaged-admin bug), so the user READ path polls the relay's `poll` op about every 15s (`relayPoll`) instead of a real-time listener; the panel also refreshes instantly on open. Delivery + notifications are unchanged (same `SUPPORT_CHAT_MESSAGE_RECEIVED` push, notify only on a genuinely new reply). Locked by the user-write-relay contract.
- **Operator path — no shipped admin key**: the operator's list / read / reply / mark-read run server-side through the admin-authed `operatorConsole` relay Cloud Function — gated by the operator's VERIFIED Firebase ID token + an admin role (`authenticate` + `requireAdmin`), strictly stronger than the old client-side check. The live console view POLLS the relay's `supportChat.listConversations` action about every 15s (replacing the old `onSnapshot` over the whole `support_conversations` collection). Delivery + notifications are unchanged (same `SUPPORT_CHAT_CONVERSATIONS_CHANGED` push; one notify per genuinely-new unread reply). Locked by the operator-console-relay contract.
- **Inbox source**: Operator-only. Unread conversations appear as `support-chat` inbox items.

### Settings

| Setting               | Type    | Default | Description                                                 |
| --------------------- | ------- | ------- | ----------------------------------------------------------- |
| `supportChatEnabled`  | boolean | `true`  | Master toggle — hides toolbar icon and panel when off       |
| `supportChatOperator` | boolean | `false` | Operator mode — see all conversations in inbox              |
| `supportChatUserName` | string  | `''`    | Display name shown on messages (required before first send) |

### IPC Channels

| Channel                              | Direction             | Purpose                                |
| ------------------------------------ | --------------------- | -------------------------------------- |
| `SUPPORT_CHAT_LIST_CONVERSATIONS`    | request-response      | List all conversations (operator only) |
| `SUPPORT_CHAT_GET_MESSAGES`          | request-response      | Get messages for a conversation        |
| `SUPPORT_CHAT_SEND`                  | request-response      | Send a message                         |
| `SUPPORT_CHAT_GET_INSTALL_ID`        | request-response      | Get this install's unique ID           |
| `SUPPORT_CHAT_MARK_READ`             | request-response      | Mark conversation as read              |
| `SUPPORT_CHAT_MESSAGE_RECEIVED`      | push (Main->Renderer) | New message notification               |
| `SUPPORT_CHAT_CONVERSATIONS_CHANGED` | push (Main->Renderer) | Conversation list changed              |

## Related

Operator mode routes conversations into the Omniscio Inbox, so the inbox is where the operator side of this lives. Filing a bug or feature request is a different route into the same team.
