# Architecture

## Overview

Teletraan is built around three layers: a **conductor agent** (Claude Code) that does intelligent work, a **channel server** that acts as a message bus and real-time bridge, and **service modules** that add discrete capabilities. PM2 manages all background processes.

## How Agents Work

Each agent is a persistent Claude Code session running in a named tmux window or as a PM2 process. Agents are configured with:

- A `CLAUDE.md` system prompt defining their personality, capabilities, and constraints
- A `.mcp.json` pointing to any MCP servers they use
- A `.claude/settings.json` with lifecycle hooks

The **conductor** is the primary agent. It handles user requests from the dashboard, executes shell commands, manages files, and can dispatch tasks to specialist agents.

### FIFO Message Delivery

Messages to agents are queued via the channel server's task ledger. The channel server stores messages in a SQLite-backed queue; agents poll via MCP tool calls or receive messages via a `session-start` hook that fetches recent history when a session restarts.

### Channel Server

The channel server (`core/tools/channel-server/`) is a TypeScript HTTP + WebSocket server that runs on port 7100. It serves as the bridge between the web dashboard and the conductor agent.

Key endpoints:

| Method | Path | Purpose |
|--------|------|---------|
| POST | `/conversations/:id/messages` | Send a message to an agent |
| GET | `/conversations/:id/messages` | Retrieve conversation history |
| POST | `/tasks` | Enqueue a task for the conductor |
| GET | `/tasks/:id` | Poll task status |
| POST | `/status` | Receive agent hook events (tool use) |
| GET | `/health` | Health check |

WebSocket connections on `/ws` receive real-time status events pushed by agent hooks.

### MCP Integration

Agents access tools through the Model Context Protocol. The channel server itself exposes an MCP interface so agents can call tools like `send_message`, `get_messages`, and `update_task_status` directly from their Claude Code session.

### stream-json

Large payloads (e.g. conversation history, image data) are delivered as newline-delimited JSON streams over HTTP to avoid memory pressure on the Node.js process.

## Message Flow

```
User (browser)
    │
    │  HTTP POST /conversations/:id/messages
    ▼
Channel Server (port 7100)
    │  stores message in SQLite
    │  broadcasts via WebSocket
    │
    ├──► Web Dashboard (WebSocket)  ← real-time UI updates
    │
    └──► Conductor Agent
             │  reads via session-start hook (on restart)
             │  or via MCP tool call (poll)
             │
             │  executes task, emits hook events
             │
             │  POST /status  (PreToolUse / PostToolUse)
             ▼
         Channel Server
             │
             └──► Web Dashboard (WebSocket) ← live tool-use feed
```

## Web Dashboard Connection

The web dashboard (a Next.js app, port 3006) connects to the channel server via:

1. **REST** — initial page load fetches conversation history and task list
2. **WebSocket** — subscribes to `/ws` for real-time status events (tool calls, completions)

Authentication uses a shared bearer token (`ONE_CHANNEL_SECRET`) set in the conductor's environment and validated by `core/tools/channel-server/lib/auth.ts`.

## Module Architecture

Each module is a self-contained Node.js service with:

```
modules/<name>/
├── manifest.json   # name, version, description, dependencies, ports, serviceType
├── package.json    # npm deps, build/start scripts
├── setup.js        # interactive config prompts (returns config object)
└── src/            # TypeScript source
    └── index.ts    # Express/Fastify HTTP server
```

During installation, modules are copied to `services/<name>/`, built, and registered in `config/manifest.json`. PM2 is configured to manage each module as a separate process.

Modules communicate with the conductor by posting to the channel server's `/status` or `/conversations` endpoints, using the shared `ONE_CHANNEL_SECRET` for authentication.

## Port Allocation

| Service | Port | Description |
|---------|------|-------------|
| Channel server | 7100 | Agent ↔ dashboard bridge |
| Main dashboard | 3006 | Next.js web UI |
| Calendar module | 8084 | Google Calendar sync API |
| Status module | 8086 | Host/service/site monitoring API |
| Notifications module | 8087 | In-app notification store |

The Telegram bridge does not bind a local port; it connects outbound to the Telegram Bot API.

## Authentication Flow (OAuth2)

The web dashboard uses Google OAuth2 for user login:

```
Browser
  │  GET /auth/google
  ▼
Next.js Auth Route
  │  redirects to Google consent screen
  ▼
Google OAuth2
  │  POST /auth/google/callback  (with code)
  ▼
Next.js Callback Handler
  │  exchanges code for tokens
  │  validates email against config adminEmail
  │  sets signed session cookie
  ▼
Dashboard (authenticated)
```

OAuth credentials (client ID, client secret) are stored in `config/system.json` and injected into the dashboard's environment at startup. Refresh tokens are stored in the dashboard's SQLite database.

For module-level OAuth (e.g., the calendar module accessing Google Calendar on behalf of the system), tokens are stored in the module's own `data/` directory and refreshed automatically.
