**Course:** Hermes Agent β From Zero to Autonomous Agent
**Module:** 7 of 10
**Reading time:** ~20 minutes
**Difficulty:** Intermediate
TL;DR
| **What it is** | A single daemon process that connects Hermes to Telegram, Discord, Slack, WhatsApp, Signal, and 15+ other platforms β all from one agent instance |
|---|---|
| **Key idea** | Same AI agent, different chat interfaces. Your tools, memory, skills, and cron jobs work identically across every platform |
| **Setup** | `hermes gateway setup` β interactive wizard, or manual `.env` config |
| **Service** | `hermes gateway install` β installs as systemd (Linux) or launchd (macOS) |
| **Security** | Allowlists, DM pairing, admin/user tiers, command approval for dangerous operations |
| **Cron** | Scheduled jobs deliver to your home channel on any platform |
What Is the Gateway?
The Hermes Gateway is a **background daemon** that runs your agent and connects it to messaging platforms. Think of it as the bridge between your LLM-powered agent and the apps you use every day.
Without the gateway, you run Hermes in your terminal β useful, but tied to your desktop. With the gateway:
- You message your agent from **Telegram on your phone** while commuting
- Your **Discord server** has a shared AI assistant your whole team can DM
- **Cron jobs** (scheduled tasks) run 24/7 and deliver results to your chat
- Your agent is always online, always ready, whether you're at your desk or not
The architecture is clean:
Telegram ββ
Discord ββ€
Slack ββ€
WhatsApp ββ€
Signal ββ€ βββββββββββββββββββββββ
Email ββ€ββ platform βββ Hermes Gateway ββββ AIAgent (your LLM)
SMS ββ€ adapters β (single daemon) ββββ Tools (shell, files, web)
Matrix ββ€ β Sessions, cron, ββββ Skills
Home ββ€ β security, voice ββββ Memory
Assistantββ βββββββββββββββββββββββ
One gateway process. Many platform adapters. Same agent, tools, skills, and memory everywhere.
Supported Platforms
Hermes ships with built-in adapters for the following platforms. Each adapter receives messages, routes them through a per-chat session store, and dispatches them to the agent for processing.
| Platform | Voice | Images | Files | Threads | Typing | Streaming |
|---|---|---|---|---|---|---|
| ---------- | :-----: | :------: | :-----: | :-------: | :------: | :---------: |
| **Telegram** | β | β | β | β | β | β |
| **Discord** | β | β | β | β | β | β |
| **Slack** | β | β | β | β | β | β |
| **WhatsApp** | β | β | β | β | β | β |
| **Signal** | β | β | β | β | β | β |
| **SMS (Twilio)** | β | β | β | β | β | β |
| **Email** | β | β | β | β | β | β |
| **Matrix** | β | β | β | β | β | β |
| **Mattermost** | β | β | β | β | β | β |
| **Home Assistant** | β | β | β | β | β | β |
| **DingTalk** | β | β | β | β | β | β |
| **Feishu / Lark** | β | β | β | β | β | β |
| **WeCom (WeChat Work)** | β | β | β | β | β | β |
| **Weixin** | β | β | β | β | β | β |
| **BlueBubbles (iMessage)** | β | β | β | β | β | β |
| **QQ** | β | β | β | β | β | β |
| **Yuanbao** | β | β | β | β | β | β |
| **Microsoft Teams** | β | β | β | β | β | β |
| **LINE** | β | β | β | β | β | β |
| **Google Chat** | β | β | β | β | β | β |
| **API Server** (OpenAI-compatible) | β | β | β | β | β | β |
| **Webhooks** | β | β | β | β | β | β |
**Voice** = TTS audio replies and/or voice message transcription. **Images** = send/receive images. **Files** = send/receive attachments. **Threads** = threaded conversations. **Typing** = typing indicator while processing. **Streaming** = progressive message updates.
Platforms without built-in adapters can be added via the plugin system. See `ADDING_A_PLATFORM.md` in the gateway source.
How the Gateway Works
Architecture Detail
Each platform has its own **adapter** β a Python module that handles the platform-specific protocol (Telegram's polling/webhook API, Discord's WebSocket gateway, Signal's CLI bridge, etc.).
When a message arrives:
1. **Authorization check** β Is this user allowed? (Allowlist, DM pairing, or admin check)
2. **Session lookup** β Does this chat have an existing conversation context?
3. **Platform-specific handling** β Mention checks, free-response channels, thread isolation
4. **Agent dispatch** β The message is sent to the AIAgent with the correct session context
5. **Response delivery** β The agent's response is streamed back to the platform with typing indicators
Same Tools Everywhere
Every platform gets the same toolset β just with a different toolset name for logging:
| Platform | Toolset Name | Capabilities |
|---|---|---|
| Telegram | `hermes-telegram` | Full tools including terminal |
| Discord | `hermes-discord` | Full tools including terminal |
| Slack | `hermes-slack` | Full tools including terminal |
| `hermes-whatsapp` | Full tools including terminal | |
| SMS | `hermes-sms` | Full tools including terminal |
| `hermes-email` | Full tools including terminal | |
| Home Assistant | `hermes-homeassistant` | Full tools + HA device control |
This means the **same agent** that runs shell commands in your terminal can do it from Telegram, Discord, or any other platform.
Session Management
Sessions persist across messages until they reset. The agent remembers your conversation context.
Reset policies (configurable):
- **Daily** β Reset at a specific hour (default: 4:00 AM)
- **Idle** β Reset after N minutes of inactivity (default: 1440 min)
- **Both** β Whichever triggers first (default)
- **None** β Never auto-reset (controlled by compression only)
Configure per-platform overrides in `~/.hermes/gateway.json`:
{
"reset_by_platform": {
"telegram": { "mode": "idle", "idle_minutes": 240 },
"discord": { "mode": "idle", "idle_minutes": 60 }
}
}
Telegram Setup
This is the most common gateway setup. Let's walk through it step by step.
Step 1: Create a Bot via BotFather
1. Open Telegram and search for **@BotFather**, or visit [t.me/BotFather](https://t.me/BotFather)
2. Send `/newbot`
3. Choose a **display name** (e.g., "My Hermes Agent")
4. Choose a **username** that ends in `bot` (e.g., `my_hermes_bot`)
5. BotFather replies with your **API token**:
123456789:ABCdefGHIjklMNOpqrSTUvwxYZ
**Keep this token secret.** Anyone with it can control your bot. If it leaks, use `/revoke` in BotFather immediately.
Step 2: Optional Customization
Message BotFather and use:
| Command | Purpose |
|---|---|
| `/setdescription` | What the bot can do β shown before a user chats |
| `/setabouttext` | Short text on the bot's profile |
| `/setuserpic` | Upload an avatar |
| `/setcommands` | Define the command menu |
A useful starting set for `/setcommands`:
help - Show help information
new - Start a new conversation
sethome - Set this chat as the home channel
Step 3: Privacy Mode (Critical for Groups)
Telegram bots have **privacy mode enabled by default**. This is the #1 source of confusion.
**With privacy mode ON**, your bot can only see:
- Messages starting with `/`
- Replies to its own messages
- Service messages (joins, leaves, pins)
**With privacy mode OFF**, the bot sees every message in the group.
To disable it:
1. Message **@BotFather**
2. Send `/mybots` β select your bot
3. Go to **Bot Settings β Group Privacy β Turn off**
**Important:** You must remove and re-add the bot to any group after changing this setting. Telegram caches the privacy state when the bot joins.
Step 4: Find Your User ID
Your Telegram user ID is a number like `123456789`. Message [@userinfobot](https://t.me/userinfobot) to get it instantly.
Step 5: Configure Hermes
**Option A: Interactive Setup (Recommended)**
hermes gateway setup
Select **Telegram** when prompted. The wizard asks for your bot token and allowed user IDs, then writes the configuration.
**Option B: Manual Configuration**
Add to `~/.hermes/.env`:
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrSTUvwxYZ
TELEGRAM_ALLOWED_USERS=123456789
For multiple users, comma-separate the IDs:
TELEGRAM_ALLOWED_USERS=123456789,987654321
Step 6: Start the Gateway
hermes gateway
The bot comes online within seconds. Send it a message on Telegram to verify.
Discord Setup
Step 1: Create a Discord Application
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications)
2. Click **New Application** β enter a name β **Create**
3. Note your **Application ID** β you'll need it for the invite URL
Step 2: Create the Bot
1. In the left sidebar, click **Bot**
2. Set **Public Bot** to **ON** (recommended)
3. Set a custom avatar if desired
Step 3: Enable Privileged Gateway Intents
**This is the most critical step.** Scroll down to **Privileged Gateway Intents** and enable:
| Intent | Required? | Why |
|---|---|---|
| -------- | :---------: | ----- |
| **Presence Intent** | Optional | See online/offline status |
| **Server Members Intent** | **Required** | Resolve usernames for allowed users |
| **Message Content Intent** | **Required** | Read the text content of messages |
**Without Message Content Intent enabled, your bot receives messages but the text content is empty.** It's the #1 reason Discord bots appear online but never respond.
Step 4: Get the Bot Token
Still on the **Bot** page, click **Reset Token**. Copy it immediately β it's only shown once.
Step 5: Generate the Invite URL
Use the **Installation** tab (if Public Bot is ON) or create a manual URL:
https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&scope=bot+applications.commands&permissions=274878286912
Replace `YOUR_APP_ID` with the ID from Step 1.
Minimum permissions: View Channels, Send Messages, Embed Links, Attach Files, Read Message History.
Step 6: Invite to Your Server
Open the invite URL β select your server β **Authorize**. The bot appears in your server's member list.
Step 7: Find Your User ID
1. Open Discord β **Settings** β **Advanced** β enable **Developer Mode**
2. Right-click your username β **Copy User ID**
Step 8: Configure Hermes
**Interactive:**
hermes gateway setup
**Manual β add to `~/.hermes/.env`:**
DISCORD_BOT_TOKEN=your-bot-token
DISCORD_ALLOWED_USERS=284102345871466496
Step 9: Start the Gateway
hermes gateway
The bot comes online. Send it a DM or `@mention` it in a channel.
Gateway as a Systemd Service
For 24/7 operation, install the gateway as a background service.
Installation
hermes gateway install
On Linux, this creates a systemd user service at `~/.config/systemd/user/hermes-gateway.service`.
For a boot-time system service (recommended for VPS):
sudo hermes gateway install --system
Service Management
hermes gateway start # Start the service
hermes gateway stop # Stop the service
hermes gateway restart # Restart the service
hermes gateway status # Check if it's running
Enable Lingering (Linux)
Keeps user services running after you log out:
sudo loginctl enable-linger $USER
View Logs
# Systemd journal (user service)
journalctl --user -u hermes-gateway -f
# Systemd journal (system service)
journalctl -u hermes-gateway -f
# Direct log file
tail -f ~/.hermes/logs/gateway.log
macOS (launchd)
hermes gateway install # Install as launchd agent
hermes gateway start # Start
hermes gateway stop # Stop
hermes gateway status # Check status
tail -f ~/.hermes/logs/gateway.log # View logs
The plist lives at `~/Library/LaunchAgents/ai.hermes.gateway.plist` and captures your PATH at install time. If you add new tools later, re-run `hermes gateway install` to update it.
Gateway Status
Check what's running:
hermes gateway status
This shows:
- Whether the service is active
- How long it's been running
- Which platforms are connected
- Last log entries
From within any connected chat, use:
/status β Show session info
/whoami β Show your access tier and allowed commands
/platforms β Show all active platform adapters
The `/platform` command lets you inspect and steer individual adapters without restarting:
/platform list β Show all adapters and their state
/platform pause <name> β Stop dispatching new messages to one adapter
/platform resume <name> β Re-enable a paused adapter
Gateway Logs
Logs are written to `~/.hermes/logs/gateway.log`. This is your primary debugging resource.
Important log locations:
| What | Where |
|---|---|
| Gateway log | `~/.hermes/logs/gateway.log` |
| Agent log | `~/.hermes/logs/agent.log` |
| Errors | `~/.hermes/logs/errors.log` |
| Systemd journal (user) | `journalctl --user -u hermes-gateway -f` |
| Systemd journal (system) | `journalctl -u hermes-gateway -f` |
Each adapter logs its name in brackets, making it easy to filter:
[telegram] Connected to Telegram (polling mode)
[discord] Connected to Discord
[telegram] Message from @username: /new
Security
Allowlists
By default, the gateway denies all users who are not explicitly allowed. This is the safe default for a bot with terminal access.
# Per-platform
TELEGRAM_ALLOWED_USERS=123456789,987654321
DISCORD_ALLOWED_USERS=284102345871466496
SIGNAL_ALLOWED_USERS=+15551234567
# Global (applies to all platforms)
GATEWAY_ALLOWED_USERS=123456789
# Or explicitly allow everyone (NOT recommended if the bot has terminal access)
GATEWAY_ALLOW_ALL_USERS=true
DM Pairing
Instead of manually configuring user IDs, unknown users receive a one-time pairing code when they DM the bot:
# User sees: "Pairing code: XKGH5N7P"
# You approve them with:
hermes pairing approve telegram XKGH5N7P
# Other commands:
hermes pairing list # View pending + approved users
hermes pairing revoke telegram 123456789 # Remove access
Pairing codes expire after 1 hour and use cryptographic randomness.
Admin vs Regular Users
Every allowed user falls into one of two tiers per scope:
- **Admin** β full access to all slash commands and capabilities
- **Regular user** β can chat normally but only run explicitly enabled slash commands
Configure in `~/.hermes/config.yaml`:
gateway:
platforms:
discord:
extra:
allow_from: ["111", "222", "333"]
allow_admin_from: ["111"]
user_allowed_commands: [status, model]
Use `/whoami` from any platform to see your current tier and allowed commands.
Command Approval
When the agent wants to run a dangerous command (e.g., `rm -rf /`), the gateway sends an **approve/deny** button (Telegram) or prompts you to use `/approve` / `/deny`. This prevents the agent from executing destructive operations without your confirmation.
Home Channel Concept
The **home channel** is the default delivery destination for your platform. When a cron job specifies `deliver: telegram` without a specific chat ID, messages are sent to the home channel.
Setting a Home Channel
The easiest way β in any chat, use:
/sethome
The gateway confirms with something like:
β
This chat is now the home channel for Telegram.
Manual Configuration
In `~/.hermes/.env`:
TELEGRAM_HOME_CHANNEL=-1001234567890
TELEGRAM_HOME_CHANNEL_NAME="My Notes"
DISCORD_HOME_CHANNEL=987654321098765432
DISCORD_HOME_CHANNEL_NAME="Team Chat"
Group chat IDs are negative numbers on Telegram.
Cron Deliveries
When Hermes executes a scheduled task (cron job), the result needs to go somewhere. The gateway handles delivery seamlessly.
How It Works
1. Cron job fires at the scheduled time
2. Agent executes the task in a fresh session
3. Result is delivered to the configured destination
Delivery Options
When creating a cron job, specify where results go:
# Deliver to the home channel
hermes cron create "0 8 * * *" "Check server status" --deliver telegram
# Deliver to Discord home channel
hermes cron create "0 8 * * *" "Check server status" --deliver discord
# Save to local files
hermes cron create "0 8 * * *" "Check server status" --deliver local
From a chat session, just tell the agent naturally:
Every morning at 9am, check the server status and send me a summary on Telegram.
Hermes creates the cron job and configures delivery automatically.
Running the Gateway 24/7 on a VPS
For a permanently online agent, run the gateway on a VPS:
1. Provision a VPS ($4-6/month β see Module 8)
2. Install Hermes with the standard one-liner
3. Configure your API keys and platform tokens in `.env`
4. Install the gateway as a system service:
sudo hermes gateway install --system
sudo hermes gateway start --system
sudo loginctl enable-linger $USER
5. Verify with:
sudo hermes gateway status --system
The gateway restarts automatically at boot, runs your cron jobs, and delivers results to your home channel.
Troubleshooting
Gateway Crash Loop
**Symptom:** The gateway starts and immediately crashes. `systemctl` shows `failed` status.
**Diagnosis:**
journalctl --user -u hermes-gateway -n 50 --no-pager
**Common causes:**
- **Missing API keys** β Your `.env` file doesn't have a required API key for your LLM provider. Check that the provider's API key is set.
- **Invalid bot token** β The Telegram or Discord token is wrong. Verify it in `.env`.
- **Platform auth failure** β Check logs for `authentication failed` messages.
**Fix:**
1. Verify `.env` has all required keys
2. Run `hermes gateway` in foreground to see real-time errors
3. If a platform token is invalid, regenerate it and update `.env`
4. Restart: `hermes gateway restart`
Discord Silent (Bot Online but Not Responding)
**Symptom:** The bot shows as online/active in your server but never responds to messages.
**Almost always caused by missing Message Content Intent.**
**Check:**
1. Go to [Discord Developer Portal](https://discord.com/developers/applications)
2. Select your application β **Bot** β **Privileged Gateway Intents**
3. Is **Message Content Intent** toggled ON?
**Fix:**
1. Toggle **Message Content Intent** ON
2. Click **Save Changes**
3. Restart the gateway: `hermes gateway restart`
**Still not working?** Check:
- Is your user ID in `DISCORD_ALLOWED_USERS`?
- Are you mentioning the bot? (In channels, you need `@mention` unless free-response channels are configured)
- Check the gateway log for authorization denials
Telegram Not Responding
**Symptom:** The bot is online but doesn't reply to messages.
**Check:**
tail -f ~/.hermes/logs/gateway.log
**Common causes:**
- **Wrong bot token** β Verify in `.env`
- **User not authorized** β Is your user ID in `TELEGRAM_ALLOWED_USERS`?
- **Privacy mode blocking** β If the bot is in a group, check privacy mode
- **Rate limiting** β Telegram rate-limits bots that send too many requests. Wait a few minutes.
**Fix:**
1. Send a message and watch the logs in real time
2. If you see `Not authorized`, add your user ID to the allowlist
3. If you see nothing at all, the bot token is likely wrong
4. Restart the gateway
Circuit Breaker Paused an Adapter
**Symptom:** A platform that was working suddenly stops. Log shows "paused-by-breaker".
**Cause:** The adapter encountered repeated failures (network blips, rate-limit replies, websocket disconnects) and the circuit breaker tripped.
**Fix:**
# From another platform or CLI
/platform resume <name>
The breaker does **not** auto-resume β this prevents the gateway from thrashing reconnects during a sustained outage.
Logs Show "Failed" but Gateway Seems Fine
The gateway uses **circuit breakers** per adapter. Repeated retryable failures cause the breaker to trip. Check:
1. `~/.hermes/logs/gateway.log` β search for `circuit breaker`, `paused`, or `disabled`
2. `/platform list` output β shows current state and last reason
3. The platform's status page (Telegram API status, Discord status, etc.)
Once upstream is healthy, use `/platform resume
Summary
- The **gateway** is a single daemon connecting Hermes to 20+ messaging platforms
- Each platform has its own **adapter** β but all share the same agent, tools, memory, and skills
- **Telegram** setup: BotFather β get token β `hermes gateway setup` β done
- **Discord** setup: Developer Portal β create bot β **enable Message Content Intent** β invite β configure
- Install as a **systemd service** for 24/7 operation: `hermes gateway install`
- **Security** is multi-layered: allowlists, DM pairing, admin/user tiers, command approval
- The **home channel** (`/sethome`) is where cron deliveries land
- **Troubleshooting** starts with the gateway log: `~/.hermes/logs/gateway.log`
In the next module, we'll deploy the gateway to a VPS for always-on, production-grade operation.