Gateway Overview

WebSocket gateway for multi-channel agent coordination and communication

python
from praisonaiagents import Agent

agent = Agent(name="gateway-agent", instructions="Route messages through the PraisonAI gateway.")
agent.start("Set up the gateway to handle Telegram and Slack messages.")

The PraisonAI Gateway enables multi-channel agent deployment through a WebSocket server that coordinates communication between agents and various platforms.

The user starts the gateway and sends a message on a channel; the gateway routes it to the right agent and returns the reply.

Diagram

Gateway Architecture

Agents

Gateway Server – Port 8765

Users

🤖 Support Agent

👤 Telegram User

📱 Telegram Channel

👤 Discord User

💬 Discord Channel

👤 WhatsApp User

📞 WhatsApp Channel

🔀 Router

🤖 Sales Agent

🤖 Technical Agent

🌐 WebSocket Server

Quick Start

  1. Install Gateway Dependencies

    Install both bot and API dependencies for gateway functionality:

    bash
    pip install "praisonai[bot,api]"

    The [api] extra provides uvicorn, fastapi, and starlette required by the gateway server.

  2. Zero-config path: just an env var

    Set a platform token and start — no channels: block required:

    bash
    export TELEGRAM_BOT_TOKEN=123:abc
    praisonai gateway

    One log line confirms the bot is live:

    text
    Auto-enabled channel 'telegram' from TELEGRAM_BOT_TOKEN

    See Zero-Config Gateway for the full precedence rules and the env-var-to-channel mapping.

  3. Custom config path: route, restrict, or use a non-env token

    When you want to route, restrict, or use a non-env token, create a gateway.yaml file:

    yaml
    gateway:
      host: "127.0.0.1"
      port: 8765
    
    agents:
      assistant:
        model: gpt-4o-mini
        instructions: "You are a helpful assistant."
    
    channels:
      telegram:
        platform: telegram
        token: ${TELEGRAM_BOT_TOKEN}
        routes:
          default: assistant
  4. Start the Gateway

    Launch the gateway server. The env-only path needs no --config flag:

    bash
    # Zero-config (env var only)
    praisonai gateway
    
    # With a custom config
    praisonai gateway start --config gateway.yaml

    The gateway starts on port 8765 with WebSocket and HTTP health endpoints.


How It Works

Diagram
LLMAgentGatewayPlatform (Telegram/Discord)UserLLMAgentGatewayPlatform (Telegram/Discord)UserSend messageForward via polling/webhookRoute to configured agentProcess with contextReturn responseSend replyForward responseDisplay message
ComponentRole
Gateway ServerManages WebSocket connections and routes messages
ChannelsPlatform-specific integrations (Telegram, Discord, etc.)
RouterDirects messages to appropriate agents based on rules
AgentsProcess messages and generate responses
Inbound TriggersPOST /hooks/<path> — start agent runs from external HTTP events (webhooks, CI, IoT). See Inbound Hooks.
Outbound Sinksgateway.hooks_outbound — run a shell command on lifecycle events (session_end, gateway_stop, …). See Outbound Hooks.
Webhook IngressPOST /webhooks/<channel> — signature-verified platform-webhook ingress for mode: webhook channels. See Shared Webhook Ingress.

Two inbound surfaces on one listener

The gateway’s single listener hosts two distinct inbound paths:

  • /hooks/<path> — the declarative inbound trigger surface: ad-hoc HTTP-to-agent triggers you define with hooks: entries.
  • /webhooks/<channel> — signature-verified platform-webhook ingress for mode: webhook channels, each verified fail-closed by its own per-channel verifier.

Leave webhook_port unset on a mode: webhook channel and it serves through /webhooks/<channel> on gateway.port — one URL for every channel. See Shared Webhook Ingress.


Architecture Principles

Single Instance Rule

Critical: Only run one gateway process per state root (~/.praisonai). Two gateways sharing it would:

  • Both poll the same Telegram token (causes 409 conflicts)
  • Both write the same outbox, DLQ and pairing stores (split-brain state)

The gateway enforces this at startup with a state lock — a second gateway on another port but the same ~/.praisonai is refused. See Single-Instance Enforcement.

Diagram

Just a different port

Own HOME + own bot tokens + own port

🤔 Two gateways on one host?

❌ Refused: state already owned

✅ Supported

Channel Isolation

Each channel operates independently:

  • Separate token per platform
  • Independent routing rules
  • Isolated session management
  • Per-channel error handling

Live Config Reload

