<!-- SPDX-License-Identifier: MIT -->
<!-- SPDX-FileCopyrightText: 2025-2026 Marcus Quinn -->

# t1327.1: SimpleX Chat Bot API — Research Report

**Date:** 2026-02-25
**Task:** Research and foundation for SimpleX Chat agent integration
**Ref:** GH#2246
**Sources:** simplex-chat/simplex-chat GitHub repo (bots/, packages/, docs/), npm registry, existing aidevops patterns

---

## Executive Summary

SimpleX Chat exposes a local WebSocket API via its CLI (`simplex-chat -p 5225`). Bots connect to this WebSocket, send JSON command strings, and receive JSON event/response messages. The TypeScript SDK (`simplex-chat` npm package v0.3.0) wraps this with a `ChatClient` class and typed command builders. The API is mature (auto-generated docs, 11 npm releases), well-suited for Bun/TypeScript bots, and follows the same pattern as our existing `matrix-bot.md` integration. Key design decisions for t1327.3–t1327.4 are documented below.

---

## 1. CLI Installation

### 1.1 Install Method

```bash
# Linux / macOS (recommended)
curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/stable/install.sh | bash

# Or wget
wget -qO- https://raw.githubusercontent.com/simplex-chat/simplex-chat/stable/install.sh | bash

# Result: simplex-chat binary on PATH
```

### 1.2 Running as WebSocket Server

```bash
# Start CLI as WebSocket server on port 5225 (default for bots)
simplex-chat -p 5225

# With custom database path (recommended for bots)
simplex-chat -p 5225 -d ~/.config/simplex-bot/db

# Create bot profile on first run
simplex-chat -p 5225 --create-bot-display-name "MyBot" --create-bot-allow-files
```

### 1.3 Bot Profile Setup (One-Time)

Bot profiles can be set up manually via desktop app or CLI, then the database reused by the bot process. This is the recommended approach — avoids programmatic profile creation complexity.

```bash
# Create bot profile interactively
simplex-chat -d ~/.config/simplex-bot/db
# In CLI: /create bot MyBot
# In CLI: /_address 1   (creates address)
# In CLI: /_address_settings 1 {"businessAddress":false,"autoAccept":{"acceptIncognito":false}}
```

### 1.4 Cloud Deployment

```bash
# Dedicated user + tmux (from BUSINESS.md)
useradd -m -s /bin/bash simplex-cli
tmux new -s simplex-cli
su - simplex-cli
curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/stable/install.sh | bash
simplex-chat -p 5225 -d ~/.config/simplex-bot/db
# Ctrl+B D to detach
```

**Security note:** WebSocket API has no auth. CLI binds to localhost only. For remote bots, use Caddy/Nginx TLS proxy + HTTP basic auth.

---

## 2. WebSocket Protocol

### 2.1 Command Format

```json
{
  "corrId": "unique-string-or-number",
  "cmd": "/_send @42 json [{\"msgContent\":{\"type\":\"text\",\"text\":\"Hello\"}}]"
}
```

### 2.2 Response Format (correlated)

```json
{
  "corrId": "same-as-sent",
  "resp": {
    "type": "newChatItems",
    "user": {...},
    "chatItems": [...]
  }
}
```

### 2.3 Event Format (uncorrelated)

```json
{
  "resp": {
    "type": "newChatItems",
    "user": {...},
    "chatItems": [...]
  }
}
```

**Critical:** Events and responses share the `resp` field. Distinguish by presence of `corrId`. Bots MUST ignore unknown event types (forward-compatibility requirement).

---

## 3. Bot API Commands (COMMANDS.md)

### 3.1 Command Categories

| Category | Key Commands | Use Case |
|----------|-------------|----------|
| Address | `APICreateMyAddress`, `APIShowMyAddress`, `APISetAddressSettings` | Bot address lifecycle |
| Message | `APISendMessages`, `APIUpdateChatItem`, `APIDeleteChatItem`, `APIChatItemReaction` | Core messaging |
| File | `ReceiveFile`, `CancelFile` | File handling |
| Group | `APINewGroup`, `APIAddMember`, `APIRemoveMembers`, `APIMembersRole`, `APIBlockMembersForAll` | Group management |
| Group Link | `APICreateGroupLink`, `APIGetGroupLink`, `APIDeleteGroupLink` | Public group links |
| Connection | `APIAddContact`, `APIConnect`, `APIAcceptContact`, `APIRejectContact` | Contact management |
| Chat | `APIListContacts`, `APIListGroups`, `APIDeleteChat` | Chat listing |
| User Profile | `ShowActiveUser`, `APIUpdateProfile`, `APISetContactPrefs` | Profile/bot config |

### 3.2 Key Command Syntax Patterns

