Gateway Relay Transport

Run bots behind NAT without public webhooks using an out-of-process relay transport

python
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful.")
# Relay transport carries user messages to the gateway-backed agent
agent.start("Hello via relay")

Relay transport lets a Bot connect to messaging platforms through an outbound WebSocket relay — no public webhook URL required.

The user connects through a relay; messages traverse the relay transport to reach the gateway and agent.

Diagram

Relay Transport

🤖 Bot

🔌 RelayAdapter

🌐 WebSocket Relay

📱 Telegram / Slack / …

Quick Start

  1. Start the relay server

    Run the gateway relay CLI to bridge between the platform and your bot:

    bash
    praisonai gateway relay --platform telegram --to wss://gw.internal/relay
  2. Connect your bot via relay

    Pass a transport= argument when creating a Bot:

    python
    import os
    from praisonaiagents import Agent
    from praisonai.bots import TelegramBot
    from praisonai.bots.relay import RelayAdapter
    
    agent = Agent(
        name="MyBot",
        instructions="Help users with their questions.",
    )
    
    relay = RelayAdapter(url="wss://gw.internal/relay")
    
    bot = TelegramBot(
        token=os.getenv("TELEGRAM_BOT_TOKEN"),
        agent=agent,
        transport=relay,
    )
    
    bot.run()
  3. Scale to zero with go_dormant()

    When idle, call go_dormant() to release the relay connection and stop consuming resources:

    python
    import asyncio
    from praisonaiagents import Agent
    from praisonai.bots import TelegramBot
    from praisonai.bots.relay import RelayAdapter
    
    agent = Agent(name="DormantBot", instructions="Respond to messages.")
    relay = RelayAdapter(url="wss://gw.internal/relay")
    bot = TelegramBot(token=os.getenv("TELEGRAM_BOT_TOKEN"), agent=agent, transport=relay)
    
    async def main():
        await bot.start()
        # After a period of inactivity:
        await bot.go_dormant()
    
    asyncio.run(main())

How It Works

Diagram
📱 Platform🌐 WebSocket Gateway🔌 RelayAdapter🤖 Bot📱 Platform🌐 WebSocket Gateway🔌 RelayAdapter🤖 Botconnect(url)WebSocket handshakeinbound messageforward messagedispatch to agentsend replyforward replydeliver to user

The RelayAdapter maintains a persistent outbound WebSocket connection to the relay gateway. Platform messages arrive through that channel instead of requiring an inbound webhook. Because the connection is outbound-only, the bot runs behind NAT or in a private network with no firewall rules.


When to Use Relay Transport

Diagram

Yes

No

Yes

No

Where is my bot hosted?

Public IP /\nwebhook reachable?

Standard webhook bot

Need scale-to-zero?

RelayAdapter + go_dormant

RelayAdapter

ScenarioRecommended approach
Public cloud with static IPStandard webhook
Private network / NATRelayAdapter
Ephemeral compute (Fly.io, serverless)RelayAdapter + go_dormant()
Multi-region bot fleetRelayAdapter (one relay per region)

Configuration

RelayAdapter

ParameterTypeDefaultDescription
urlstr_(required)_WebSocket URL of the relay gateway
reconnect_intervalfloat5.0Seconds between reconnect attempts
max_reconnectsint0Max reconnect attempts (0 = unlimited)

Bot(transport=...)

python
from praisonai.bots.relay import RelayAdapter

relay = RelayAdapter(
    url="wss://gw.internal/relay",
    reconnect_interval=3.0,
)

bot = TelegramBot(token=token, agent=agent, transport=relay)

CLI

bash
praisonai gateway relay --platform <platform> --to <relay-url>
FlagDescription
--platformPlatform adapter to use (telegram, slack, discord, …)
--toWebSocket URL of the relay gateway

go_dormant()

go_dormant() disconnects the relay transport cleanly when the bot is idle, enabling scale-to-zero hosting. The bot wakes up automatically when a new message arrives and the transport reconnects.

python
async def on_idle():
    await bot.go_dormant()
    # Bot releases WebSocket connection; reconnects on next inbound

See Gateway Scale-to-Zero for configuring the full idle policy.


Best Practices

Keep relay URLs internal

The relay URL is an internal control plane endpoint. Do not expose it publicly — it should only be reachable from your bot’s network.

Set reconnect_interval based on your SLO

The default 5-second reconnect is suitable for most bots. Reduce it if you need sub-second reconnection; increase it on flaky networks to avoid reconnect storms.

Combine with durable delivery

On reconnect the relay may miss messages. Enable Durable Delivery so the outbound outbox survives relay disconnections.

Use go_dormant() only when truly idle

Call go_dormant() only after all in-flight agent turns complete. The relay closes the WebSocket, so any in-progress sends will fail if the turn is still running.


Related