Gateway
The gateway is the unified run command for lsbot. It starts all configured platform bots (Telegram, Slack, Discord, etc.) and a WebSocket API server in a single process.
On startup the gateway:
- Loads
~/.lingti.yamlfor platform credentials, agent definitions, and routing bindings - Registers all configured platforms as bot listeners
- Starts the WebSocket server on
:18789(use--no-wsto disable) - Routes each incoming message to the correct agent based on bindings
- Writes
~/.lingti/gateway.pidsogateway restartcan reload config without a full restart
Quick Start
Managing Channels
Channels are platform credentials stored in ~/.lingti.yaml. The channels command is the preferred way to manage them — it reads, updates, and writes the config file for you.
Once saved, credentials are loaded automatically every time you run gateway — no flags needed.
Managing Agents
An agent is an isolated AI brain with its own workspace, session memory, model, instructions, and tool permissions. You can have multiple agents and route different platforms or channels to different agents.
Adding agents
Each agent gets its own workspace directory (~/.lingti/agents/<id> by default). Workspace, model, provider, API key, and instructions are all optional — unset fields inherit from the global ai: config.
Routing bindings
Bindings control which agent handles messages from which source. Resolution order (most specific wins):
| Priority | Match | Example |
|---|---|---|
| 1 (highest) | platform + channel_id | Only messages from slack:C_WORK |
| 2 | platform only | All messages from telegram |
| 3 (lowest) | Default agent | Anything unmatched |
Agent config in ~/.lingti.yaml
allow_tools non-empty = whitelist (only those tools available). deny_tools = blacklist (those tools removed from the full set).
Gateway Flags
| Flag | Env Var | Default | Description |
|---|---|---|---|
--addr | GATEWAY_ADDR | :18789 | WebSocket listen address |
--auth-token | GATEWAY_AUTH_TOKEN | Single auth token for WebSocket clients | |
--auth-tokens | GATEWAY_AUTH_TOKENS | Comma-separated auth tokens | |
--no-ws | false | Disable WebSocket server | |
--provider | AI_PROVIDER | claude | AI provider |
--api-key | AI_API_KEY | AI API key (required) | |
--base-url | AI_BASE_URL | Custom AI API base URL | |
--model | AI_MODEL | Model name | |
--instructions | Path to custom instructions file | ||
--call-timeout | AI_CALL_TIMEOUT | 90 | AI API call timeout (seconds) |
--webapp-port | WEBAPP_PORT | 0 | Web chat UI port (0 = disabled) |
--debug-dir | BROWSER_DEBUG_DIR | Browser debug screenshot directory |
All platform credential flags are also accepted (e.g. --telegram-token, --slack-bot-token) as one-time overrides. See the CLI Reference for the full table.
Disabling the WebSocket Server
The WebSocket server starts by default. Disable it if you only want platform bots:
Reloading Config
After changing ~/.lingti.yaml (e.g. adding a channel or agent), reload the running gateway without restarting it:
This sends SIGHUP to the running process via ~/.lingti/gateway.pid.
Docker / CI (Flags Only)
For deployments without a config file, all credentials can be supplied via flags or environment variables:
WebSocket API
The gateway also exposes a WebSocket API on :18789 for custom clients (web UIs, scripts, mobile apps). This is independent of platform bots — both run concurrently.
Authentication
By default all WebSocket connections are accepted. To require a token:
When auth is enabled, clients must send an auth message before chatting.
HTTP endpoints
| Method | Path | Description |
|---|---|---|
GET | /health | Returns {"status":"ok"} |
GET | /status | Running status, client count, auth state |
GET | /ws | WebSocket upgrade endpoint |
Message protocol
All WebSocket messages are JSON with this envelope:
Client → Server
| Type | Description |
|---|---|
ping | Keep-alive; server replies with pong |
auth | Authenticate: {"payload": {"token": "..."}} |
chat | Send message: {"payload": {"text": "...", "session_id": "optional"}} |
command | Built-in command: {"payload": {"command": "status"}} or "clear" |
Server → Client
| Type | Description |
|---|---|
pong | Reply to ping |
auth_result | Auth outcome: {"payload": {"success": true}} |
response | AI reply: {"payload": {"text": "...", "session_id": "...", "done": true}} |
event | Command result |
error | Error: {"payload": {"code": "unauthorized", "message": "..."}} |
Error codes: unauthorized, invalid_message, invalid_payload, handler_error, no_handler, unknown_type, unknown_command
Example clients
JavaScript:
Python:
Sessions
Each WebSocket connection has one active session. The session ID is established on the first chat message:
- Provide
session_idin the payload to use a specific session - Omit it to use the connection's client ID as the session
Send {"type": "command", "payload": {"command": "clear"}} to reset the session and start a fresh conversation.
Connection lifecycle
The server sends WebSocket-level Ping frames every 30 seconds. Connections that don't respond with Pong within 60 seconds are closed.