```bash
# Send message (most common)
/_send @<contactId> json [{"msgContent":{"type":"text","text":"Hello"},"mentions":{}}]
/_send #<groupId> json [{"msgContent":{"type":"text","text":"Hello"},"mentions":{}}]

# Get/create address
/_show_address <userId>
/_address <userId>

# Set address auto-accept
/_address_settings <userId> {"businessAddress":false,"autoAccept":{"acceptIncognito":false}}

# List contacts/groups
/_contacts <userId>
/_groups <userId>

# Accept contact request
/_accept <contactReqId>

# Update profile (set bot type + commands)
/_profile <userId> {"displayName":"MyBot","fullName":"","peerType":"bot","preferences":{"commands":[...]}}
```

### 3.3 ChatRef Syntax

```
@<contactId>    → direct chat
#<groupId>      → group chat
*<noteFolderId> → local notes
```

### 3.4 Network Usage Classification

- `no` — instant, no network (list, get)
- `interactive` — waits for network before responding (connect, create address)
- `background` — responds immediately, network happens async (send message, delete)

---

## 4. Bot API Events (EVENTS.md)

### 4.1 Essential Events for Bots

| Event | Type Tag | When | Action |
|-------|----------|------|--------|
| Contact connected | `contactConnected` | User connects via address | Send welcome, store contactId |
| Business request | `acceptingBusinessRequest` | User connects via business address | New business chat created |
| New message | `newChatItems` | Message received | Parse and respond |
| Contact request | `receivedContactRequest` | Auto-accept off | Call `/_accept <id>` |
| File ready | `rcvFileDescrReady` | File incoming | Call `/freceive <fileId>` |
| File complete | `rcvFileComplete` | File downloaded | Process file |
| Group invitation | `receivedGroupInvitation` | Bot invited to group | Call `/_join #<groupId>` |
| Member joined | `joinedGroupMember` | New group member | Optional: welcome |
| Member removed | `deletedMemberUser` | Bot removed from group | Cleanup |

### 4.2 Message Event Structure

```typescript
// newChatItems event
{
  type: "newChatItems",
  user: User,
  chatItems: AChatItem[]  // array of {chatInfo, chatItem}
}

// AChatItem
{
  chatInfo: ChatInfo,  // {type: "direct", contact} or {type: "group", groupInfo}
  chatItem: ChatItem   // {chatDir, meta, content, file?, ...}
}

// Extract text from chatItem
chatItem.content.type === "rcvMsgContent"
  ? chatItem.content.msgContent.text  // for text messages
  : null
```

### 4.3 Error Events (Log, Don't Fail)

- `messageError` — network/delivery errors (common, not failures)
- `chatError` — command errors
- `chatErrors` — multiple errors

---

## 5. API Types (TYPES.md) — Key Types for Bot Development

### 5.1 Core Types

```typescript
// User
{ userId: int64, localDisplayName: string, profile: LocalProfile, ... }

// Contact
{ contactId: int64, localDisplayName: string, profile: LocalProfile, ... }

// GroupInfo
{ groupId: int64, localDisplayName: string, groupProfile: GroupProfile, ... }

// MsgContent (discriminated union)
{ type: "text", text: string }
{ type: "image", text: string, image: string }  // base64 image
{ type: "file", text: string }
{ type: "voice", text: string, duration: int }
{ type: "link", text: string, preview: LinkPreview }

// ComposedMessage (for APISendMessages)
{
  fileSource?: CryptoFile,
  quotedItemId?: number,
  msgContent: MsgContent,
  mentions: Record<string, number>  // displayName → groupMemberId
}
```

> **Type note:** The Haskell source uses `Int64` for IDs and `Int` for small values like `duration`. In TypeScript/JSON these are represented as `number`. For IDs that may exceed `Number.MAX_SAFE_INTEGER` (2^53−1), serialize them as JSON strings and parse with `BigInt(idString)` — converting a JSON `number` with `BigInt(value)` is unsafe because precision is already lost at parse time. Small values such as `duration` can remain TypeScript `number`.

### 5.2 ChatBotCommand (for bot menus)

```typescript
// Command leaf
{ type: "command", keyword: string, label: string, params?: string }

// Menu node (nested)
{ type: "menu", label: string, commands: ChatBotCommand[] }
```

Configure via `/_profile <userId> {"preferences":{"commands":[...]}}` or `/set bot commands ...`

### 5.3 AddressSettings

```typescript
{
  businessAddress: bool,
  autoAccept?: { acceptIncognito: bool },
  autoReply?: MsgContent
}
```

### 5.4 GroupMemberRole

`observer | author | member | moderator | admin | owner`

### 5.5 ChatDeleteMode

`full | entity | messages` (with optional `notify=off`)

---

## 6. TypeScript SDK (`simplex-chat` npm package)

### 6.1 Package Info

