# WebChat

URL: https://cogitator.app/docs/channels/webchat

> Built-in WebSocket channel for custom web interfaces and CLI tools.

## Setup

```bash
pnpm add ws
```

## Usage

```typescript
import { webchatChannel } from '@cogitator-ai/channels';

const webchat = webchatChannel({
  port: 3100,
  path: '/ws',
  auth: (token) => token === process.env.WEBCHAT_TOKEN,
});
```

## Use with `cogitator up`

Add a `webchat` entry to `cogitator.yml` and set `WEBCHAT_TOKEN` in the `.env` next to it:

```yaml title="cogitator.yml"
channels:
  webchat:
    port: 8080 # default
    path: /ws # default
```

```bash title=".env"
WEBCHAT_TOKEN=a-long-random-secret
```

Clients connect to `ws://localhost:8080/ws?token=<WEBCHAT_TOKEN>`. Without `WEBCHAT_TOKEN` the channel is not started (a warning is logged), because an open socket would give anyone the assistant's tools. WebChat has no `ownerIds`: every connection is a separate anonymous user. `cogitator wizard --edit` keeps the entry. See the [config reference](/docs/channels/wizard#config-reference).

## Configuration

```typescript
interface WebChatConfig {
  port: number;
  path?: string; // default: '/ws'
  auth?: (token: string) => boolean;
  maxPayload?: number; // max incoming frame size in bytes, default 1 MiB
}
```

## Connecting

WebSocket URL: `ws://localhost:3100/ws?token=YOUR_SECRET`

### With wscat

```bash
npx wscat -c "ws://localhost:3100/ws?token=secret"
> {"text": "Hello!"}
```

### From JavaScript

```javascript
const ws = new WebSocket('ws://localhost:3100/ws?token=secret');
ws.onmessage = (e) => {
  const msg = JSON.parse(e.data);
  if (msg.type === 'message') console.log('Bot:', msg.text);
  if (msg.type === 'edit') console.log('Bot (updated):', msg.text);
};
ws.onopen = () => ws.send(JSON.stringify({ text: 'Hi!' }));
```

## Protocol

### Client → Server

```json
{ "text": "Hello!", "id": "optional-id" }
```

### Server → Client

| Type        | Fields                                           | Description                                                  |
| ----------- | ------------------------------------------------ | ------------------------------------------------------------ |
| `connected` | `clientId`                                       | Connection established                                       |
| `message`   | `id`, `text`, `replyTo?`                         | New message                                                  |
| `edit`      | `id`, `text`                                     | Updated message (streaming)                                  |
| `delete`    | `id`                                             | Message removed (e.g. aborted stream)                        |
| `typing`    | —                                                | Agent is thinking                                            |
| `file`      | `filename`, `mimeType`, `url?`, `data?` (base64) | File attachment                                              |
| `error`     | `message`                                        | Sent before closing an unauthorized socket (close code 1008) |

Frames that are not JSON or have no non-empty `text` string are ignored.

## Features

| Feature              | Support                   |
| -------------------- | ------------------------- |
| Streaming (edit)     | ✅                         |
| Typing indicator     | ✅                         |
| Token auth           | ✅                         |
| File metadata        | ✅                         |
| Message length limit | None                      |
| Multiple clients     | ✅ (each gets own session) |

## Tips

* Perfect for building custom chat UIs
* Auth is optional but recommended for production
* Each WebSocket connection is treated as a separate user: `channelId` and `userId` are the random `clientId`, so a reconnect starts a new thread
* Incoming messages carry text only — attachments are not supported from the client
* `start()` rejects if the port is already in use; `stop()` closes all client connections (code 1001)
* Use with reverse proxy (nginx) for TLS in production