The gateway diffs gateway.yaml against the running config and restarts only affected agents or channels — and for three timing/logging keys, applies the change in place with no restart at all. Invalid YAML saves keep the last-known-good config. The WebSocket server is never restarted. See Gateway Hot-Reload.

Fail-Safe Design

The gateway implements fail-safe patterns:

  • Health checks at /health endpoint
  • Automatic reconnection for platform polling
  • Graceful degradation when agents are unavailable
  • Request timeout handling (25-30 seconds for RAG)
  • Versioned hello handshake with capability negotiation — see Handshake Protocol. Connecting from Python, the bundled praisonai-bot client performs this handshake automatically and exposes the negotiated client.features / client.policy / client.heartbeat_ms — see Client-side wiring.
  • Shutdown forensics — on every exit, one log line and one diagnostic file record why the gateway stopped. See Crash Forensics.
  • Opt-in close-the-loop notice on permanent delivery failure — see Undelivered Message Notice.

Config Validation

A typo in gateway.yaml won’t start a broken gateway — it fails fast with exit 78 before your users notice.

praisonai gateway start and praisonai bot serve now validate the same gateway.yaml to the same standard: canonical configs start, legacy configs auto-migrate then start, and typos exit 78.

Diagram

Outcomes

Inputs

📄 Canonical config

📄 Legacy config

⚠️ Typo config

🛡️ Registry-aware validator

✅ Starts

🔄 Auto-migrated then starts

🛑 Exit 78

The validator consults the platform registry — built-in platforms plus any published via praisonai.channels / praisonai.bots entry points plus any registered at runtime with register_platform().

Typo channel (telegran) — rejected, exit 78
yaml
agents:
  assistant:
    instructions: "You are a helpful assistant."
channels:
  telegran:                     # typo — should be 'telegram'
    token: ${TELEGRAM_BOT_TOKEN}

Before: warns, gateway starts, /health returns 200 — but Telegram is silently dead.

text
WARNING  Channel 'telegran' platform 'telegran' is not a known platform
         (agentmail, discord, email, linear, local, slack, telegram, whatsapp)
INFO     Gateway started on http://127.0.0.1:8765

After: rejected before startup, exit 78 — the supervisor does not crash-loop.

text
ERROR    Gateway config invalid (gateway.yaml):
           - Unknown channel 'telegran' (platform 'telegran').
             Supported platforms: agentmail, discord, email, linear, local, slack, telegram, whatsapp
$ echo $?                              # 78
Real adapter (linear) — accepted cleanly
yaml
channels:
  linear:
    token: ${LINEAR_API_KEY}

Before: spurious “not a known platform” warning, then starts.

After: accepted with no warning — linear is a shipped adapter on the registry.

Plugin channel (mattermost) — accepted via register_platform()
python
from praisonai.bots._registry import register_platform

class MattermostBot:
    def __init__(self, **kwargs): self.cfg = kwargs
    async def start(self): ...
    async def stop(self): ...

register_platform("mattermost", MattermostBot)
yaml
channels:
  mattermost:
    token: ${MATTERMOST_TOKEN}

Before: warned “not a known platform”, gateway started, channel silently dead on the runtime path.

After: accepted on both praisonai bot serve and praisonai gateway start.


Gateway Modes

Multi-Channel Mode

Run multiple platforms simultaneously:

yaml
channels:
  telegram_support:
    platform: telegram
    token: ${TELEGRAM_SUPPORT_TOKEN}
    routes:
      default: support_agent

  discord_community:
    platform: discord
    token: ${DISCORD_BOT_TOKEN}
    routes:
      default: community_agent

Each channel requires a unique token and can route to different agents.

WebSocket-Only Mode

Pure WebSocket server without chat platforms:

bash
praisonai gateway start --host 127.0.0.1 --port 8765

Provides WebSocket endpoint for custom client integration.

Agent File Mode

Load agents from separate configuration:

bash
praisonai gateway start --agents agents.yaml

Separates agent definitions from gateway configuration.

Safe Mode (Incident Triage)

Boot core-only — external plugins and inbound YAML hooks disabled — to isolate a fault:

bash
praisonai gateway start --config gateway.yaml --safe-mode

Aliases --pure / --no-plugins are equivalent. See Gateway Safe Mode.


Health Monitoring

The gateway exposes health information:

bash
curl http://127.0.0.1:8765/health

Response:

json
{
  "status": "healthy",
  "uptime": 3600.5,
  "agents": 3,
  "sessions": 12,
  "clients": 8,
  "channels": {
    "telegram_support": {
      "platform": "telegram",
      "running": true
    },
    "discord_community": {
      "platform": "discord", 
      "running": true
    }
  }
}