| Property | Value |
|----------|-------|
| Package | `simplex-chat` v0.3.0 |
| Types | `@simplex-chat/types` v0.3.0 |
| License | AGPL-3.0 |
| Runtime dep | `isomorphic-ws` (WebSocket) |
| Bun compat | Yes — Bun has native WebSocket, `isomorphic-ws` works |

### 6.2 ChatClient API

```typescript
import {ChatClient} from "simplex-chat"

// Connect
const chat = await ChatClient.create("ws://localhost:5225")

// Get active user
const user = await chat.apiGetActiveUser()

// Get/create address
const address = await chat.apiGetUserAddress() || await chat.apiCreateUserAddress()

// Enable auto-accept
await chat.enableAddressAutoAccept(user.userId)

// Send text message
await chat.apiSendTextMessage("direct", contactId, "Hello!")

// Send to group
await chat.apiSendTextMessage("group", groupId, "Hello group!")

// Event loop
for await (const event of chat.msgQ) {
  switch (event.type) {
    case "contactConnected": ...
    case "newChatItems": ...
  }
}
```

### 6.3 Low-Level Command API

```typescript
// Raw command (for commands not wrapped by SDK)
const resp = await chat.sendChatCmd("/_show_address 1")
```

### 6.4 Bun vs Node Considerations

- Bun has native WebSocket — `isomorphic-ws` will use the native impl
- `better-sqlite3` (used in matrix-bot) works with Bun via `bun:sqlite` alternative
- For new bot framework: use `bun:sqlite` directly (no native module compilation)
- TypeScript compilation: Bun runs `.ts` natively — no `tsc` step needed

---

## 7. Business Address Pattern

Business addresses create a new group chat per connecting customer. Key differences from regular address:

| Feature | Regular Address | Business Address |
|---------|----------------|-----------------|
| Connection type | Direct chat | Group chat (hidden) |
| Customer sees | Bot profile | Bot profile |
| Bot sees | Customer profile | Customer profile |
| Add agents | No | Yes (add to group) |
| Scale | 1:1 | 1:many (support teams) |

```bash
# Enable business address
/_address_settings <userId> {"businessAddress":true,"autoAccept":{"acceptIncognito":false}}

# Event when customer connects
{ type: "acceptingBusinessRequest", user, groupInfo }

# Then treat groupInfo.groupId as the customer's chat
```

---

## 8. Pattern Analysis: Existing aidevops Helpers

### 8.1 matrix-bot.md Patterns to Reuse

| Pattern | matrix-bot | simplex-bot |
|---------|-----------|-------------|
| Config file | `~/.config/aidevops/matrix-bot.json` (600 perms) | `~/.config/aidevops/simplex-bot.json` (600 perms) |
| Data dir | `~/.aidevops/.agent-workspace/matrix-bot/` | `~/.aidevops/.agent-workspace/simplex-bot/` |
| Session DB | SQLite WAL, `sessions.db` | Same pattern |
| Daemon mode | PID file + `--daemon` flag | Same pattern |
| Subcommands | `setup\|start\|stop\|status\|test\|logs` | Same + `connect\|send\|group` |
| Runner dispatch | `runner-helper.sh` | Same |
| Concurrency | One dispatch per room | One dispatch per contact/group |

### 8.2 mail-helper.sh Patterns to Reuse

| Pattern | mail-helper | simplex-helper |
|---------|------------|----------------|
| SQLite WAL | `PRAGMA journal_mode=WAL` | Same |
| Busy timeout | `.timeout 5000` | Same |
| Agent ID | `get_agent_id()` from branch/worktree | Same |
| Message types | `task_dispatch\|status_report\|discovery\|request\|broadcast` | Same (transport adapter) |
| TOON output | `<!--TOON:inbox{...}:` | Same |
| Error handling | `log_error`, `return 1` | Same |

### 8.3 ip-reputation-helper.sh Patterns to Reuse

| Pattern | ip-reputation | simplex-helper |
|---------|--------------|----------------|
| Parallel providers | Background jobs + wait | Parallel WebSocket commands |
| Cache (SQLite) | Per-provider TTL | Per-contact session cache |
| `shared-constants.sh` | `source "${SCRIPT_DIR}/shared-constants.sh"` | Same |
| `local var="$1"` | All functions | All functions |
| Explicit `return 0/1` | All functions | All functions |
| `set -euo pipefail` | Yes | Yes |
| `readonly VERSION` | Yes | Yes |

---

## 9. Architecture Decisions for t1327.3–t1327.4

### 9.1 simplex-helper.sh Subcommands