Push Transport Health

When push is enabled with Redis fan-out, health() adds a push block reporting the cross-instance transport:

FieldTypeMeaning
push.redis_connectedbooltrue only when the client is live and not degraded
push.redis_degradedbooltrue while a reconnect is in flight
push.redis_dropped_writesintCumulative writes that hit a disconnected adapter

A degraded transport is also listed under degraded_owners as route:redis-pubsub. See Redis Pub/Sub Resilience.

Auth brute-force budgets are also shared across replicas when push.redis is set — see Cluster-Wide Auth Rate Limiting.

Pressure Telemetry

When the back-pressure machinery is wired, health() adds a top-level pressure block that classifies gateway load as nominal, elevated, or saturated — so you see the gateway backing up before it sheds turns:

json
{
  "status": "healthy",
  "pressure": {
    "admission_in_flight": 15,
    "admission_queued": 40,
    "outbox_pending": 412,
    "outbox_dead_letter": 2,
    "event_loop_lag_p99_ms": 740.0,
    "pressure": "saturated"
  }
}
Pressure Telemetry

Admission, queues, dead-letter and loop-lag on one health block.

Health Check Limitations

Current implementation limitations:

  • "running": true doesn’t guarantee platform polling health
  • No detection of Telegram 409 conflicts until PraisonAI fix ships
  • Manual verification required for silent bot issues

Configuration Options

OptionTypeDefaultDescription
hoststr"127.0.0.1"Gateway bind address
portint8765Gateway port
auto_enable_from_envbooltrueAuto-register a channel for any known platform whose credential env var(s) are all present and that isn’t declared or disabled. See Zero-Config Gateway.
channels.<name>.enabledbooltrueExplicit per-channel opt-out. false drops the channel and suppresses credential-presence auto-enable for that platform.
max_connectionsint100WebSocket connection limit
heartbeat_intervalint30WebSocket ping interval
session_timeoutint3600Session expiration in seconds
session.persistbooltruePersist sessions across restarts (durable-by-default as of PR #3595 — set to false to opt back to ephemeral, in-memory sessions). See Gateway Session Persistence.
gateway.durable_runsboolauto (on when session store is durable)Auto-enables durable runs (ExecutionConfig(durable=True)) when a durable session store is present (the default). Explicit false or gateway.reliability: "off" opts back out. Per-agent durable overrides. See Gateway Durable Runs.
session.persist_pathstr~/.praisonai/sessions/Storage directory
session.storestr"sqlite"Transcript backend when persist is on — "sqlite" (SQLite Transcript Store) or "file" (legacy JSON)
session.resume_windowint86400Seconds detached sessions stay resumable

Best Practices

Edit gateway.yaml live

You can change agent instructions, models, or a single channel section while the gateway runs. Agent-only edits recreate agents without restarting channels. See Hot-Reload.

Use environment variables for secrets

Never hardcode tokens in configuration files:

yaml
# Good
channels:
  telegram:
    token: ${TELEGRAM_BOT_TOKEN}

# Bad  
channels:
  telegram:
    token: "123456:hardcoded_token"

Store tokens in .env file or environment variables.

Implement proper error handling

Monitor gateway logs for platform-specific errors:

bash
# View gateway logs
tail -f ~/.praisonai/logs/gateway.log

# Common error patterns
grep "409\|Conflict\|getUpdates" ~/.praisonai/logs/gateway.log
Configure resource limits

Set appropriate limits based on expected load:

yaml
gateway:
  max_connections: 100    # Adjust for concurrent users
  session_timeout: 3600   # 1 hour default
  message_queue_size: 1000
Use unique tokens per channel

Each channel must have its own platform token:

yaml
channels:
  telegram_support:
    token: ${TELEGRAM_SUPPORT_TOKEN}  # Bot 1
  telegram_sales:  
    token: ${TELEGRAM_SALES_TOKEN}    # Bot 2 (different bot)

Never reuse tokens across channels.


Cost Optimization

Running a PraisonAI gateway on serverless hosts (Fly.io Machines, Modal, Cloud Run) charges for every second the machine is alive — even when no users are chatting. The Scale-to-Zero feature lets the gateway stand transports down after a configurable period of silence and wake back up on the next inbound message, so you pay only for active time.

Scale-to-Zero Gateway

Suspend the gateway when idle and wake on the next message — pay only for active time.


Lifecycle

Three opt-in lifecycle policies wire into the primary WebSocketGateway through a lifecycle: block in gateway.yaml, matching CLI flags, and a Python surface. Each is off unless enabled, and configured features surface under health()["lifecycle"].


Related