```
simplex-helper.sh install          # Download simplex-chat binary
simplex-helper.sh init             # Interactive setup wizard
simplex-helper.sh bot-start        # Start bot daemon
simplex-helper.sh bot-stop         # Stop bot daemon
simplex-helper.sh bot-status       # Check bot status + logs
simplex-helper.sh send @<id> <msg> # Send message to contact
simplex-helper.sh send #<id> <msg> # Send message to group
simplex-helper.sh connect <link>   # Connect via SimpleX link
simplex-helper.sh group create     # Create group
simplex-helper.sh group list       # List groups
simplex-helper.sh status           # Show address, contacts, groups
simplex-helper.sh server           # SMP server management
```

### 9.2 Bot Framework Architecture (TypeScript/Bun)

```
simplex-bot/
├── index.ts          # Entry point, event loop
├── client.ts         # ChatClient wrapper (thin layer over SDK)
├── router.ts         # Command router (/slash commands)
├── handlers/
│   ├── contact.ts    # contactConnected, contactRequest
│   ├── message.ts    # newChatItems (main handler)
│   ├── group.ts      # group events
│   └── file.ts       # file events
├── session.ts        # SQLite session store (bun:sqlite)
├── config.ts         # Config loader (~/.config/aidevops/simplex-bot.json)
└── runner.ts         # runner-helper.sh dispatch bridge
```

### 9.3 Config File Schema

```json
{
  "wsPort": 5225,
  "wsHost": "localhost",
  "userId": 1,
  "botPrefix": "/",
  "allowedContacts": [],
  "defaultRunner": "",
  "contactMappings": {},
  "groupMappings": {},
  "businessAddress": false,
  "autoAccept": true,
  "sessionIdleTimeout": 300,
  "maxPromptLength": 4000,
  "responseTimeout": 600
}
```

### 9.4 Mailbox Transport Adapter (t1327.5)

Extend `mail-helper.sh` with a `--transport simplex` flag:

```bash
# New transport flag
mail-helper.sh send --to agent-b --type task_dispatch \
  --payload "..." --transport simplex

# Internally: calls simplex-helper.sh send @<mapped-contact-id> <payload>
# Contact mapping: agent-id → simplex contactId (stored in SQLite)
```

---

## 10. Limitations and Gotchas

| Issue | Detail |
|-------|--------|
| No multi-device | One CLI process per database; no concurrent access |
| No auth on WebSocket | Localhost-only; use TLS proxy for remote |
| File paths | Files stored on CLI machine; bot must be co-located or use shared FS |
| Group complexity | Groups are more complex than direct chats; message delivery can lag |
| Unknown events | MUST ignore unknown event types (forward-compat) |
| AGPL license | SDK is AGPL-3.0; bot code that uses it must be AGPL or compatible |
| CLI version | Bot API requires v6.4.3+ for bot profile/commands feature |
| Bun WebSocket | `isomorphic-ws` works; alternatively use `Bun.connect()` directly |
| No voice API | Voice notes are received as files; sending voice requires encoding |

---

## 11. Acceptance Criteria Checklist

- [DONE] Deep-read COMMANDS.md — all command categories documented
- [DONE] Deep-read EVENTS.md — all event types documented
- [DONE] Deep-read TYPES.md — key types extracted for bot development
- [DONE] TypeScript SDK reviewed — `ChatClient` API, package info, Bun compat
- [DONE] CLI install method documented
- [DONE] matrix-bot.md patterns reviewed and mapped to simplex equivalent
- [DONE] mail-helper.sh patterns reviewed (SQLite WAL, TOON, agent ID)
- [DONE] ip-reputation-helper.sh patterns reviewed (shell conventions)
- [DONE] Architecture decisions documented for t1327.3 and t1327.4
- [DONE] Business address pattern documented
- [DONE] Limitations and gotchas documented

---

## 12. Recommendations for Downstream Tasks

**t1327.2 (Subagent doc):** Use sections 1–7 of this report as primary source. Add self-hosted SMP server setup from `docs/SERVER.md`. Cross-reference `tools/security/opsec.md` for metadata/privacy notes.

**t1327.3 (Helper script):** Follow ip-reputation-helper.sh shell conventions exactly. Use `shared-constants.sh`. Subcommands: `install|init|bot-start|bot-stop|bot-status|send|connect|group|status|server`. Config at `~/.config/aidevops/simplex-bot.json`.

**t1327.4 (Bot framework):** Use `simplex-chat` npm SDK v0.3.0. Bun-native: `bun:sqlite` for sessions, native WebSocket. Follow matrix-bot session/dispatch pattern. Router handles `/slash` commands. Business address support via `businessAddress: true` in config.

**t1327.5 (Mailbox transport):** Add `--transport simplex|matrix` flag to `mail-helper.sh`. Store agent→contactId mapping in SQLite. Reuse existing message types unchanged.

**t1327.6 (Opsec agent):** SimpleX metadata: no user IDs, no message graph, SMP servers see only encrypted blobs. Compare with Matrix (federation metadata), Telegram (phone number), Signal (phone number). SimpleX is strongest for metadata resistance.
