The architecture has been updated
This commit is contained in:
parent
805f7a017e
commit
a01257ead9
1119 changed files with 226 additions and 352 deletions
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"label": "Messaging Gateway",
|
||||
"position": 3,
|
||||
"link": {
|
||||
"type": "doc",
|
||||
"id": "user-guide/messaging/index"
|
||||
}
|
||||
}
|
||||
192
hermes_code/website/docs/user-guide/messaging/dingtalk.md
Normal file
192
hermes_code/website/docs/user-guide/messaging/dingtalk.md
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
---
|
||||
sidebar_position: 10
|
||||
title: "DingTalk"
|
||||
description: "Set up Hermes Agent as a DingTalk chatbot"
|
||||
---
|
||||
|
||||
# DingTalk Setup
|
||||
|
||||
Hermes Agent integrates with DingTalk (钉钉) as a chatbot, letting you chat with your AI assistant through direct messages or group chats. The bot connects via DingTalk's Stream Mode — a long-lived WebSocket connection that requires no public URL or webhook server — and replies using markdown-formatted messages through DingTalk's session webhook API.
|
||||
|
||||
Before setup, here's the part most people want to know: how Hermes behaves once it's in your DingTalk workspace.
|
||||
|
||||
## How Hermes Behaves
|
||||
|
||||
| Context | Behavior |
|
||||
|---------|----------|
|
||||
| **DMs (1:1 chat)** | Hermes responds to every message. No `@mention` needed. Each DM has its own session. |
|
||||
| **Group chats** | Hermes responds when you `@mention` it. Without a mention, Hermes ignores the message. |
|
||||
| **Shared groups with multiple users** | By default, Hermes isolates session history per user inside the group. Two people talking in the same group do not share one transcript unless you explicitly disable that. |
|
||||
|
||||
### Session Model in DingTalk
|
||||
|
||||
By default:
|
||||
|
||||
- each DM gets its own session
|
||||
- each user in a shared group chat gets their own session inside that group
|
||||
|
||||
This is controlled by `config.yaml`:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
Set it to `false` only if you explicitly want one shared conversation for the entire group:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: false
|
||||
```
|
||||
|
||||
This guide walks you through the full setup process — from creating your DingTalk bot to sending your first message.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Install the required Python packages:
|
||||
|
||||
```bash
|
||||
pip install dingtalk-stream httpx
|
||||
```
|
||||
|
||||
- `dingtalk-stream` — DingTalk's official SDK for Stream Mode (WebSocket-based real-time messaging)
|
||||
- `httpx` — async HTTP client used for sending replies via session webhooks
|
||||
|
||||
## Step 1: Create a DingTalk App
|
||||
|
||||
1. Go to the [DingTalk Developer Console](https://open-dev.dingtalk.com/).
|
||||
2. Log in with your DingTalk admin account.
|
||||
3. Click **Application Development** → **Custom Apps** → **Create App via H5 Micro-App** (or **Robot** depending on your console version).
|
||||
4. Fill in:
|
||||
- **App Name**: e.g., `Hermes Agent`
|
||||
- **Description**: optional
|
||||
5. After creating, navigate to **Credentials & Basic Info** to find your **Client ID** (AppKey) and **Client Secret** (AppSecret). Copy both.
|
||||
|
||||
:::warning[Credentials shown only once]
|
||||
The Client Secret is only displayed once when you create the app. If you lose it, you'll need to regenerate it. Never share these credentials publicly or commit them to Git.
|
||||
:::
|
||||
|
||||
## Step 2: Enable the Robot Capability
|
||||
|
||||
1. In your app's settings page, go to **Add Capability** → **Robot**.
|
||||
2. Enable the robot capability.
|
||||
3. Under **Message Reception Mode**, select **Stream Mode** (recommended — no public URL needed).
|
||||
|
||||
:::tip
|
||||
Stream Mode is the recommended setup. It uses a long-lived WebSocket connection initiated from your machine, so you don't need a public IP, domain name, or webhook endpoint. This works behind NAT, firewalls, and on local machines.
|
||||
:::
|
||||
|
||||
## Step 3: Find Your DingTalk User ID
|
||||
|
||||
Hermes Agent uses your DingTalk User ID to control who can interact with the bot. DingTalk User IDs are alphanumeric strings set by your organization's admin.
|
||||
|
||||
To find yours:
|
||||
|
||||
1. Ask your DingTalk organization admin — User IDs are configured in the DingTalk admin console under **Contacts** → **Members**.
|
||||
2. Alternatively, the bot logs the `sender_id` for each incoming message. Start the gateway, send the bot a message, then check the logs for your ID.
|
||||
|
||||
## Step 4: Configure Hermes Agent
|
||||
|
||||
### Option A: Interactive Setup (Recommended)
|
||||
|
||||
Run the guided setup command:
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Select **DingTalk** when prompted, then paste your Client ID, Client Secret, and allowed user IDs when asked.
|
||||
|
||||
### Option B: Manual Configuration
|
||||
|
||||
Add the following to your `~/.hermes/.env` file:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
DINGTALK_CLIENT_ID=your-app-key
|
||||
DINGTALK_CLIENT_SECRET=your-app-secret
|
||||
|
||||
# Security: restrict who can interact with the bot
|
||||
DINGTALK_ALLOWED_USERS=user-id-1
|
||||
|
||||
# Multiple allowed users (comma-separated)
|
||||
# DINGTALK_ALLOWED_USERS=user-id-1,user-id-2
|
||||
```
|
||||
|
||||
Optional behavior settings in `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
- `group_sessions_per_user: true` keeps each participant's context isolated inside shared group chats
|
||||
|
||||
### Start the Gateway
|
||||
|
||||
Once configured, start the DingTalk gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
The bot should connect to DingTalk's Stream Mode within a few seconds. Send it a message — either a DM or in a group where it's been added — to test.
|
||||
|
||||
:::tip
|
||||
You can run `hermes gateway` in the background or as a systemd service for persistent operation. See the deployment docs for details.
|
||||
:::
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Bot is not responding to messages
|
||||
|
||||
**Cause**: The robot capability isn't enabled, or `DINGTALK_ALLOWED_USERS` doesn't include your User ID.
|
||||
|
||||
**Fix**: Verify the robot capability is enabled in your app settings and that Stream Mode is selected. Check that your User ID is in `DINGTALK_ALLOWED_USERS`. Restart the gateway.
|
||||
|
||||
### "dingtalk-stream not installed" error
|
||||
|
||||
**Cause**: The `dingtalk-stream` Python package is not installed.
|
||||
|
||||
**Fix**: Install it:
|
||||
|
||||
```bash
|
||||
pip install dingtalk-stream httpx
|
||||
```
|
||||
|
||||
### "DINGTALK_CLIENT_ID and DINGTALK_CLIENT_SECRET required"
|
||||
|
||||
**Cause**: The credentials aren't set in your environment or `.env` file.
|
||||
|
||||
**Fix**: Verify `DINGTALK_CLIENT_ID` and `DINGTALK_CLIENT_SECRET` are set correctly in `~/.hermes/.env`. The Client ID is your AppKey, and the Client Secret is your AppSecret from the DingTalk Developer Console.
|
||||
|
||||
### Stream disconnects / reconnection loops
|
||||
|
||||
**Cause**: Network instability, DingTalk platform maintenance, or credential issues.
|
||||
|
||||
**Fix**: The adapter automatically reconnects with exponential backoff (2s → 5s → 10s → 30s → 60s). Check that your credentials are valid and your app hasn't been deactivated. Verify your network allows outbound WebSocket connections.
|
||||
|
||||
### Bot is offline
|
||||
|
||||
**Cause**: The Hermes gateway isn't running, or it failed to connect.
|
||||
|
||||
**Fix**: Check that `hermes gateway` is running. Look at the terminal output for error messages. Common issues: wrong credentials, app deactivated, `dingtalk-stream` or `httpx` not installed.
|
||||
|
||||
### "No session_webhook available"
|
||||
|
||||
**Cause**: The bot tried to reply but doesn't have a session webhook URL. This typically happens if the webhook expired or the bot was restarted between receiving the message and sending the reply.
|
||||
|
||||
**Fix**: Send a new message to the bot — each incoming message provides a fresh session webhook for replies. This is a normal DingTalk limitation; the bot can only reply to messages it has received recently.
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
Always set `DINGTALK_ALLOWED_USERS` to restrict who can interact with the bot. Without it, the gateway denies all users by default as a safety measure. Only add User IDs of people you trust — authorized users have full access to the agent's capabilities, including tool use and system access.
|
||||
:::
|
||||
|
||||
For more information on securing your Hermes Agent deployment, see the [Security Guide](../security.md).
|
||||
|
||||
## Notes
|
||||
|
||||
- **Stream Mode**: No public URL, domain name, or webhook server needed. The connection is initiated from your machine via WebSocket, so it works behind NAT and firewalls.
|
||||
- **Markdown responses**: Replies are formatted in DingTalk's markdown format for rich text display.
|
||||
- **Message deduplication**: The adapter deduplicates messages with a 5-minute window to prevent processing the same message twice.
|
||||
- **Auto-reconnection**: If the stream connection drops, the adapter automatically reconnects with exponential backoff.
|
||||
- **Message length limit**: Responses are capped at 20,000 characters per message. Longer responses are truncated.
|
||||
363
hermes_code/website/docs/user-guide/messaging/discord.md
Normal file
363
hermes_code/website/docs/user-guide/messaging/discord.md
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
---
|
||||
sidebar_position: 3
|
||||
title: "Discord"
|
||||
description: "Set up Hermes Agent as a Discord bot"
|
||||
---
|
||||
|
||||
# Discord Setup
|
||||
|
||||
Hermes Agent integrates with Discord as a bot, letting you chat with your AI assistant through direct messages or server channels. The bot receives your messages, processes them through the Hermes Agent pipeline (including tool use, memory, and reasoning), and responds in real time. It supports text, voice messages, file attachments, and slash commands.
|
||||
|
||||
Before setup, here's the part most people want to know: how Hermes behaves once it's in your server.
|
||||
|
||||
## How Hermes Behaves
|
||||
|
||||
| Context | Behavior |
|
||||
|---------|----------|
|
||||
| **DMs** | Hermes responds to every message. No `@mention` needed. Each DM has its own session. |
|
||||
| **Server channels** | By default, Hermes only responds when you `@mention` it. If you post in a channel without mentioning it, Hermes ignores the message. |
|
||||
| **Free-response channels** | You can make specific channels mention-free with `DISCORD_FREE_RESPONSE_CHANNELS`, or disable mentions globally with `DISCORD_REQUIRE_MENTION=false`. |
|
||||
| **Threads** | Hermes replies in the same thread. Mention rules still apply unless that thread or its parent channel is configured as free-response. Threads stay isolated from the parent channel for session history. |
|
||||
| **Shared channels with multiple users** | By default, Hermes isolates session history per user inside the channel for safety and clarity. Two people talking in the same channel do not share one transcript unless you explicitly disable that. |
|
||||
|
||||
:::tip
|
||||
If you want a normal bot-help channel where people can talk to Hermes without tagging it every time, add that channel to `DISCORD_FREE_RESPONSE_CHANNELS`.
|
||||
:::
|
||||
|
||||
### Discord Gateway Model
|
||||
|
||||
Hermes on Discord is not a webhook that replies statelessly. It runs through the full messaging gateway, which means each incoming message goes through:
|
||||
|
||||
1. authorization (`DISCORD_ALLOWED_USERS`)
|
||||
2. mention / free-response checks
|
||||
3. session lookup
|
||||
4. session transcript loading
|
||||
5. normal Hermes agent execution, including tools, memory, and slash commands
|
||||
6. response delivery back to Discord
|
||||
|
||||
That matters because behavior in a busy server depends on both Discord routing and Hermes session policy.
|
||||
|
||||
### Session Model in Discord
|
||||
|
||||
By default:
|
||||
|
||||
- each DM gets its own session
|
||||
- each server thread gets its own session namespace
|
||||
- each user in a shared channel gets their own session inside that channel
|
||||
|
||||
So if Alice and Bob both talk to Hermes in `#research`, Hermes treats those as separate conversations by default even though they are using the same visible Discord channel.
|
||||
|
||||
This is controlled by `config.yaml`:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
Set it to `false` only if you explicitly want one shared conversation for the entire room:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: false
|
||||
```
|
||||
|
||||
Shared sessions can be useful for a collaborative room, but they also mean:
|
||||
|
||||
- users share context growth and token costs
|
||||
- one person's long tool-heavy task can bloat everyone else's context
|
||||
- one person's in-flight run can interrupt another person's follow-up in the same room
|
||||
|
||||
### Interrupts and Concurrency
|
||||
|
||||
Hermes tracks running agents by session key.
|
||||
|
||||
With the default `group_sessions_per_user: true`:
|
||||
|
||||
- Alice interrupting her own in-flight request only affects Alice's session in that channel
|
||||
- Bob can keep talking in the same channel without inheriting Alice's history or interrupting Alice's run
|
||||
|
||||
With `group_sessions_per_user: false`:
|
||||
|
||||
- the whole room shares one running-agent slot for that channel/thread
|
||||
- follow-up messages from different people can interrupt or queue behind each other
|
||||
|
||||
This guide walks you through the full setup process — from creating your bot on Discord's Developer Portal to sending your first message.
|
||||
|
||||
## Step 1: Create a Discord Application
|
||||
|
||||
1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) and sign in with your Discord account.
|
||||
2. Click **New Application** in the top-right corner.
|
||||
3. Enter a name for your application (e.g., "Hermes Agent") and accept the Developer Terms of Service.
|
||||
4. Click **Create**.
|
||||
|
||||
You'll land on the **General Information** page. Note the **Application ID** — you'll need it later to build the invite URL.
|
||||
|
||||
## Step 2: Create the Bot
|
||||
|
||||
1. In the left sidebar, click **Bot**.
|
||||
2. Discord automatically creates a bot user for your application. You'll see the bot's username, which you can customize.
|
||||
3. Under **Authorization Flow**:
|
||||
- Set **Public Bot** to **OFF** — this prevents other people from inviting your bot to their servers.
|
||||
- Leave **Require OAuth2 Code Grant** set to **OFF**.
|
||||
|
||||
:::tip
|
||||
You can set a custom avatar and banner for your bot on this page. This is what users will see in Discord.
|
||||
:::
|
||||
|
||||
## Step 3: Enable Privileged Gateway Intents
|
||||
|
||||
This is the most critical step in the entire setup. Without the correct intents enabled, your bot will connect to Discord but **will not be able to read message content**.
|
||||
|
||||
On the **Bot** page, scroll down to **Privileged Gateway Intents**. You'll see three toggles:
|
||||
|
||||
| Intent | Purpose | Required? |
|
||||
|--------|---------|-----------|
|
||||
| **Presence Intent** | See user online/offline status | Optional |
|
||||
| **Server Members Intent** | Access the member list, resolve usernames | **Required** |
|
||||
| **Message Content Intent** | Read the text content of messages | **Required** |
|
||||
|
||||
**Enable both Server Members Intent and Message Content Intent** by toggling them **ON**.
|
||||
|
||||
- Without **Message Content Intent**, your bot receives message events but the message text is empty — the bot literally cannot see what you typed.
|
||||
- Without **Server Members Intent**, the bot cannot resolve usernames for the allowed users list and may fail to identify who is messaging it.
|
||||
|
||||
:::warning[This is the #1 reason Discord bots don't work]
|
||||
If your bot is online but never responds to messages, the **Message Content Intent** is almost certainly disabled. Go back to the [Developer Portal](https://discord.com/developers/applications), select your application → Bot → Privileged Gateway Intents, and make sure **Message Content Intent** is toggled ON. Click **Save Changes**.
|
||||
:::
|
||||
|
||||
**Regarding server count:**
|
||||
- If your bot is in **fewer than 100 servers**, you can simply toggle intents on and off freely.
|
||||
- If your bot is in **100 or more servers**, Discord requires you to submit a verification application to use privileged intents. For personal use, this is not a concern.
|
||||
|
||||
Click **Save Changes** at the bottom of the page.
|
||||
|
||||
## Step 4: Get the Bot Token
|
||||
|
||||
The bot token is the credential Hermes Agent uses to log in as your bot. Still on the **Bot** page:
|
||||
|
||||
1. Under the **Token** section, click **Reset Token**.
|
||||
2. If you have two-factor authentication enabled on your Discord account, enter your 2FA code.
|
||||
3. Discord will display your new token. **Copy it immediately.**
|
||||
|
||||
:::warning[Token shown only once]
|
||||
The token is only displayed once. If you lose it, you'll need to reset it and generate a new one. Never share your token publicly or commit it to Git — anyone with this token has full control of your bot.
|
||||
:::
|
||||
|
||||
Store the token somewhere safe (a password manager, for example). You'll need it in Step 8.
|
||||
|
||||
## Step 5: Generate the Invite URL
|
||||
|
||||
You need an OAuth2 URL to invite the bot to your server. There are two ways to do this:
|
||||
|
||||
### Option A: Using the Installation Tab (Recommended)
|
||||
|
||||
1. In the left sidebar, click **Installation**.
|
||||
2. Under **Installation Contexts**, enable **Guild Install**.
|
||||
3. For **Install Link**, select **Discord Provided Link**.
|
||||
4. Under **Default Install Settings** for Guild Install:
|
||||
- **Scopes**: select `bot` and `applications.commands`
|
||||
- **Permissions**: select the permissions listed below.
|
||||
|
||||
### Option B: Manual URL
|
||||
|
||||
You can construct the invite URL directly using this format:
|
||||
|
||||
```
|
||||
https://discord.com/oauth2/authorize?client_id=YOUR_APP_ID&scope=bot+applications.commands&permissions=274878286912
|
||||
```
|
||||
|
||||
Replace `YOUR_APP_ID` with the Application ID from Step 1.
|
||||
|
||||
### Required Permissions
|
||||
|
||||
These are the minimum permissions your bot needs:
|
||||
|
||||
- **View Channels** — see the channels it has access to
|
||||
- **Send Messages** — respond to your messages
|
||||
- **Embed Links** — format rich responses
|
||||
- **Attach Files** — send images, audio, and file outputs
|
||||
- **Read Message History** — maintain conversation context
|
||||
|
||||
### Recommended Additional Permissions
|
||||
|
||||
- **Send Messages in Threads** — respond in thread conversations
|
||||
- **Add Reactions** — react to messages for acknowledgment
|
||||
|
||||
### Permission Integers
|
||||
|
||||
| Level | Permissions Integer | What's Included |
|
||||
|-------|-------------------|-----------------|
|
||||
| Minimal | `117760` | View Channels, Send Messages, Read Message History, Attach Files |
|
||||
| Recommended | `274878286912` | All of the above plus Embed Links, Send Messages in Threads, Add Reactions |
|
||||
|
||||
## Step 6: Invite to Your Server
|
||||
|
||||
1. Open the invite URL in your browser (from the Installation tab or the manual URL you constructed).
|
||||
2. In the **Add to Server** dropdown, select your server.
|
||||
3. Click **Continue**, then **Authorize**.
|
||||
4. Complete the CAPTCHA if prompted.
|
||||
|
||||
:::info
|
||||
You need the **Manage Server** permission on the Discord server to invite a bot. If you don't see your server in the dropdown, ask a server admin to use the invite link instead.
|
||||
:::
|
||||
|
||||
After authorizing, the bot will appear in your server's member list (it will show as offline until you start the Hermes gateway).
|
||||
|
||||
## Step 7: Find Your Discord User ID
|
||||
|
||||
Hermes Agent uses your Discord User ID to control who can interact with the bot. To find it:
|
||||
|
||||
1. Open Discord (desktop or web app).
|
||||
2. Go to **Settings** → **Advanced** → toggle **Developer Mode** to **ON**.
|
||||
3. Close settings.
|
||||
4. Right-click your own username (in a message, the member list, or your profile) → **Copy User ID**.
|
||||
|
||||
Your User ID is a long number like `284102345871466496`.
|
||||
|
||||
:::tip
|
||||
Developer Mode also lets you copy **Channel IDs** and **Server IDs** the same way — right-click the channel or server name and select Copy ID. You'll need a Channel ID if you want to set a home channel manually.
|
||||
:::
|
||||
|
||||
## Step 8: Configure Hermes Agent
|
||||
|
||||
### Option A: Interactive Setup (Recommended)
|
||||
|
||||
Run the guided setup command:
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Select **Discord** when prompted, then paste your bot token and user ID when asked.
|
||||
|
||||
### Option B: Manual Configuration
|
||||
|
||||
Add the following to your `~/.hermes/.env` file:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
DISCORD_BOT_TOKEN=your-bot-token
|
||||
DISCORD_ALLOWED_USERS=284102345871466496
|
||||
|
||||
# Multiple allowed users (comma-separated)
|
||||
# DISCORD_ALLOWED_USERS=284102345871466496,198765432109876543
|
||||
|
||||
# Optional: respond without @mention (default: true = require mention)
|
||||
# DISCORD_REQUIRE_MENTION=false
|
||||
|
||||
# Optional: channels where bot responds without @mention (comma-separated channel IDs)
|
||||
# DISCORD_FREE_RESPONSE_CHANNELS=1234567890,9876543210
|
||||
```
|
||||
|
||||
Optional behavior settings in `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
discord:
|
||||
require_mention: true
|
||||
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
- `discord.require_mention: true` keeps Hermes quiet in normal server traffic unless mentioned
|
||||
- `group_sessions_per_user: true` keeps each participant's context isolated inside shared channels and threads
|
||||
|
||||
### Start the Gateway
|
||||
|
||||
Once configured, start the Discord gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
The bot should come online in Discord within a few seconds. Send it a message — either a DM or in a channel it can see — to test.
|
||||
|
||||
:::tip
|
||||
You can run `hermes gateway` in the background or as a systemd service for persistent operation. See the deployment docs for details.
|
||||
:::
|
||||
|
||||
## Home Channel
|
||||
|
||||
You can designate a "home channel" where the bot sends proactive messages (such as cron job output, reminders, and notifications). There are two ways to set it:
|
||||
|
||||
### Using the Slash Command
|
||||
|
||||
Type `/sethome` in any Discord channel where the bot is present. That channel becomes the home channel.
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
Add these to your `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
DISCORD_HOME_CHANNEL=123456789012345678
|
||||
DISCORD_HOME_CHANNEL_NAME="#bot-updates"
|
||||
```
|
||||
|
||||
Replace the ID with the actual channel ID (right-click → Copy Channel ID with Developer Mode on).
|
||||
|
||||
## Voice Messages
|
||||
|
||||
Hermes Agent supports Discord voice messages:
|
||||
|
||||
- **Incoming voice messages** are automatically transcribed using the configured STT provider: local `faster-whisper` (no key), Groq Whisper (`GROQ_API_KEY`), or OpenAI Whisper (`VOICE_TOOLS_OPENAI_KEY`).
|
||||
- **Text-to-speech**: Use `/voice tts` to have the bot send spoken audio responses alongside text replies.
|
||||
- **Discord voice channels**: Hermes can also join a voice channel, listen to users speaking, and talk back in the channel.
|
||||
|
||||
For the full setup and operational guide, see:
|
||||
- [Voice Mode](/docs/user-guide/features/voice-mode)
|
||||
- [Use Voice Mode with Hermes](/docs/guides/use-voice-mode-with-hermes)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Bot is online but not responding to messages
|
||||
|
||||
**Cause**: Message Content Intent is disabled.
|
||||
|
||||
**Fix**: Go to [Developer Portal](https://discord.com/developers/applications) → your app → Bot → Privileged Gateway Intents → enable **Message Content Intent** → Save Changes. Restart the gateway.
|
||||
|
||||
### "Disallowed Intents" error on startup
|
||||
|
||||
**Cause**: Your code requests intents that aren't enabled in the Developer Portal.
|
||||
|
||||
**Fix**: Enable all three Privileged Gateway Intents (Presence, Server Members, Message Content) in the Bot settings, then restart.
|
||||
|
||||
### Bot can't see messages in a specific channel
|
||||
|
||||
**Cause**: The bot's role doesn't have permission to view that channel.
|
||||
|
||||
**Fix**: In Discord, go to the channel's settings → Permissions → add the bot's role with **View Channel** and **Read Message History** enabled.
|
||||
|
||||
### 403 Forbidden errors
|
||||
|
||||
**Cause**: The bot is missing required permissions.
|
||||
|
||||
**Fix**: Re-invite the bot with the correct permissions using the URL from Step 5, or manually adjust the bot's role permissions in Server Settings → Roles.
|
||||
|
||||
### Bot is offline
|
||||
|
||||
**Cause**: The Hermes gateway isn't running, or the token is incorrect.
|
||||
|
||||
**Fix**: Check that `hermes gateway` is running. Verify `DISCORD_BOT_TOKEN` in your `.env` file. If you recently reset the token, update it.
|
||||
|
||||
### "User not allowed" / Bot ignores you
|
||||
|
||||
**Cause**: Your User ID isn't in `DISCORD_ALLOWED_USERS`.
|
||||
|
||||
**Fix**: Add your User ID to `DISCORD_ALLOWED_USERS` in `~/.hermes/.env` and restart the gateway.
|
||||
|
||||
### People in the same channel are sharing context unexpectedly
|
||||
|
||||
**Cause**: `group_sessions_per_user` is disabled, or the platform cannot provide a user ID for the messages in that context.
|
||||
|
||||
**Fix**: Set this in `~/.hermes/config.yaml` and restart the gateway:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
If you intentionally want a shared room conversation, leave it off — just expect shared transcript history and shared interrupt behavior.
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
Always set `DISCORD_ALLOWED_USERS` to restrict who can interact with the bot. Without it, the gateway denies all users by default as a safety measure. Only add User IDs of people you trust — authorized users have full access to the agent's capabilities, including tool use and system access.
|
||||
:::
|
||||
|
||||
For more information on securing your Hermes Agent deployment, see the [Security Guide](../security.md).
|
||||
189
hermes_code/website/docs/user-guide/messaging/email.md
Normal file
189
hermes_code/website/docs/user-guide/messaging/email.md
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
---
|
||||
sidebar_position: 7
|
||||
title: "Email"
|
||||
description: "Set up Hermes Agent as an email assistant via IMAP/SMTP"
|
||||
---
|
||||
|
||||
# Email Setup
|
||||
|
||||
Hermes can receive and reply to emails using standard IMAP and SMTP protocols. Send an email to the agent's address and it replies in-thread — no special client or bot API needed. Works with Gmail, Outlook, Yahoo, Fastmail, or any provider that supports IMAP/SMTP.
|
||||
|
||||
:::info No External Dependencies
|
||||
The Email adapter uses Python's built-in `imaplib`, `smtplib`, and `email` modules. No additional packages or external services are required.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **A dedicated email account** for your Hermes agent (don't use your personal email)
|
||||
- **IMAP enabled** on the email account
|
||||
- **An app password** if using Gmail or another provider with 2FA
|
||||
|
||||
### Gmail Setup
|
||||
|
||||
1. Enable 2-Factor Authentication on your Google Account
|
||||
2. Go to [App Passwords](https://myaccount.google.com/apppasswords)
|
||||
3. Create a new App Password (select "Mail" or "Other")
|
||||
4. Copy the 16-character password — you'll use this instead of your regular password
|
||||
|
||||
### Outlook / Microsoft 365
|
||||
|
||||
1. Go to [Security Settings](https://account.microsoft.com/security)
|
||||
2. Enable 2FA if not already active
|
||||
3. Create an App Password under "Additional security options"
|
||||
4. IMAP host: `outlook.office365.com`, SMTP host: `smtp.office365.com`
|
||||
|
||||
### Other Providers
|
||||
|
||||
Most email providers support IMAP/SMTP. Check your provider's documentation for:
|
||||
- IMAP host and port (usually port 993 with SSL)
|
||||
- SMTP host and port (usually port 587 with STARTTLS)
|
||||
- Whether app passwords are required
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Configure Hermes
|
||||
|
||||
The easiest way:
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Select **Email** from the platform menu. The wizard prompts for your email address, password, IMAP/SMTP hosts, and allowed senders.
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
Add to `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
EMAIL_ADDRESS=hermes@gmail.com
|
||||
EMAIL_PASSWORD=abcd efgh ijkl mnop # App password (not your regular password)
|
||||
EMAIL_IMAP_HOST=imap.gmail.com
|
||||
EMAIL_SMTP_HOST=smtp.gmail.com
|
||||
|
||||
# Security (recommended)
|
||||
EMAIL_ALLOWED_USERS=your@email.com,colleague@work.com
|
||||
|
||||
# Optional
|
||||
EMAIL_IMAP_PORT=993 # Default: 993 (IMAP SSL)
|
||||
EMAIL_SMTP_PORT=587 # Default: 587 (SMTP STARTTLS)
|
||||
EMAIL_POLL_INTERVAL=15 # Seconds between inbox checks (default: 15)
|
||||
EMAIL_HOME_ADDRESS=your@email.com # Default delivery target for cron jobs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Start the Gateway
|
||||
|
||||
```bash
|
||||
hermes gateway # Run in foreground
|
||||
hermes gateway install # Install as a user service
|
||||
sudo hermes gateway install --system # Linux only: boot-time system service
|
||||
```
|
||||
|
||||
On startup, the adapter:
|
||||
1. Tests IMAP and SMTP connections
|
||||
2. Marks all existing inbox messages as "seen" (only processes new emails)
|
||||
3. Starts polling for new messages
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
### Receiving Messages
|
||||
|
||||
The adapter polls the IMAP inbox for UNSEEN messages at a configurable interval (default: 15 seconds). For each new email:
|
||||
|
||||
- **Subject line** is included as context (e.g., `[Subject: Deploy to production]`)
|
||||
- **Reply emails** (subject starting with `Re:`) skip the subject prefix — the thread context is already established
|
||||
- **Attachments** are cached locally:
|
||||
- Images (JPEG, PNG, GIF, WebP) → available to the vision tool
|
||||
- Documents (PDF, ZIP, etc.) → available for file access
|
||||
- **HTML-only emails** have tags stripped for plain text extraction
|
||||
- **Self-messages** are filtered out to prevent reply loops
|
||||
|
||||
### Sending Replies
|
||||
|
||||
Replies are sent via SMTP with proper email threading:
|
||||
|
||||
- **In-Reply-To** and **References** headers maintain the thread
|
||||
- **Subject line** preserved with `Re:` prefix (no double `Re: Re:`)
|
||||
- **Message-ID** generated with the agent's domain
|
||||
- Responses are sent as plain text (UTF-8)
|
||||
|
||||
### File Attachments
|
||||
|
||||
The agent can send file attachments in replies. Include `MEDIA:/path/to/file` in the response and the file is attached to the outgoing email.
|
||||
|
||||
### Skipping Attachments
|
||||
|
||||
To ignore all incoming attachments (for malware protection or bandwidth savings), add to your `config.yaml`:
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
email:
|
||||
skip_attachments: true
|
||||
```
|
||||
|
||||
When enabled, attachment and inline parts are skipped before payload decoding. The email body text is still processed normally.
|
||||
|
||||
---
|
||||
|
||||
## Access Control
|
||||
|
||||
Email access follows the same pattern as all other Hermes platforms:
|
||||
|
||||
1. **`EMAIL_ALLOWED_USERS` set** → only emails from those addresses are processed
|
||||
2. **No allowlist set** → unknown senders get a pairing code
|
||||
3. **`EMAIL_ALLOW_ALL_USERS=true`** → any sender is accepted (use with caution)
|
||||
|
||||
:::warning
|
||||
**Always configure `EMAIL_ALLOWED_USERS`.** Without it, anyone who knows the agent's email address could send commands. The agent has terminal access by default.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| **"IMAP connection failed"** at startup | Verify `EMAIL_IMAP_HOST` and `EMAIL_IMAP_PORT`. Ensure IMAP is enabled on the account. For Gmail, enable it in Settings → Forwarding and POP/IMAP. |
|
||||
| **"SMTP connection failed"** at startup | Verify `EMAIL_SMTP_HOST` and `EMAIL_SMTP_PORT`. Check that your password is correct (use App Password for Gmail). |
|
||||
| **Messages not received** | Check `EMAIL_ALLOWED_USERS` includes the sender's email. Check spam folder — some providers flag automated replies. |
|
||||
| **"Authentication failed"** | For Gmail, you must use an App Password, not your regular password. Ensure 2FA is enabled first. |
|
||||
| **Duplicate replies** | Ensure only one gateway instance is running. Check `hermes gateway status`. |
|
||||
| **Slow response** | The default poll interval is 15 seconds. Reduce with `EMAIL_POLL_INTERVAL=5` for faster response (but more IMAP connections). |
|
||||
| **Replies not threading** | The adapter uses In-Reply-To headers. Some email clients (especially web-based) may not thread correctly with automated messages. |
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
**Use a dedicated email account.** Don't use your personal email — the agent stores the password in `.env` and has full inbox access via IMAP.
|
||||
:::
|
||||
|
||||
- Use **App Passwords** instead of your main password (required for Gmail with 2FA)
|
||||
- Set `EMAIL_ALLOWED_USERS` to restrict who can interact with the agent
|
||||
- The password is stored in `~/.hermes/.env` — protect this file (`chmod 600`)
|
||||
- IMAP uses SSL (port 993) and SMTP uses STARTTLS (port 587) by default — connections are encrypted
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Reference
|
||||
|
||||
| Variable | Required | Default | Description |
|
||||
|----------|----------|---------|-------------|
|
||||
| `EMAIL_ADDRESS` | Yes | — | Agent's email address |
|
||||
| `EMAIL_PASSWORD` | Yes | — | Email password or app password |
|
||||
| `EMAIL_IMAP_HOST` | Yes | — | IMAP server host (e.g., `imap.gmail.com`) |
|
||||
| `EMAIL_SMTP_HOST` | Yes | — | SMTP server host (e.g., `smtp.gmail.com`) |
|
||||
| `EMAIL_IMAP_PORT` | No | `993` | IMAP server port |
|
||||
| `EMAIL_SMTP_PORT` | No | `587` | SMTP server port |
|
||||
| `EMAIL_POLL_INTERVAL` | No | `15` | Seconds between inbox checks |
|
||||
| `EMAIL_ALLOWED_USERS` | No | — | Comma-separated allowed sender addresses |
|
||||
| `EMAIL_HOME_ADDRESS` | No | — | Default delivery target for cron jobs |
|
||||
| `EMAIL_ALLOW_ALL_USERS` | No | `false` | Allow all senders (not recommended) |
|
||||
249
hermes_code/website/docs/user-guide/messaging/homeassistant.md
Normal file
249
hermes_code/website/docs/user-guide/messaging/homeassistant.md
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
---
|
||||
title: Home Assistant
|
||||
description: Control your smart home with Hermes Agent via Home Assistant integration.
|
||||
sidebar_label: Home Assistant
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Home Assistant Integration
|
||||
|
||||
Hermes Agent integrates with [Home Assistant](https://www.home-assistant.io/) in two ways:
|
||||
|
||||
1. **Gateway platform** — subscribes to real-time state changes via WebSocket and responds to events
|
||||
2. **Smart home tools** — four LLM-callable tools for querying and controlling devices via the REST API
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Create a Long-Lived Access Token
|
||||
|
||||
1. Open your Home Assistant instance
|
||||
2. Go to your **Profile** (click your name in the sidebar)
|
||||
3. Scroll to **Long-Lived Access Tokens**
|
||||
4. Click **Create Token**, give it a name like "Hermes Agent"
|
||||
5. Copy the token
|
||||
|
||||
### 2. Configure Environment Variables
|
||||
|
||||
```bash
|
||||
# Add to ~/.hermes/.env
|
||||
|
||||
# Required: your Long-Lived Access Token
|
||||
HASS_TOKEN=your-long-lived-access-token
|
||||
|
||||
# Optional: HA URL (default: http://homeassistant.local:8123)
|
||||
HASS_URL=http://192.168.1.100:8123
|
||||
```
|
||||
|
||||
:::info
|
||||
The `homeassistant` toolset is automatically enabled when `HASS_TOKEN` is set. Both the gateway platform and the device control tools activate from this single token.
|
||||
:::
|
||||
|
||||
### 3. Start the Gateway
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
Home Assistant will appear as a connected platform alongside any other messaging platforms (Telegram, Discord, etc.).
|
||||
|
||||
## Available Tools
|
||||
|
||||
Hermes Agent registers four tools for smart home control:
|
||||
|
||||
### `ha_list_entities`
|
||||
|
||||
List Home Assistant entities, optionally filtered by domain or area.
|
||||
|
||||
**Parameters:**
|
||||
- `domain` *(optional)* — Filter by entity domain: `light`, `switch`, `climate`, `sensor`, `binary_sensor`, `cover`, `fan`, `media_player`, etc.
|
||||
- `area` *(optional)* — Filter by area/room name (matches against friendly names): `living room`, `kitchen`, `bedroom`, etc.
|
||||
|
||||
**Example:**
|
||||
```
|
||||
List all lights in the living room
|
||||
```
|
||||
|
||||
Returns entity IDs, states, and friendly names.
|
||||
|
||||
### `ha_get_state`
|
||||
|
||||
Get detailed state of a single entity, including all attributes (brightness, color, temperature setpoint, sensor readings, etc.).
|
||||
|
||||
**Parameters:**
|
||||
- `entity_id` *(required)* — The entity to query, e.g., `light.living_room`, `climate.thermostat`, `sensor.temperature`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
What's the current state of climate.thermostat?
|
||||
```
|
||||
|
||||
Returns: state, all attributes, last changed/updated timestamps.
|
||||
|
||||
### `ha_list_services`
|
||||
|
||||
List available services (actions) for device control. Shows what actions can be performed on each device type and what parameters they accept.
|
||||
|
||||
**Parameters:**
|
||||
- `domain` *(optional)* — Filter by domain, e.g., `light`, `climate`, `switch`
|
||||
|
||||
**Example:**
|
||||
```
|
||||
What services are available for climate devices?
|
||||
```
|
||||
|
||||
### `ha_call_service`
|
||||
|
||||
Call a Home Assistant service to control a device.
|
||||
|
||||
**Parameters:**
|
||||
- `domain` *(required)* — Service domain: `light`, `switch`, `climate`, `cover`, `media_player`, `fan`, `scene`, `script`
|
||||
- `service` *(required)* — Service name: `turn_on`, `turn_off`, `toggle`, `set_temperature`, `set_hvac_mode`, `open_cover`, `close_cover`, `set_volume_level`
|
||||
- `entity_id` *(optional)* — Target entity, e.g., `light.living_room`
|
||||
- `data` *(optional)* — Additional parameters as a JSON object
|
||||
|
||||
**Examples:**
|
||||
|
||||
```
|
||||
Turn on the living room lights
|
||||
→ ha_call_service(domain="light", service="turn_on", entity_id="light.living_room")
|
||||
```
|
||||
|
||||
```
|
||||
Set the thermostat to 22 degrees in heat mode
|
||||
→ ha_call_service(domain="climate", service="set_temperature",
|
||||
entity_id="climate.thermostat", data={"temperature": 22, "hvac_mode": "heat"})
|
||||
```
|
||||
|
||||
```
|
||||
Set living room lights to blue at 50% brightness
|
||||
→ ha_call_service(domain="light", service="turn_on",
|
||||
entity_id="light.living_room", data={"brightness": 128, "color_name": "blue"})
|
||||
```
|
||||
|
||||
## Gateway Platform: Real-Time Events
|
||||
|
||||
The Home Assistant gateway adapter connects via WebSocket and subscribes to `state_changed` events. When a device state changes and matches your filters, it's forwarded to the agent as a message.
|
||||
|
||||
### Event Filtering
|
||||
|
||||
:::warning Required Configuration
|
||||
By default, **no events are forwarded**. You must configure at least one of `watch_domains`, `watch_entities`, or `watch_all` to receive events. Without filters, a warning is logged at startup and all state changes are silently dropped.
|
||||
:::
|
||||
|
||||
Configure which events the agent sees in `~/.hermes/gateway.json` under the Home Assistant platform's `extra` section:
|
||||
|
||||
```json
|
||||
{
|
||||
"platforms": {
|
||||
"homeassistant": {
|
||||
"enabled": true,
|
||||
"extra": {
|
||||
"watch_domains": ["climate", "binary_sensor", "alarm_control_panel", "light"],
|
||||
"watch_entities": ["sensor.front_door_battery"],
|
||||
"ignore_entities": ["sensor.uptime", "sensor.cpu_usage", "sensor.memory_usage"],
|
||||
"cooldown_seconds": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `watch_domains` | *(none)* | Only watch these entity domains (e.g., `climate`, `light`, `binary_sensor`) |
|
||||
| `watch_entities` | *(none)* | Only watch these specific entity IDs |
|
||||
| `watch_all` | `false` | Set to `true` to receive **all** state changes (not recommended for most setups) |
|
||||
| `ignore_entities` | *(none)* | Always ignore these entities (applied before domain/entity filters) |
|
||||
| `cooldown_seconds` | `30` | Minimum seconds between events for the same entity |
|
||||
|
||||
:::tip
|
||||
Start with a focused set of domains — `climate`, `binary_sensor`, and `alarm_control_panel` cover the most useful automations. Add more as needed. Use `ignore_entities` to suppress noisy sensors like CPU temperature or uptime counters.
|
||||
:::
|
||||
|
||||
### Event Formatting
|
||||
|
||||
State changes are formatted as human-readable messages based on domain:
|
||||
|
||||
| Domain | Format |
|
||||
|--------|--------|
|
||||
| `climate` | "HVAC mode changed from 'off' to 'heat' (current: 21, target: 23)" |
|
||||
| `sensor` | "changed from 21°C to 22°C" |
|
||||
| `binary_sensor` | "triggered" / "cleared" |
|
||||
| `light`, `switch`, `fan` | "turned on" / "turned off" |
|
||||
| `alarm_control_panel` | "alarm state changed from 'armed_away' to 'triggered'" |
|
||||
| *(other)* | "changed from 'old' to 'new'" |
|
||||
|
||||
### Agent Responses
|
||||
|
||||
Outbound messages from the agent are delivered as **Home Assistant persistent notifications** (via `persistent_notification.create`). These appear in the HA notification panel with the title "Hermes Agent".
|
||||
|
||||
### Connection Management
|
||||
|
||||
- **WebSocket** with 30-second heartbeat for real-time events
|
||||
- **Automatic reconnection** with backoff: 5s → 10s → 30s → 60s
|
||||
- **REST API** for outbound notifications (separate session to avoid WebSocket conflicts)
|
||||
- **Authorization** — HA events are always authorized (no user allowlist needed, since the `HASS_TOKEN` authenticates the connection)
|
||||
|
||||
## Security
|
||||
|
||||
The Home Assistant tools enforce security restrictions:
|
||||
|
||||
:::warning Blocked Domains
|
||||
The following service domains are **blocked** to prevent arbitrary code execution on the HA host:
|
||||
|
||||
- `shell_command` — arbitrary shell commands
|
||||
- `command_line` — sensors/switches that execute commands
|
||||
- `python_script` — scripted Python execution
|
||||
- `pyscript` — broader scripting integration
|
||||
- `hassio` — addon control, host shutdown/reboot
|
||||
- `rest_command` — HTTP requests from HA server (SSRF vector)
|
||||
|
||||
Attempting to call services in these domains returns an error.
|
||||
:::
|
||||
|
||||
Entity IDs are validated against the pattern `^[a-z_][a-z0-9_]*\.[a-z0-9_]+$` to prevent injection attacks.
|
||||
|
||||
## Example Automations
|
||||
|
||||
### Morning Routine
|
||||
|
||||
```
|
||||
User: Start my morning routine
|
||||
|
||||
Agent:
|
||||
1. ha_call_service(domain="light", service="turn_on",
|
||||
entity_id="light.bedroom", data={"brightness": 128})
|
||||
2. ha_call_service(domain="climate", service="set_temperature",
|
||||
entity_id="climate.thermostat", data={"temperature": 22})
|
||||
3. ha_call_service(domain="media_player", service="turn_on",
|
||||
entity_id="media_player.kitchen_speaker")
|
||||
```
|
||||
|
||||
### Security Check
|
||||
|
||||
```
|
||||
User: Is the house secure?
|
||||
|
||||
Agent:
|
||||
1. ha_list_entities(domain="binary_sensor")
|
||||
→ checks door/window sensors
|
||||
2. ha_get_state(entity_id="alarm_control_panel.home")
|
||||
→ checks alarm status
|
||||
3. ha_list_entities(domain="lock")
|
||||
→ checks lock states
|
||||
4. Reports: "All doors closed, alarm is armed_away, all locks engaged."
|
||||
```
|
||||
|
||||
### Reactive Automation (via Gateway Events)
|
||||
|
||||
When connected as a gateway platform, the agent can react to events:
|
||||
|
||||
```
|
||||
[Home Assistant] Front Door: triggered (was cleared)
|
||||
|
||||
Agent automatically:
|
||||
1. ha_get_state(entity_id="binary_sensor.front_door")
|
||||
2. ha_call_service(domain="light", service="turn_on",
|
||||
entity_id="light.hallway")
|
||||
3. Sends notification: "Front door opened. Hallway lights turned on."
|
||||
```
|
||||
332
hermes_code/website/docs/user-guide/messaging/index.md
Normal file
332
hermes_code/website/docs/user-guide/messaging/index.md
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
title: "Messaging Gateway"
|
||||
description: "Chat with Hermes from Telegram, Discord, Slack, WhatsApp, Signal, SMS, Email, Home Assistant, Mattermost, Matrix, DingTalk, Webhooks, or any OpenAI-compatible frontend via the API server — architecture and setup overview"
|
||||
---
|
||||
|
||||
# Messaging Gateway
|
||||
|
||||
Chat with Hermes from Telegram, Discord, Slack, WhatsApp, Signal, SMS, Email, Home Assistant, Mattermost, Matrix, DingTalk, or your browser. The gateway is a single background process that connects to all your configured platforms, handles sessions, runs cron jobs, and delivers voice messages.
|
||||
|
||||
For the full voice feature set — including CLI microphone mode, spoken replies in messaging, and Discord voice-channel conversations — see [Voice Mode](/docs/user-guide/features/voice-mode) and [Use Voice Mode with Hermes](/docs/guides/use-voice-mode-with-hermes).
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Gateway["Hermes Gateway"]
|
||||
subgraph Adapters["Platform adapters"]
|
||||
tg[Telegram]
|
||||
dc[Discord]
|
||||
wa[WhatsApp]
|
||||
sl[Slack]
|
||||
sig[Signal]
|
||||
sms[SMS]
|
||||
em[Email]
|
||||
ha[Home Assistant]
|
||||
mm[Mattermost]
|
||||
mx[Matrix]
|
||||
dt[DingTalk]
|
||||
api["API Server<br/>(OpenAI-compatible)"]
|
||||
wh[Webhooks]
|
||||
end
|
||||
|
||||
store["Session store<br/>per chat"]
|
||||
agent["AIAgent<br/>run_agent.py"]
|
||||
cron["Cron scheduler<br/>ticks every 60s"]
|
||||
end
|
||||
|
||||
tg --> store
|
||||
dc --> store
|
||||
wa --> store
|
||||
sl --> store
|
||||
sig --> store
|
||||
sms --> store
|
||||
em --> store
|
||||
ha --> store
|
||||
mm --> store
|
||||
mx --> store
|
||||
dt --> store
|
||||
api --> store
|
||||
wh --> store
|
||||
store --> agent
|
||||
cron --> store
|
||||
```
|
||||
|
||||
Each platform adapter receives messages, routes them through a per-chat session store, and dispatches them to the AIAgent for processing. The gateway also runs the cron scheduler, ticking every 60 seconds to execute any due jobs.
|
||||
|
||||
## Quick Setup
|
||||
|
||||
The easiest way to configure messaging platforms is the interactive wizard:
|
||||
|
||||
```bash
|
||||
hermes gateway setup # Interactive setup for all messaging platforms
|
||||
```
|
||||
|
||||
This walks you through configuring each platform with arrow-key selection, shows which platforms are already configured, and offers to start/restart the gateway when done.
|
||||
|
||||
## Gateway Commands
|
||||
|
||||
```bash
|
||||
hermes gateway # Run in foreground
|
||||
hermes gateway setup # Configure messaging platforms interactively
|
||||
hermes gateway install # Install as a user service (Linux) / launchd service (macOS)
|
||||
sudo hermes gateway install --system # Linux only: install a boot-time system service
|
||||
hermes gateway start # Start the default service
|
||||
hermes gateway stop # Stop the default service
|
||||
hermes gateway status # Check default service status
|
||||
hermes gateway status --system # Linux only: inspect the system service explicitly
|
||||
```
|
||||
|
||||
## Chat Commands (Inside Messaging)
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/new` or `/reset` | Start a fresh conversation |
|
||||
| `/model [provider:model]` | Show or change the model (supports `provider:model` syntax) |
|
||||
| `/provider` | Show available providers with auth status |
|
||||
| `/personality [name]` | Set a personality |
|
||||
| `/retry` | Retry the last message |
|
||||
| `/undo` | Remove the last exchange |
|
||||
| `/status` | Show session info |
|
||||
| `/stop` | Stop the running agent |
|
||||
| `/approve` | Approve a pending dangerous command |
|
||||
| `/deny` | Reject a pending dangerous command |
|
||||
| `/sethome` | Set this chat as the home channel |
|
||||
| `/compress` | Manually compress conversation context |
|
||||
| `/title [name]` | Set or show the session title |
|
||||
| `/resume [name]` | Resume a previously named session |
|
||||
| `/usage` | Show token usage for this session |
|
||||
| `/insights [days]` | Show usage insights and analytics |
|
||||
| `/reasoning [level\|show\|hide]` | Change reasoning effort or toggle reasoning display |
|
||||
| `/voice [on\|off\|tts\|join\|leave\|status]` | Control messaging voice replies and Discord voice-channel behavior |
|
||||
| `/rollback [number]` | List or restore filesystem checkpoints |
|
||||
| `/background <prompt>` | Run a prompt in a separate background session |
|
||||
| `/reload-mcp` | Reload MCP servers from config |
|
||||
| `/update` | Update Hermes Agent to the latest version |
|
||||
| `/help` | Show available commands |
|
||||
| `/<skill-name>` | Invoke any installed skill |
|
||||
|
||||
## Session Management
|
||||
|
||||
### Session Persistence
|
||||
|
||||
Sessions persist across messages until they reset. The agent remembers your conversation context.
|
||||
|
||||
### Reset Policies
|
||||
|
||||
Sessions reset based on configurable policies:
|
||||
|
||||
| Policy | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| Daily | 4:00 AM | Reset at a specific hour each day |
|
||||
| Idle | 1440 min | Reset after N minutes of inactivity |
|
||||
| Both | (combined) | Whichever triggers first |
|
||||
|
||||
Configure per-platform overrides in `~/.hermes/gateway.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"reset_by_platform": {
|
||||
"telegram": { "mode": "idle", "idle_minutes": 240 },
|
||||
"discord": { "mode": "idle", "idle_minutes": 60 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
**By default, the gateway denies all users who are not in an allowlist or paired via DM.** This is the safe default for a bot with terminal access.
|
||||
|
||||
```bash
|
||||
# Restrict to specific users (recommended):
|
||||
TELEGRAM_ALLOWED_USERS=123456789,987654321
|
||||
DISCORD_ALLOWED_USERS=123456789012345678
|
||||
SIGNAL_ALLOWED_USERS=+155****4567,+155****6543
|
||||
SMS_ALLOWED_USERS=+155****4567,+155****6543
|
||||
EMAIL_ALLOWED_USERS=trusted@example.com,colleague@work.com
|
||||
MATTERMOST_ALLOWED_USERS=3uo8dkh1p7g1mfk49ear5fzs5c
|
||||
MATRIX_ALLOWED_USERS=@alice:matrix.org
|
||||
DINGTALK_ALLOWED_USERS=user-id-1
|
||||
|
||||
# Or allow
|
||||
GATEWAY_ALLOWED_USERS=123456789,987654321
|
||||
|
||||
# Or explicitly allow all users (NOT recommended for bots with terminal access):
|
||||
GATEWAY_ALLOW_ALL_USERS=true
|
||||
```
|
||||
|
||||
### DM Pairing (Alternative to Allowlists)
|
||||
|
||||
Instead of manually configuring user IDs, unknown users receive a one-time pairing code when they DM the bot:
|
||||
|
||||
```bash
|
||||
# The user sees: "Pairing code: XKGH5N7P"
|
||||
# You approve them with:
|
||||
hermes pairing approve telegram XKGH5N7P
|
||||
|
||||
# Other pairing commands:
|
||||
hermes pairing list # View pending + approved users
|
||||
hermes pairing revoke telegram 123456789 # Remove access
|
||||
```
|
||||
|
||||
Pairing codes expire after 1 hour, are rate-limited, and use cryptographic randomness.
|
||||
|
||||
## Interrupting the Agent
|
||||
|
||||
Send any message while the agent is working to interrupt it. Key behaviors:
|
||||
|
||||
- **In-progress terminal commands are killed immediately** (SIGTERM, then SIGKILL after 1s)
|
||||
- **Tool calls are cancelled** — only the currently-executing one runs, the rest are skipped
|
||||
- **Multiple messages are combined** — messages sent during interruption are joined into one prompt
|
||||
- **`/stop` command** — interrupts without queuing a follow-up message
|
||||
|
||||
## Tool Progress Notifications
|
||||
|
||||
Control how much tool activity is displayed in `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
display:
|
||||
tool_progress: all # off | new | all | verbose
|
||||
```
|
||||
|
||||
When enabled, the bot sends status messages as it works:
|
||||
|
||||
```text
|
||||
💻 `ls -la`...
|
||||
🔍 web_search...
|
||||
📄 web_extract...
|
||||
🐍 execute_code...
|
||||
```
|
||||
|
||||
## Background Sessions
|
||||
|
||||
Run a prompt in a separate background session so the agent works on it independently while your main chat stays responsive:
|
||||
|
||||
```
|
||||
/background Check all servers in the cluster and report any that are down
|
||||
```
|
||||
|
||||
Hermes confirms immediately:
|
||||
|
||||
```
|
||||
🔄 Background task started: "Check all servers in the cluster..."
|
||||
Task ID: bg_143022_a1b2c3
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
Each `/background` prompt spawns a **separate agent instance** that runs asynchronously:
|
||||
|
||||
- **Isolated session** — the background agent has its own session with its own conversation history. It has no knowledge of your current chat context and receives only the prompt you provide.
|
||||
- **Same configuration** — inherits your model, provider, toolsets, reasoning settings, and provider routing from the current gateway setup.
|
||||
- **Non-blocking** — your main chat stays fully interactive. Send messages, run other commands, or start more background tasks while it works.
|
||||
- **Result delivery** — when the task finishes, the result is sent back to the **same chat or channel** where you issued the command, prefixed with "✅ Background task complete". If it fails, you'll see "❌ Background task failed" with the error.
|
||||
|
||||
### Background Process Notifications
|
||||
|
||||
When the agent running a background session uses `terminal(background=true)` to start long-running processes (servers, builds, etc.), the gateway can push status updates to your chat. Control this with `display.background_process_notifications` in `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
display:
|
||||
background_process_notifications: all # all | result | error | off
|
||||
```
|
||||
|
||||
| Mode | What you receive |
|
||||
|------|-----------------|
|
||||
| `all` | Running-output updates **and** the final completion message (default) |
|
||||
| `result` | Only the final completion message (regardless of exit code) |
|
||||
| `error` | Only the final message when the exit code is non-zero |
|
||||
| `off` | No process watcher messages at all |
|
||||
|
||||
You can also set this via environment variable:
|
||||
|
||||
```bash
|
||||
HERMES_BACKGROUND_NOTIFICATIONS=result
|
||||
```
|
||||
|
||||
### Use Cases
|
||||
|
||||
- **Server monitoring** — "/background Check the health of all services and alert me if anything is down"
|
||||
- **Long builds** — "/background Build and deploy the staging environment" while you continue chatting
|
||||
- **Research tasks** — "/background Research competitor pricing and summarize in a table"
|
||||
- **File operations** — "/background Organize the photos in ~/Downloads by date into folders"
|
||||
|
||||
:::tip
|
||||
Background tasks on messaging platforms are fire-and-forget — you don't need to wait or check on them. Results arrive in the same chat automatically when the task finishes.
|
||||
:::
|
||||
|
||||
## Service Management
|
||||
|
||||
### Linux (systemd)
|
||||
|
||||
```bash
|
||||
hermes gateway install # Install as user service
|
||||
hermes gateway start # Start the service
|
||||
hermes gateway stop # Stop the service
|
||||
hermes gateway status # Check status
|
||||
journalctl --user -u hermes-gateway -f # View logs
|
||||
|
||||
# Enable lingering (keeps running after logout)
|
||||
sudo loginctl enable-linger $USER
|
||||
|
||||
# Or install a boot-time system service that still runs as your user
|
||||
sudo hermes gateway install --system
|
||||
sudo hermes gateway start --system
|
||||
sudo hermes gateway status --system
|
||||
journalctl -u hermes-gateway -f
|
||||
```
|
||||
|
||||
Use the user service on laptops and dev boxes. Use the system service on VPS or headless hosts that should come back at boot without relying on systemd linger.
|
||||
|
||||
Avoid keeping both the user and system gateway units installed at once unless you really mean to. Hermes will warn if it detects both because start/stop/status behavior gets ambiguous.
|
||||
|
||||
:::info Multiple installations
|
||||
If you run multiple Hermes installations on the same machine (with different `HERMES_HOME` directories), each gets its own systemd service name. The default `~/.hermes` uses `hermes-gateway`; other installations use `hermes-gateway-<hash>`. The `hermes gateway` commands automatically target the correct service for your current `HERMES_HOME`.
|
||||
:::
|
||||
|
||||
### macOS (launchd)
|
||||
|
||||
```bash
|
||||
hermes gateway install
|
||||
launchctl start ai.hermes.gateway
|
||||
launchctl stop ai.hermes.gateway
|
||||
tail -f ~/.hermes/logs/gateway.log
|
||||
```
|
||||
|
||||
## Platform-Specific Toolsets
|
||||
|
||||
Each platform has its own toolset:
|
||||
|
||||
| Platform | Toolset | Capabilities |
|
||||
|----------|---------|--------------|
|
||||
| CLI | `hermes-cli` | Full access |
|
||||
| Telegram | `hermes-telegram` | Full tools including terminal |
|
||||
| Discord | `hermes-discord` | Full tools including terminal |
|
||||
| WhatsApp | `hermes-whatsapp` | Full tools including terminal |
|
||||
| Slack | `hermes-slack` | Full tools including terminal |
|
||||
| Signal | `hermes-signal` | Full tools including terminal |
|
||||
| SMS | `hermes-sms` | Full tools including terminal |
|
||||
| Email | `hermes-email` | Full tools including terminal |
|
||||
| Home Assistant | `hermes-homeassistant` | Full tools + HA device control (ha_list_entities, ha_get_state, ha_call_service, ha_list_services) |
|
||||
| Mattermost | `hermes-mattermost` | Full tools including terminal |
|
||||
| Matrix | `hermes-matrix` | Full tools including terminal |
|
||||
| DingTalk | `hermes-dingtalk` | Full tools including terminal |
|
||||
| API Server | `hermes` (default) | Full tools including terminal |
|
||||
| Webhooks | `hermes-webhook` | Full tools including terminal |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Telegram Setup](telegram.md)
|
||||
- [Discord Setup](discord.md)
|
||||
- [Slack Setup](slack.md)
|
||||
- [WhatsApp Setup](whatsapp.md)
|
||||
- [Signal Setup](signal.md)
|
||||
- [SMS Setup (Twilio)](sms.md)
|
||||
- [Email Setup](email.md)
|
||||
- [Home Assistant Integration](homeassistant.md)
|
||||
- [Mattermost Setup](mattermost.md)
|
||||
- [Matrix Setup](matrix.md)
|
||||
- [DingTalk Setup](dingtalk.md)
|
||||
- [Open WebUI + API Server](open-webui.md)
|
||||
- [Webhooks](webhooks.md)
|
||||
354
hermes_code/website/docs/user-guide/messaging/matrix.md
Normal file
354
hermes_code/website/docs/user-guide/messaging/matrix.md
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
---
|
||||
sidebar_position: 9
|
||||
title: "Matrix"
|
||||
description: "Set up Hermes Agent as a Matrix bot"
|
||||
---
|
||||
|
||||
# Matrix Setup
|
||||
|
||||
Hermes Agent integrates with Matrix, the open, federated messaging protocol. Matrix lets you run your own homeserver or use a public one like matrix.org — either way, you keep control of your communications. The bot connects via the `matrix-nio` Python SDK, processes messages through the Hermes Agent pipeline (including tool use, memory, and reasoning), and responds in real time. It supports text, file attachments, images, audio, video, and optional end-to-end encryption (E2EE).
|
||||
|
||||
Hermes works with any Matrix homeserver — Synapse, Conduit, Dendrite, or matrix.org.
|
||||
|
||||
Before setup, here's the part most people want to know: how Hermes behaves once it's connected.
|
||||
|
||||
## How Hermes Behaves
|
||||
|
||||
| Context | Behavior |
|
||||
|---------|----------|
|
||||
| **DMs** | Hermes responds to every message. No `@mention` needed. Each DM has its own session. |
|
||||
| **Rooms** | Hermes responds to all messages in rooms it has joined. Room invites are auto-accepted. |
|
||||
| **Threads** | Hermes supports Matrix threads (MSC3440). If you reply in a thread, Hermes keeps the thread context isolated from the main room timeline. |
|
||||
| **Shared rooms with multiple users** | By default, Hermes isolates session history per user inside the room. Two people talking in the same room do not share one transcript unless you explicitly disable that. |
|
||||
|
||||
:::tip
|
||||
The bot automatically joins rooms when invited. Just invite the bot's Matrix user to any room and it will join and start responding.
|
||||
:::
|
||||
|
||||
### Session Model in Matrix
|
||||
|
||||
By default:
|
||||
|
||||
- each DM gets its own session
|
||||
- each thread gets its own session namespace
|
||||
- each user in a shared room gets their own session inside that room
|
||||
|
||||
This is controlled by `config.yaml`:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
Set it to `false` only if you explicitly want one shared conversation for the entire room:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: false
|
||||
```
|
||||
|
||||
Shared sessions can be useful for a collaborative room, but they also mean:
|
||||
|
||||
- users share context growth and token costs
|
||||
- one person's long tool-heavy task can bloat everyone else's context
|
||||
- one person's in-flight run can interrupt another person's follow-up in the same room
|
||||
|
||||
This guide walks you through the full setup process — from creating your bot account to sending your first message.
|
||||
|
||||
## Step 1: Create a Bot Account
|
||||
|
||||
You need a Matrix user account for the bot. There are several ways to do this:
|
||||
|
||||
### Option A: Register on Your Homeserver (Recommended)
|
||||
|
||||
If you run your own homeserver (Synapse, Conduit, Dendrite):
|
||||
|
||||
1. Use the admin API or registration tool to create a new user:
|
||||
|
||||
```bash
|
||||
# Synapse example
|
||||
register_new_matrix_user -c /etc/synapse/homeserver.yaml http://localhost:8008
|
||||
```
|
||||
|
||||
2. Choose a username like `hermes` — the full user ID will be `@hermes:your-server.org`.
|
||||
|
||||
### Option B: Use matrix.org or Another Public Homeserver
|
||||
|
||||
1. Go to [Element Web](https://app.element.io) and create a new account.
|
||||
2. Pick a username for your bot (e.g., `hermes-bot`).
|
||||
|
||||
### Option C: Use Your Own Account
|
||||
|
||||
You can also run Hermes as your own user. This means the bot posts as you — useful for personal assistants.
|
||||
|
||||
## Step 2: Get an Access Token
|
||||
|
||||
Hermes needs an access token to authenticate with the homeserver. You have two options:
|
||||
|
||||
### Option A: Access Token (Recommended)
|
||||
|
||||
The most reliable way to get a token:
|
||||
|
||||
**Via Element:**
|
||||
1. Log in to [Element](https://app.element.io) with the bot account.
|
||||
2. Go to **Settings** → **Help & About**.
|
||||
3. Scroll down and expand **Advanced** — the access token is displayed there.
|
||||
4. **Copy it immediately.**
|
||||
|
||||
**Via the API:**
|
||||
|
||||
```bash
|
||||
curl -X POST https://your-server/_matrix/client/v3/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"type": "m.login.password",
|
||||
"user": "@hermes:your-server.org",
|
||||
"password": "your-password"
|
||||
}'
|
||||
```
|
||||
|
||||
The response includes an `access_token` field — copy it.
|
||||
|
||||
:::warning[Keep your access token safe]
|
||||
The access token gives full access to the bot's Matrix account. Never share it publicly or commit it to Git. If compromised, revoke it by logging out all sessions for that user.
|
||||
:::
|
||||
|
||||
### Option B: Password Login
|
||||
|
||||
Instead of providing an access token, you can give Hermes the bot's user ID and password. Hermes will log in automatically on startup. This is simpler but means the password is stored in your `.env` file.
|
||||
|
||||
```bash
|
||||
MATRIX_USER_ID=@hermes:your-server.org
|
||||
MATRIX_PASSWORD=your-password
|
||||
```
|
||||
|
||||
## Step 3: Find Your Matrix User ID
|
||||
|
||||
Hermes Agent uses your Matrix User ID to control who can interact with the bot. Matrix User IDs follow the format `@username:server`.
|
||||
|
||||
To find yours:
|
||||
|
||||
1. Open [Element](https://app.element.io) (or your preferred Matrix client).
|
||||
2. Click your avatar → **Settings**.
|
||||
3. Your User ID is displayed at the top of the profile (e.g., `@alice:matrix.org`).
|
||||
|
||||
:::tip
|
||||
Matrix User IDs always start with `@` and contain a `:` followed by the server name. For example: `@alice:matrix.org`, `@bob:your-server.com`.
|
||||
:::
|
||||
|
||||
## Step 4: Configure Hermes Agent
|
||||
|
||||
### Option A: Interactive Setup (Recommended)
|
||||
|
||||
Run the guided setup command:
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Select **Matrix** when prompted, then provide your homeserver URL, access token (or user ID + password), and allowed user IDs when asked.
|
||||
|
||||
### Option B: Manual Configuration
|
||||
|
||||
Add the following to your `~/.hermes/.env` file:
|
||||
|
||||
**Using an access token:**
|
||||
|
||||
```bash
|
||||
# Required
|
||||
MATRIX_HOMESERVER=https://matrix.example.org
|
||||
MATRIX_ACCESS_TOKEN=***
|
||||
|
||||
# Optional: user ID (auto-detected from token if omitted)
|
||||
# MATRIX_USER_ID=@hermes:matrix.example.org
|
||||
|
||||
# Security: restrict who can interact with the bot
|
||||
MATRIX_ALLOWED_USERS=@alice:matrix.example.org
|
||||
|
||||
# Multiple allowed users (comma-separated)
|
||||
# MATRIX_ALLOWED_USERS=@alice:matrix.example.org,@bob:matrix.example.org
|
||||
```
|
||||
|
||||
**Using password login:**
|
||||
|
||||
```bash
|
||||
# Required
|
||||
MATRIX_HOMESERVER=https://matrix.example.org
|
||||
MATRIX_USER_ID=@hermes:matrix.example.org
|
||||
MATRIX_PASSWORD=***
|
||||
|
||||
# Security
|
||||
MATRIX_ALLOWED_USERS=@alice:matrix.example.org
|
||||
```
|
||||
|
||||
Optional behavior settings in `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
- `group_sessions_per_user: true` keeps each participant's context isolated inside shared rooms
|
||||
|
||||
### Start the Gateway
|
||||
|
||||
Once configured, start the Matrix gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
The bot should connect to your homeserver and start syncing within a few seconds. Send it a message — either a DM or in a room it has joined — to test.
|
||||
|
||||
:::tip
|
||||
You can run `hermes gateway` in the background or as a systemd service for persistent operation. See the deployment docs for details.
|
||||
:::
|
||||
|
||||
## End-to-End Encryption (E2EE)
|
||||
|
||||
Hermes supports Matrix end-to-end encryption, so you can chat with your bot in encrypted rooms.
|
||||
|
||||
### Requirements
|
||||
|
||||
E2EE requires the `matrix-nio` library with encryption extras and the `libolm` C library:
|
||||
|
||||
```bash
|
||||
# Install matrix-nio with E2EE support
|
||||
pip install 'matrix-nio[e2e]'
|
||||
|
||||
# Or install with hermes extras
|
||||
pip install 'hermes-agent[matrix]'
|
||||
```
|
||||
|
||||
You also need `libolm` installed on your system:
|
||||
|
||||
```bash
|
||||
# Debian/Ubuntu
|
||||
sudo apt install libolm-dev
|
||||
|
||||
# macOS
|
||||
brew install libolm
|
||||
|
||||
# Fedora
|
||||
sudo dnf install libolm-devel
|
||||
```
|
||||
|
||||
### Enable E2EE
|
||||
|
||||
Add to your `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
MATRIX_ENCRYPTION=true
|
||||
```
|
||||
|
||||
When E2EE is enabled, Hermes:
|
||||
|
||||
- Stores encryption keys in `~/.hermes/matrix/store/`
|
||||
- Uploads device keys on first connection
|
||||
- Decrypts incoming messages and encrypts outgoing messages automatically
|
||||
- Auto-joins encrypted rooms when invited
|
||||
|
||||
:::warning
|
||||
If you delete the `~/.hermes/matrix/store/` directory, the bot loses its encryption keys. You'll need to verify the device again in your Matrix client. Back up this directory if you want to preserve encrypted sessions.
|
||||
:::
|
||||
|
||||
:::info
|
||||
If `matrix-nio[e2e]` is not installed or `libolm` is missing, the bot falls back to a plain (unencrypted) client automatically. You'll see a warning in the logs.
|
||||
:::
|
||||
|
||||
## Home Room
|
||||
|
||||
You can designate a "home room" where the bot sends proactive messages (such as cron job output, reminders, and notifications). There are two ways to set it:
|
||||
|
||||
### Using the Slash Command
|
||||
|
||||
Type `/sethome` in any Matrix room where the bot is present. That room becomes the home room.
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
Add this to your `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
MATRIX_HOME_ROOM=!abc123def456:matrix.example.org
|
||||
```
|
||||
|
||||
:::tip
|
||||
To find a Room ID: in Element, go to the room → **Settings** → **Advanced** → the **Internal room ID** is shown there (starts with `!`).
|
||||
:::
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Bot is not responding to messages
|
||||
|
||||
**Cause**: The bot hasn't joined the room, or `MATRIX_ALLOWED_USERS` doesn't include your User ID.
|
||||
|
||||
**Fix**: Invite the bot to the room — it auto-joins on invite. Verify your User ID is in `MATRIX_ALLOWED_USERS` (use the full `@user:server` format). Restart the gateway.
|
||||
|
||||
### "Failed to authenticate" / "whoami failed" on startup
|
||||
|
||||
**Cause**: The access token or homeserver URL is incorrect.
|
||||
|
||||
**Fix**: Verify `MATRIX_HOMESERVER` points to your homeserver (include `https://`, no trailing slash). Check that `MATRIX_ACCESS_TOKEN` is valid — try it with curl:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
https://your-server/_matrix/client/v3/account/whoami
|
||||
```
|
||||
|
||||
If this returns your user info, the token is valid. If it returns an error, generate a new token.
|
||||
|
||||
### "matrix-nio not installed" error
|
||||
|
||||
**Cause**: The `matrix-nio` Python package is not installed.
|
||||
|
||||
**Fix**: Install it:
|
||||
|
||||
```bash
|
||||
pip install 'matrix-nio[e2e]'
|
||||
```
|
||||
|
||||
Or with Hermes extras:
|
||||
|
||||
```bash
|
||||
pip install 'hermes-agent[matrix]'
|
||||
```
|
||||
|
||||
### Encryption errors / "could not decrypt event"
|
||||
|
||||
**Cause**: Missing encryption keys, `libolm` not installed, or the bot's device isn't trusted.
|
||||
|
||||
**Fix**:
|
||||
1. Verify `libolm` is installed on your system (see the E2EE section above).
|
||||
2. Make sure `MATRIX_ENCRYPTION=true` is set in your `.env`.
|
||||
3. In your Matrix client (Element), go to the bot's profile → **Sessions** → verify/trust the bot's device.
|
||||
4. If the bot just joined an encrypted room, it can only decrypt messages sent *after* it joined. Older messages are inaccessible.
|
||||
|
||||
### Sync issues / bot falls behind
|
||||
|
||||
**Cause**: Long-running tool executions can delay the sync loop, or the homeserver is slow.
|
||||
|
||||
**Fix**: The sync loop automatically retries every 5 seconds on error. Check the Hermes logs for sync-related warnings. If the bot consistently falls behind, ensure your homeserver has adequate resources.
|
||||
|
||||
### Bot is offline
|
||||
|
||||
**Cause**: The Hermes gateway isn't running, or it failed to connect.
|
||||
|
||||
**Fix**: Check that `hermes gateway` is running. Look at the terminal output for error messages. Common issues: wrong homeserver URL, expired access token, homeserver unreachable.
|
||||
|
||||
### "User not allowed" / Bot ignores you
|
||||
|
||||
**Cause**: Your User ID isn't in `MATRIX_ALLOWED_USERS`.
|
||||
|
||||
**Fix**: Add your User ID to `MATRIX_ALLOWED_USERS` in `~/.hermes/.env` and restart the gateway. Use the full `@user:server` format.
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
Always set `MATRIX_ALLOWED_USERS` to restrict who can interact with the bot. Without it, the gateway denies all users by default as a safety measure. Only add User IDs of people you trust — authorized users have full access to the agent's capabilities, including tool use and system access.
|
||||
:::
|
||||
|
||||
For more information on securing your Hermes Agent deployment, see the [Security Guide](../security.md).
|
||||
|
||||
## Notes
|
||||
|
||||
- **Any homeserver**: Works with Synapse, Conduit, Dendrite, matrix.org, or any spec-compliant Matrix homeserver. No specific homeserver software required.
|
||||
- **Federation**: If you're on a federated homeserver, the bot can communicate with users from other servers — just add their full `@user:server` IDs to `MATRIX_ALLOWED_USERS`.
|
||||
- **Auto-join**: The bot automatically accepts room invites and joins. It starts responding immediately after joining.
|
||||
- **Media support**: Hermes can send and receive images, audio, video, and file attachments. Media is uploaded to your homeserver using the Matrix content repository API.
|
||||
277
hermes_code/website/docs/user-guide/messaging/mattermost.md
Normal file
277
hermes_code/website/docs/user-guide/messaging/mattermost.md
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
---
|
||||
sidebar_position: 8
|
||||
title: "Mattermost"
|
||||
description: "Set up Hermes Agent as a Mattermost bot"
|
||||
---
|
||||
|
||||
# Mattermost Setup
|
||||
|
||||
Hermes Agent integrates with Mattermost as a bot, letting you chat with your AI assistant through direct messages or team channels. Mattermost is a self-hosted, open-source Slack alternative — you run it on your own infrastructure, keeping full control of your data. The bot connects via Mattermost's REST API (v4) and WebSocket for real-time events, processes messages through the Hermes Agent pipeline (including tool use, memory, and reasoning), and responds in real time. It supports text, file attachments, images, and slash commands.
|
||||
|
||||
No external Mattermost library is required — the adapter uses `aiohttp`, which is already a Hermes dependency.
|
||||
|
||||
Before setup, here's the part most people want to know: how Hermes behaves once it's in your Mattermost instance.
|
||||
|
||||
## How Hermes Behaves
|
||||
|
||||
| Context | Behavior |
|
||||
|---------|----------|
|
||||
| **DMs** | Hermes responds to every message. No `@mention` needed. Each DM has its own session. |
|
||||
| **Public/private channels** | Hermes responds when you `@mention` it. Without a mention, Hermes ignores the message. |
|
||||
| **Threads** | If `MATTERMOST_REPLY_MODE=thread`, Hermes replies in a thread under your message. Thread context stays isolated from the parent channel. |
|
||||
| **Shared channels with multiple users** | By default, Hermes isolates session history per user inside the channel. Two people talking in the same channel do not share one transcript unless you explicitly disable that. |
|
||||
|
||||
:::tip
|
||||
If you want Hermes to reply as threaded conversations (nested under your original message), set `MATTERMOST_REPLY_MODE=thread`. The default is `off`, which sends flat messages in the channel.
|
||||
:::
|
||||
|
||||
### Session Model in Mattermost
|
||||
|
||||
By default:
|
||||
|
||||
- each DM gets its own session
|
||||
- each thread gets its own session namespace
|
||||
- each user in a shared channel gets their own session inside that channel
|
||||
|
||||
This is controlled by `config.yaml`:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
Set it to `false` only if you explicitly want one shared conversation for the entire channel:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: false
|
||||
```
|
||||
|
||||
Shared sessions can be useful for a collaborative channel, but they also mean:
|
||||
|
||||
- users share context growth and token costs
|
||||
- one person's long tool-heavy task can bloat everyone else's context
|
||||
- one person's in-flight run can interrupt another person's follow-up in the same channel
|
||||
|
||||
This guide walks you through the full setup process — from creating your bot on Mattermost to sending your first message.
|
||||
|
||||
## Step 1: Enable Bot Accounts
|
||||
|
||||
Bot accounts must be enabled on your Mattermost server before you can create one.
|
||||
|
||||
1. Log in to Mattermost as a **System Admin**.
|
||||
2. Go to **System Console** → **Integrations** → **Bot Accounts**.
|
||||
3. Set **Enable Bot Account Creation** to **true**.
|
||||
4. Click **Save**.
|
||||
|
||||
:::info
|
||||
If you don't have System Admin access, ask your Mattermost administrator to enable bot accounts and create one for you.
|
||||
:::
|
||||
|
||||
## Step 2: Create a Bot Account
|
||||
|
||||
1. In Mattermost, click the **☰** menu (top-left) → **Integrations** → **Bot Accounts**.
|
||||
2. Click **Add Bot Account**.
|
||||
3. Fill in the details:
|
||||
- **Username**: e.g., `hermes`
|
||||
- **Display Name**: e.g., `Hermes Agent`
|
||||
- **Description**: optional
|
||||
- **Role**: `Member` is sufficient
|
||||
4. Click **Create Bot Account**.
|
||||
5. Mattermost will display the **bot token**. **Copy it immediately.**
|
||||
|
||||
:::warning[Token shown only once]
|
||||
The bot token is only displayed once when you create the bot account. If you lose it, you'll need to regenerate it from the bot account settings. Never share your token publicly or commit it to Git — anyone with this token has full control of the bot.
|
||||
:::
|
||||
|
||||
Store the token somewhere safe (a password manager, for example). You'll need it in Step 5.
|
||||
|
||||
:::tip
|
||||
You can also use a **personal access token** instead of a bot account. Go to **Profile** → **Security** → **Personal Access Tokens** → **Create Token**. This is useful if you want Hermes to post as your own user rather than a separate bot user.
|
||||
:::
|
||||
|
||||
## Step 3: Add the Bot to Channels
|
||||
|
||||
The bot needs to be a member of any channel where you want it to respond:
|
||||
|
||||
1. Open the channel where you want the bot.
|
||||
2. Click the channel name → **Add Members**.
|
||||
3. Search for your bot username (e.g., `hermes`) and add it.
|
||||
|
||||
For DMs, simply open a direct message with the bot — it will be able to respond immediately.
|
||||
|
||||
## Step 4: Find Your Mattermost User ID
|
||||
|
||||
Hermes Agent uses your Mattermost User ID to control who can interact with the bot. To find it:
|
||||
|
||||
1. Click your **avatar** (top-left corner) → **Profile**.
|
||||
2. Your User ID is displayed in the profile dialog — click it to copy.
|
||||
|
||||
Your User ID is a 26-character alphanumeric string like `3uo8dkh1p7g1mfk49ear5fzs5c`.
|
||||
|
||||
:::warning
|
||||
Your User ID is **not** your username. The username is what appears after `@` (e.g., `@alice`). The User ID is a long alphanumeric identifier that Mattermost uses internally.
|
||||
:::
|
||||
|
||||
**Alternative**: You can also get your User ID via the API:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
https://your-mattermost-server/api/v4/users/me | jq .id
|
||||
```
|
||||
|
||||
:::tip
|
||||
To get a **Channel ID**: click the channel name → **View Info**. The Channel ID is shown in the info panel. You'll need this if you want to set a home channel manually.
|
||||
:::
|
||||
|
||||
## Step 5: Configure Hermes Agent
|
||||
|
||||
### Option A: Interactive Setup (Recommended)
|
||||
|
||||
Run the guided setup command:
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Select **Mattermost** when prompted, then paste your server URL, bot token, and user ID when asked.
|
||||
|
||||
### Option B: Manual Configuration
|
||||
|
||||
Add the following to your `~/.hermes/.env` file:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
MATTERMOST_URL=https://mm.example.com
|
||||
MATTERMOST_TOKEN=***
|
||||
MATTERMOST_ALLOWED_USERS=3uo8dkh1p7g1mfk49ear5fzs5c
|
||||
|
||||
# Multiple allowed users (comma-separated)
|
||||
# MATTERMOST_ALLOWED_USERS=3uo8dkh1p7g1mfk49ear5fzs5c,8fk2jd9s0a7bncm1xqw4tp6r3e
|
||||
|
||||
# Optional: reply mode (thread or off, default: off)
|
||||
# MATTERMOST_REPLY_MODE=thread
|
||||
```
|
||||
|
||||
Optional behavior settings in `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
group_sessions_per_user: true
|
||||
```
|
||||
|
||||
- `group_sessions_per_user: true` keeps each participant's context isolated inside shared channels and threads
|
||||
|
||||
### Start the Gateway
|
||||
|
||||
Once configured, start the Mattermost gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
The bot should connect to your Mattermost server within a few seconds. Send it a message — either a DM or in a channel where it's been added — to test.
|
||||
|
||||
:::tip
|
||||
You can run `hermes gateway` in the background or as a systemd service for persistent operation. See the deployment docs for details.
|
||||
:::
|
||||
|
||||
## Home Channel
|
||||
|
||||
You can designate a "home channel" where the bot sends proactive messages (such as cron job output, reminders, and notifications). There are two ways to set it:
|
||||
|
||||
### Using the Slash Command
|
||||
|
||||
Type `/sethome` in any Mattermost channel where the bot is present. That channel becomes the home channel.
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
Add this to your `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
MATTERMOST_HOME_CHANNEL=abc123def456ghi789jkl012mn
|
||||
```
|
||||
|
||||
Replace the ID with the actual channel ID (click the channel name → View Info → copy the ID).
|
||||
|
||||
## Reply Mode
|
||||
|
||||
The `MATTERMOST_REPLY_MODE` setting controls how Hermes posts responses:
|
||||
|
||||
| Mode | Behavior |
|
||||
|------|----------|
|
||||
| `off` (default) | Hermes posts flat messages in the channel, like a normal user. |
|
||||
| `thread` | Hermes replies in a thread under your original message. Keeps channels clean when there's lots of back-and-forth. |
|
||||
|
||||
Set it in your `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
MATTERMOST_REPLY_MODE=thread
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Bot is not responding to messages
|
||||
|
||||
**Cause**: The bot is not a member of the channel, or `MATTERMOST_ALLOWED_USERS` doesn't include your User ID.
|
||||
|
||||
**Fix**: Add the bot to the channel (channel name → Add Members → search for the bot). Verify your User ID is in `MATTERMOST_ALLOWED_USERS`. Restart the gateway.
|
||||
|
||||
### 403 Forbidden errors
|
||||
|
||||
**Cause**: The bot token is invalid, or the bot doesn't have permission to post in the channel.
|
||||
|
||||
**Fix**: Check that `MATTERMOST_TOKEN` in your `.env` file is correct. Make sure the bot account hasn't been deactivated. Verify the bot has been added to the channel. If using a personal access token, ensure your account has the required permissions.
|
||||
|
||||
### WebSocket disconnects / reconnection loops
|
||||
|
||||
**Cause**: Network instability, Mattermost server restarts, or firewall/proxy issues with WebSocket connections.
|
||||
|
||||
**Fix**: The adapter automatically reconnects with exponential backoff (2s → 60s). Check your server's WebSocket configuration — reverse proxies (nginx, Apache) need WebSocket upgrade headers configured. Verify no firewall is blocking WebSocket connections on your Mattermost server.
|
||||
|
||||
For nginx, ensure your config includes:
|
||||
|
||||
```nginx
|
||||
location /api/v4/websocket {
|
||||
proxy_pass http://mattermost-backend;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
```
|
||||
|
||||
### "Failed to authenticate" on startup
|
||||
|
||||
**Cause**: The token or server URL is incorrect.
|
||||
|
||||
**Fix**: Verify `MATTERMOST_URL` points to your Mattermost server (include `https://`, no trailing slash). Check that `MATTERMOST_TOKEN` is valid — try it with curl:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer YOUR_TOKEN" \
|
||||
https://your-server/api/v4/users/me
|
||||
```
|
||||
|
||||
If this returns your bot's user info, the token is valid. If it returns an error, regenerate the token.
|
||||
|
||||
### Bot is offline
|
||||
|
||||
**Cause**: The Hermes gateway isn't running, or it failed to connect.
|
||||
|
||||
**Fix**: Check that `hermes gateway` is running. Look at the terminal output for error messages. Common issues: wrong URL, expired token, Mattermost server unreachable.
|
||||
|
||||
### "User not allowed" / Bot ignores you
|
||||
|
||||
**Cause**: Your User ID isn't in `MATTERMOST_ALLOWED_USERS`.
|
||||
|
||||
**Fix**: Add your User ID to `MATTERMOST_ALLOWED_USERS` in `~/.hermes/.env` and restart the gateway. Remember: the User ID is a 26-character alphanumeric string, not your `@username`.
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
Always set `MATTERMOST_ALLOWED_USERS` to restrict who can interact with the bot. Without it, the gateway denies all users by default as a safety measure. Only add User IDs of people you trust — authorized users have full access to the agent's capabilities, including tool use and system access.
|
||||
:::
|
||||
|
||||
For more information on securing your Hermes Agent deployment, see the [Security Guide](../security.md).
|
||||
|
||||
## Notes
|
||||
|
||||
- **Self-hosted friendly**: Works with any self-hosted Mattermost instance. No Mattermost Cloud account or subscription required.
|
||||
- **No extra dependencies**: The adapter uses `aiohttp` for HTTP and WebSocket, which is already included with Hermes Agent.
|
||||
- **Team Edition compatible**: Works with both Mattermost Team Edition (free) and Enterprise Edition.
|
||||
208
hermes_code/website/docs/user-guide/messaging/open-webui.md
Normal file
208
hermes_code/website/docs/user-guide/messaging/open-webui.md
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
---
|
||||
sidebar_position: 8
|
||||
title: "Open WebUI"
|
||||
description: "Connect Open WebUI to Hermes Agent via the OpenAI-compatible API server"
|
||||
---
|
||||
|
||||
# Open WebUI Integration
|
||||
|
||||
[Open WebUI](https://github.com/open-webui/open-webui) (126k★) is the most popular self-hosted chat interface for AI. With Hermes Agent's built-in API server, you can use Open WebUI as a polished web frontend for your agent — complete with conversation management, user accounts, and a modern chat interface.
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Open WebUI<br/>browser UI<br/>port 3000"]
|
||||
B["hermes-agent<br/>gateway API server<br/>port 8642"]
|
||||
A -->|POST /v1/chat/completions| B
|
||||
B -->|SSE streaming response| A
|
||||
```
|
||||
|
||||
Open WebUI connects to Hermes Agent's API server just like it would connect to OpenAI. Your agent handles the requests with its full toolset — terminal, file operations, web search, memory, skills — and returns the final response.
|
||||
|
||||
Open WebUI talks to Hermes server-to-server, so you do not need `API_SERVER_CORS_ORIGINS` for this integration.
|
||||
|
||||
## Quick Setup
|
||||
|
||||
### 1. Enable the API server
|
||||
|
||||
Add to `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
API_SERVER_ENABLED=true
|
||||
API_SERVER_KEY=your-secret-key
|
||||
```
|
||||
|
||||
### 2. Start Hermes Agent gateway
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
You should see:
|
||||
|
||||
```
|
||||
[API Server] API server listening on http://127.0.0.1:8642
|
||||
```
|
||||
|
||||
### 3. Start Open WebUI
|
||||
|
||||
```bash
|
||||
docker run -d -p 3000:8080 \
|
||||
-e OPENAI_API_BASE_URL=http://host.docker.internal:8642/v1 \
|
||||
-e OPENAI_API_KEY=your-secret-key \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
-v open-webui:/app/backend/data \
|
||||
--name open-webui \
|
||||
--restart always \
|
||||
ghcr.io/open-webui/open-webui:main
|
||||
```
|
||||
|
||||
### 4. Open the UI
|
||||
|
||||
Go to **http://localhost:3000**. Create your admin account (the first user becomes admin). You should see **hermes-agent** in the model dropdown. Start chatting!
|
||||
|
||||
## Docker Compose Setup
|
||||
|
||||
For a more permanent setup, create a `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
open-webui:
|
||||
image: ghcr.io/open-webui/open-webui:main
|
||||
ports:
|
||||
- "3000:8080"
|
||||
volumes:
|
||||
- open-webui:/app/backend/data
|
||||
environment:
|
||||
- OPENAI_API_BASE_URL=http://host.docker.internal:8642/v1
|
||||
- OPENAI_API_KEY=your-secret-key
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
open-webui:
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Configuring via the Admin UI
|
||||
|
||||
If you prefer to configure the connection through the UI instead of environment variables:
|
||||
|
||||
1. Log in to Open WebUI at **http://localhost:3000**
|
||||
2. Click your **profile avatar** → **Admin Settings**
|
||||
3. Go to **Connections**
|
||||
4. Under **OpenAI API**, click the **wrench icon** (Manage)
|
||||
5. Click **+ Add New Connection**
|
||||
6. Enter:
|
||||
- **URL**: `http://host.docker.internal:8642/v1`
|
||||
- **API Key**: your key or any non-empty value (e.g., `not-needed`)
|
||||
7. Click the **checkmark** to verify the connection
|
||||
8. **Save**
|
||||
|
||||
The **hermes-agent** model should now appear in the model dropdown.
|
||||
|
||||
:::warning
|
||||
Environment variables only take effect on Open WebUI's **first launch**. After that, connection settings are stored in its internal database. To change them later, use the Admin UI or delete the Docker volume and start fresh.
|
||||
:::
|
||||
|
||||
## API Type: Chat Completions vs Responses
|
||||
|
||||
Open WebUI supports two API modes when connecting to a backend:
|
||||
|
||||
| Mode | Format | When to use |
|
||||
|------|--------|-------------|
|
||||
| **Chat Completions** (default) | `/v1/chat/completions` | Recommended. Works out of the box. |
|
||||
| **Responses** (experimental) | `/v1/responses` | For server-side conversation state via `previous_response_id`. |
|
||||
|
||||
### Using Chat Completions (recommended)
|
||||
|
||||
This is the default and requires no extra configuration. Open WebUI sends standard OpenAI-format requests and Hermes Agent responds accordingly. Each request includes the full conversation history.
|
||||
|
||||
### Using Responses API
|
||||
|
||||
To use the Responses API mode:
|
||||
|
||||
1. Go to **Admin Settings** → **Connections** → **OpenAI** → **Manage**
|
||||
2. Edit your hermes-agent connection
|
||||
3. Change **API Type** from "Chat Completions" to **"Responses (Experimental)"**
|
||||
4. Save
|
||||
|
||||
With the Responses API, Open WebUI sends requests in the Responses format (`input` array + `instructions`), and Hermes Agent can preserve full tool call history across turns via `previous_response_id`.
|
||||
|
||||
:::note
|
||||
Open WebUI currently manages conversation history client-side even in Responses mode — it sends the full message history in each request rather than using `previous_response_id`. The Responses API mode is mainly useful for future compatibility as frontends evolve.
|
||||
:::
|
||||
|
||||
## How It Works
|
||||
|
||||
When you send a message in Open WebUI:
|
||||
|
||||
1. Open WebUI sends a `POST /v1/chat/completions` request with your message and conversation history
|
||||
2. Hermes Agent creates an AIAgent instance with its full toolset
|
||||
3. The agent processes your request — it may call tools (terminal, file operations, web search, etc.)
|
||||
4. Tool calls happen invisibly server-side
|
||||
5. The agent's final text response is returned to Open WebUI
|
||||
6. Open WebUI displays the response in its chat interface
|
||||
|
||||
Your agent has access to all the same tools and capabilities as when using the CLI or Telegram — the only difference is the frontend.
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
### Hermes Agent (API server)
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `API_SERVER_ENABLED` | `false` | Enable the API server |
|
||||
| `API_SERVER_PORT` | `8642` | HTTP server port |
|
||||
| `API_SERVER_HOST` | `127.0.0.1` | Bind address |
|
||||
| `API_SERVER_KEY` | _(required)_ | Bearer token for auth. Match `OPENAI_API_KEY`. |
|
||||
|
||||
### Open WebUI
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `OPENAI_API_BASE_URL` | Hermes Agent's API URL (include `/v1`) |
|
||||
| `OPENAI_API_KEY` | Must be non-empty. Match your `API_SERVER_KEY`. |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No models appear in the dropdown
|
||||
|
||||
- **Check the URL has `/v1` suffix**: `http://host.docker.internal:8642/v1` (not just `:8642`)
|
||||
- **Verify the gateway is running**: `curl http://localhost:8642/health` should return `{"status": "ok"}`
|
||||
- **Check model listing**: `curl http://localhost:8642/v1/models` should return a list with `hermes-agent`
|
||||
- **Docker networking**: From inside Docker, `localhost` means the container, not your host. Use `host.docker.internal` or `--network=host`.
|
||||
|
||||
### Connection test passes but no models load
|
||||
|
||||
This is almost always the missing `/v1` suffix. Open WebUI's connection test is a basic connectivity check — it doesn't verify model listing works.
|
||||
|
||||
### Response takes a long time
|
||||
|
||||
Hermes Agent may be executing multiple tool calls (reading files, running commands, searching the web) before producing its final response. This is normal for complex queries. The response appears all at once when the agent finishes.
|
||||
|
||||
### "Invalid API key" errors
|
||||
|
||||
Make sure your `OPENAI_API_KEY` in Open WebUI matches the `API_SERVER_KEY` in Hermes Agent.
|
||||
|
||||
## Linux Docker (no Docker Desktop)
|
||||
|
||||
On Linux without Docker Desktop, `host.docker.internal` doesn't resolve by default. Options:
|
||||
|
||||
```bash
|
||||
# Option 1: Add host mapping
|
||||
docker run --add-host=host.docker.internal:host-gateway ...
|
||||
|
||||
# Option 2: Use host networking
|
||||
docker run --network=host -e OPENAI_API_BASE_URL=http://localhost:8642/v1 ...
|
||||
|
||||
# Option 3: Use Docker bridge IP
|
||||
docker run -e OPENAI_API_BASE_URL=http://172.17.0.1:8642/v1 ...
|
||||
```
|
||||
238
hermes_code/website/docs/user-guide/messaging/signal.md
Normal file
238
hermes_code/website/docs/user-guide/messaging/signal.md
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
---
|
||||
sidebar_position: 6
|
||||
title: "Signal"
|
||||
description: "Set up Hermes Agent as a Signal messenger bot via signal-cli daemon"
|
||||
---
|
||||
|
||||
# Signal Setup
|
||||
|
||||
Hermes connects to Signal through the [signal-cli](https://github.com/AsamK/signal-cli) daemon running in HTTP mode. The adapter streams messages in real-time via SSE (Server-Sent Events) and sends responses via JSON-RPC.
|
||||
|
||||
Signal is the most privacy-focused mainstream messenger — end-to-end encrypted by default, open-source protocol, minimal metadata collection. This makes it ideal for security-sensitive agent workflows.
|
||||
|
||||
:::info No New Python Dependencies
|
||||
The Signal adapter uses `httpx` (already a core Hermes dependency) for all communication. No additional Python packages are required. You just need signal-cli installed externally.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **signal-cli** — Java-based Signal client ([GitHub](https://github.com/AsamK/signal-cli))
|
||||
- **Java 17+** runtime — required by signal-cli
|
||||
- **A phone number** with Signal installed (for linking as a secondary device)
|
||||
|
||||
### Installing signal-cli
|
||||
|
||||
```bash
|
||||
# Linux (Debian/Ubuntu)
|
||||
sudo apt install signal-cli
|
||||
|
||||
# macOS
|
||||
brew install signal-cli
|
||||
|
||||
# Manual install (any platform)
|
||||
# Download from https://github.com/AsamK/signal-cli/releases
|
||||
# Extract and add to PATH
|
||||
```
|
||||
|
||||
### Alternative: Docker (signal-cli-rest-api)
|
||||
|
||||
If you prefer Docker, use the [signal-cli-rest-api](https://github.com/bbernhard/signal-cli-rest-api) container:
|
||||
|
||||
```bash
|
||||
docker run -d --name signal-cli \
|
||||
-p 8080:8080 \
|
||||
-v $HOME/.local/share/signal-cli:/home/.local/share/signal-cli \
|
||||
-e MODE=json-rpc \
|
||||
bbernhard/signal-cli-rest-api
|
||||
```
|
||||
|
||||
:::tip
|
||||
Use `MODE=json-rpc` for best performance. The `normal` mode spawns a JVM per request and is much slower.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Link Your Signal Account
|
||||
|
||||
Signal-cli works as a **linked device** — like WhatsApp Web, but for Signal. Your phone stays the primary device.
|
||||
|
||||
```bash
|
||||
# Generate a linking URI (displays a QR code or link)
|
||||
signal-cli link -n "HermesAgent"
|
||||
```
|
||||
|
||||
1. Open **Signal** on your phone
|
||||
2. Go to **Settings → Linked Devices**
|
||||
3. Tap **Link New Device**
|
||||
4. Scan the QR code or enter the URI
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Start the signal-cli Daemon
|
||||
|
||||
```bash
|
||||
# Replace +1234567890 with your Signal phone number (E.164 format)
|
||||
signal-cli --account +1234567890 daemon --http 127.0.0.1:8080
|
||||
```
|
||||
|
||||
:::tip
|
||||
Keep this running in the background. You can use `systemd`, `tmux`, `screen`, or run it as a service.
|
||||
:::
|
||||
|
||||
Verify it's running:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8080/api/v1/check
|
||||
# Should return: {"versions":{"signal-cli":...}}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Configure Hermes
|
||||
|
||||
The easiest way:
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Select **Signal** from the platform menu. The wizard will:
|
||||
|
||||
1. Check if signal-cli is installed
|
||||
2. Prompt for the HTTP URL (default: `http://127.0.0.1:8080`)
|
||||
3. Test connectivity to the daemon
|
||||
4. Ask for your account phone number
|
||||
5. Configure allowed users and access policies
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
Add to `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
SIGNAL_HTTP_URL=http://127.0.0.1:8080
|
||||
SIGNAL_ACCOUNT=+1234567890
|
||||
|
||||
# Security (recommended)
|
||||
SIGNAL_ALLOWED_USERS=+1234567890,+0987654321 # Comma-separated E.164 numbers or UUIDs
|
||||
|
||||
# Optional
|
||||
SIGNAL_GROUP_ALLOWED_USERS=groupId1,groupId2 # Enable groups (omit to disable, * for all)
|
||||
SIGNAL_HOME_CHANNEL=+1234567890 # Default delivery target for cron jobs
|
||||
```
|
||||
|
||||
Then start the gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway # Foreground
|
||||
hermes gateway install # Install as a user service
|
||||
sudo hermes gateway install --system # Linux only: boot-time system service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Access Control
|
||||
|
||||
### DM Access
|
||||
|
||||
DM access follows the same pattern as all other Hermes platforms:
|
||||
|
||||
1. **`SIGNAL_ALLOWED_USERS` set** → only those users can message
|
||||
2. **No allowlist set** → unknown users get a DM pairing code (approve via `hermes pairing approve signal CODE`)
|
||||
3. **`SIGNAL_ALLOW_ALL_USERS=true`** → anyone can message (use with caution)
|
||||
|
||||
### Group Access
|
||||
|
||||
Group access is controlled by the `SIGNAL_GROUP_ALLOWED_USERS` env var:
|
||||
|
||||
| Configuration | Behavior |
|
||||
|---------------|----------|
|
||||
| Not set (default) | All group messages are ignored. The bot only responds to DMs. |
|
||||
| Set with group IDs | Only listed groups are monitored (e.g., `groupId1,groupId2`). |
|
||||
| Set to `*` | The bot responds in any group it's a member of. |
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Attachments
|
||||
|
||||
The adapter supports sending and receiving:
|
||||
|
||||
- **Images** — PNG, JPEG, GIF, WebP (auto-detected via magic bytes)
|
||||
- **Audio** — MP3, OGG, WAV, M4A (voice messages transcribed if Whisper is configured)
|
||||
- **Documents** — PDF, ZIP, and other file types
|
||||
|
||||
Attachment size limit: **100 MB**.
|
||||
|
||||
### Typing Indicators
|
||||
|
||||
The bot sends typing indicators while processing messages, refreshing every 8 seconds.
|
||||
|
||||
### Phone Number Redaction
|
||||
|
||||
All phone numbers are automatically redacted in logs:
|
||||
- `+15551234567` → `+155****4567`
|
||||
- This applies to both Hermes gateway logs and the global redaction system
|
||||
|
||||
### Note to Self (Single-Number Setup)
|
||||
|
||||
If you run signal-cli as a **linked secondary device** on your own phone number (rather than a separate bot number), you can interact with Hermes through Signal's "Note to Self" feature.
|
||||
|
||||
Just send a message to yourself from your phone — signal-cli picks it up and Hermes responds in the same conversation.
|
||||
|
||||
**How it works:**
|
||||
- "Note to Self" messages arrive as `syncMessage.sentMessage` envelopes
|
||||
- The adapter detects when these are addressed to the bot's own account and processes them as regular inbound messages
|
||||
- Echo-back protection (sent-timestamp tracking) prevents infinite loops — the bot's own replies are filtered out automatically
|
||||
|
||||
**No extra configuration needed.** This works automatically as long as `SIGNAL_ACCOUNT` matches your phone number.
|
||||
|
||||
### Health Monitoring
|
||||
|
||||
The adapter monitors the SSE connection and automatically reconnects if:
|
||||
- The connection drops (with exponential backoff: 2s → 60s)
|
||||
- No activity is detected for 120 seconds (pings signal-cli to verify)
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| **"Cannot reach signal-cli"** during setup | Ensure signal-cli daemon is running: `signal-cli --account +YOUR_NUMBER daemon --http 127.0.0.1:8080` |
|
||||
| **Messages not received** | Check that `SIGNAL_ALLOWED_USERS` includes the sender's number in E.164 format (with `+` prefix) |
|
||||
| **"signal-cli not found on PATH"** | Install signal-cli and ensure it's in your PATH, or use Docker |
|
||||
| **Connection keeps dropping** | Check signal-cli logs for errors. Ensure Java 17+ is installed. |
|
||||
| **Group messages ignored** | Configure `SIGNAL_GROUP_ALLOWED_USERS` with specific group IDs, or `*` to allow all groups. |
|
||||
| **Bot responds to no one** | Configure `SIGNAL_ALLOWED_USERS`, use DM pairing, or explicitly allow all users through gateway policy if you want broader access. |
|
||||
| **Duplicate messages** | Ensure only one signal-cli instance is listening on your phone number |
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
**Always configure access controls.** The bot has terminal access by default. Without `SIGNAL_ALLOWED_USERS` or DM pairing, the gateway denies all incoming messages as a safety measure.
|
||||
:::
|
||||
|
||||
- Phone numbers are redacted in all log output
|
||||
- Use DM pairing or explicit allowlists for safe onboarding of new users
|
||||
- Keep groups disabled unless you specifically need group support, or allowlist only the groups you trust
|
||||
- Signal's end-to-end encryption protects message content in transit
|
||||
- The signal-cli session data in `~/.local/share/signal-cli/` contains account credentials — protect it like a password
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Reference
|
||||
|
||||
| Variable | Required | Default | Description |
|
||||
|----------|----------|---------|-------------|
|
||||
| `SIGNAL_HTTP_URL` | Yes | — | signal-cli HTTP endpoint |
|
||||
| `SIGNAL_ACCOUNT` | Yes | — | Bot phone number (E.164) |
|
||||
| `SIGNAL_ALLOWED_USERS` | No | — | Comma-separated phone numbers/UUIDs |
|
||||
| `SIGNAL_GROUP_ALLOWED_USERS` | No | — | Group IDs to monitor, or `*` for all (omit to disable groups) |
|
||||
| `SIGNAL_ALLOW_ALL_USERS` | No | `false` | Allow any user to interact (skip allowlist) |
|
||||
| `SIGNAL_HOME_CHANNEL` | No | — | Default delivery target for cron jobs |
|
||||
274
hermes_code/website/docs/user-guide/messaging/slack.md
Normal file
274
hermes_code/website/docs/user-guide/messaging/slack.md
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
---
|
||||
sidebar_position: 4
|
||||
title: "Slack"
|
||||
description: "Set up Hermes Agent as a Slack bot using Socket Mode"
|
||||
---
|
||||
|
||||
# Slack Setup
|
||||
|
||||
Connect Hermes Agent to Slack as a bot using Socket Mode. Socket Mode uses WebSockets instead of
|
||||
public HTTP endpoints, so your Hermes instance doesn't need to be publicly accessible — it works
|
||||
behind firewalls, on your laptop, or on a private server.
|
||||
|
||||
:::warning Classic Slack Apps Deprecated
|
||||
Classic Slack apps (using RTM API) were **fully deprecated in March 2025**. Hermes uses the modern
|
||||
Bolt SDK with Socket Mode. If you have an old classic app, you must create a new one following
|
||||
the steps below.
|
||||
:::
|
||||
|
||||
## Overview
|
||||
|
||||
| Component | Value |
|
||||
|-----------|-------|
|
||||
| **Library** | `slack-bolt` / `slack_sdk` for Python (Socket Mode) |
|
||||
| **Connection** | WebSocket — no public URL required |
|
||||
| **Auth tokens needed** | Bot Token (`xoxb-`) + App-Level Token (`xapp-`) |
|
||||
| **User identification** | Slack Member IDs (e.g., `U01ABC2DEF3`) |
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Create a Slack App
|
||||
|
||||
1. Go to [https://api.slack.com/apps](https://api.slack.com/apps)
|
||||
2. Click **Create New App**
|
||||
3. Choose **From scratch**
|
||||
4. Enter an app name (e.g., "Hermes Agent") and select your workspace
|
||||
5. Click **Create App**
|
||||
|
||||
You'll land on the app's **Basic Information** page.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Configure Bot Token Scopes
|
||||
|
||||
Navigate to **Features → OAuth & Permissions** in the sidebar. Scroll to **Scopes → Bot Token Scopes** and add the following:
|
||||
|
||||
| Scope | Purpose |
|
||||
|-------|---------|
|
||||
| `chat:write` | Send messages as the bot |
|
||||
| `app_mentions:read` | Detect when @mentioned in channels |
|
||||
| `channels:history` | Read messages in public channels the bot is in |
|
||||
| `channels:read` | List and get info about public channels |
|
||||
| `groups:history` | Read messages in private channels the bot is invited to |
|
||||
| `im:history` | Read direct message history |
|
||||
| `im:read` | View basic DM info |
|
||||
| `im:write` | Open and manage DMs |
|
||||
| `users:read` | Look up user information |
|
||||
| `files:write` | Upload files (images, audio, documents) |
|
||||
|
||||
:::caution Missing scopes = missing features
|
||||
Without `channels:history` and `groups:history`, the bot **will not receive messages in channels** —
|
||||
it will only work in DMs. These are the most commonly missed scopes.
|
||||
:::
|
||||
|
||||
**Optional scopes:**
|
||||
|
||||
| Scope | Purpose |
|
||||
|-------|---------|
|
||||
| `groups:read` | List and get info about private channels |
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Enable Socket Mode
|
||||
|
||||
Socket Mode lets the bot connect via WebSocket instead of requiring a public URL.
|
||||
|
||||
1. In the sidebar, go to **Settings → Socket Mode**
|
||||
2. Toggle **Enable Socket Mode** to ON
|
||||
3. You'll be prompted to create an **App-Level Token**:
|
||||
- Name it something like `hermes-socket` (the name doesn't matter)
|
||||
- Add the **`connections:write`** scope
|
||||
- Click **Generate**
|
||||
4. **Copy the token** — it starts with `xapp-`. This is your `SLACK_APP_TOKEN`
|
||||
|
||||
:::tip
|
||||
You can always find or regenerate app-level tokens under **Settings → Basic Information → App-Level Tokens**.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Subscribe to Events
|
||||
|
||||
This step is critical — it controls what messages the bot can see.
|
||||
|
||||
|
||||
1. In the sidebar, go to **Features → Event Subscriptions**
|
||||
2. Toggle **Enable Events** to ON
|
||||
3. Expand **Subscribe to bot events** and add:
|
||||
|
||||
| Event | Required? | Purpose |
|
||||
|-------|-----------|---------|
|
||||
| `message.im` | **Yes** | Bot receives direct messages |
|
||||
| `message.channels` | **Yes** | Bot receives messages in **public** channels it's added to |
|
||||
| `message.groups` | **Recommended** | Bot receives messages in **private** channels it's invited to |
|
||||
| `app_mention` | **Yes** | Prevents Bolt SDK errors when bot is @mentioned |
|
||||
|
||||
4. Click **Save Changes** at the bottom of the page
|
||||
|
||||
:::danger Missing event subscriptions is the #1 setup issue
|
||||
If the bot works in DMs but **not in channels**, you almost certainly forgot to add
|
||||
`message.channels` (for public channels) and/or `message.groups` (for private channels).
|
||||
Without these events, Slack simply never delivers channel messages to the bot.
|
||||
:::
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Install App to Workspace
|
||||
|
||||
1. In the sidebar, go to **Settings → Install App**
|
||||
2. Click **Install to Workspace**
|
||||
3. Review the permissions and click **Allow**
|
||||
4. After authorization, you'll see a **Bot User OAuth Token** starting with `xoxb-`
|
||||
5. **Copy this token** — this is your `SLACK_BOT_TOKEN`
|
||||
|
||||
:::tip
|
||||
If you change scopes or event subscriptions later, you **must reinstall the app** for the changes
|
||||
to take effect. The Install App page will show a banner prompting you to do so.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Find User IDs for the Allowlist
|
||||
|
||||
Hermes uses Slack **Member IDs** (not usernames or display names) for the allowlist.
|
||||
|
||||
To find a Member ID:
|
||||
|
||||
1. In Slack, click on the user's name or avatar
|
||||
2. Click **View full profile**
|
||||
3. Click the **⋮** (more) button
|
||||
4. Select **Copy member ID**
|
||||
|
||||
Member IDs look like `U01ABC2DEF3`. You need your own Member ID at minimum.
|
||||
|
||||
---
|
||||
|
||||
## Step 7: Configure Hermes
|
||||
|
||||
Add the following to your `~/.hermes/.env` file:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
SLACK_BOT_TOKEN=xoxb-your-bot-token-here
|
||||
SLACK_APP_TOKEN=xapp-your-app-token-here
|
||||
SLACK_ALLOWED_USERS=U01ABC2DEF3 # Comma-separated Member IDs
|
||||
|
||||
# Optional
|
||||
SLACK_HOME_CHANNEL=C01234567890 # Default channel for cron/scheduled messages
|
||||
SLACK_HOME_CHANNEL_NAME=general # Human-readable name for the home channel (optional)
|
||||
```
|
||||
|
||||
Or run the interactive setup:
|
||||
|
||||
```bash
|
||||
hermes gateway setup # Select Slack when prompted
|
||||
```
|
||||
|
||||
Then start the gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway # Foreground
|
||||
hermes gateway install # Install as a user service
|
||||
sudo hermes gateway install --system # Linux only: boot-time system service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 8: Invite the Bot to Channels
|
||||
|
||||
After starting the gateway, you need to **invite the bot** to any channel where you want it to respond:
|
||||
|
||||
```
|
||||
/invite @Hermes Agent
|
||||
```
|
||||
|
||||
The bot will **not** automatically join channels. You must invite it to each channel individually.
|
||||
|
||||
---
|
||||
|
||||
## How the Bot Responds
|
||||
|
||||
Understanding how Hermes behaves in different contexts:
|
||||
|
||||
| Context | Behavior |
|
||||
|---------|----------|
|
||||
| **DMs** | Bot responds to every message — no @mention needed |
|
||||
| **Channels** | Bot **only responds when @mentioned** (e.g., `@Hermes Agent what time is it?`). In channels, Hermes replies in a thread attached to that message. |
|
||||
| **Threads** | If you @mention Hermes inside an existing thread, it replies in that same thread. |
|
||||
|
||||
:::tip
|
||||
In channels, always @mention the bot. Simply typing a message without mentioning it will be ignored.
|
||||
This is intentional — it prevents the bot from responding to every message in busy channels.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
|
||||
## Home Channel
|
||||
|
||||
Set `SLACK_HOME_CHANNEL` to a channel ID where Hermes will deliver scheduled messages,
|
||||
cron job results, and other proactive notifications. To find a channel ID:
|
||||
|
||||
1. Right-click the channel name in Slack
|
||||
2. Click **View channel details**
|
||||
3. Scroll to the bottom — the Channel ID is shown there
|
||||
|
||||
```bash
|
||||
SLACK_HOME_CHANNEL=C01234567890
|
||||
```
|
||||
|
||||
Make sure the bot has been **invited to the channel** (`/invite @Hermes Agent`).
|
||||
|
||||
---
|
||||
|
||||
## Voice Messages
|
||||
|
||||
Hermes supports voice on Slack:
|
||||
|
||||
- **Incoming:** Voice/audio messages are automatically transcribed using the configured STT provider: local `faster-whisper`, Groq Whisper (`GROQ_API_KEY`), or OpenAI Whisper (`VOICE_TOOLS_OPENAI_KEY`)
|
||||
- **Outgoing:** TTS responses are sent as audio file attachments
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| Bot doesn't respond to DMs | Verify `message.im` is in your event subscriptions and the app is reinstalled |
|
||||
| Bot works in DMs but not in channels | **Most common issue.** Add `message.channels` and `message.groups` to event subscriptions, reinstall the app, and invite the bot to the channel with `/invite @Hermes Agent` |
|
||||
| Bot doesn't respond to @mentions in channels | 1) Check `message.channels` event is subscribed. 2) Bot must be invited to the channel. 3) Ensure `channels:history` scope is added. 4) Reinstall the app after scope/event changes |
|
||||
| Bot ignores messages in private channels | Add both the `message.groups` event subscription and `groups:history` scope, then reinstall the app and `/invite` the bot |
|
||||
| "not_authed" or "invalid_auth" errors | Regenerate your Bot Token and App Token, update `.env` |
|
||||
| Bot responds but can't post in a channel | Invite the bot to the channel with `/invite @Hermes Agent` |
|
||||
| "missing_scope" error | Add the required scope in OAuth & Permissions, then **reinstall** the app |
|
||||
| Socket disconnects frequently | Check your network; Bolt auto-reconnects but unstable connections cause lag |
|
||||
| Changed scopes/events but nothing changed | You **must reinstall** the app to your workspace after any scope or event subscription change |
|
||||
|
||||
### Quick Checklist
|
||||
|
||||
If the bot isn't working in channels, verify **all** of the following:
|
||||
|
||||
1. ✅ `message.channels` event is subscribed (for public channels)
|
||||
2. ✅ `message.groups` event is subscribed (for private channels)
|
||||
3. ✅ `app_mention` event is subscribed
|
||||
4. ✅ `channels:history` scope is added (for public channels)
|
||||
5. ✅ `groups:history` scope is added (for private channels)
|
||||
6. ✅ App was **reinstalled** after adding scopes/events
|
||||
7. ✅ Bot was **invited** to the channel (`/invite @Hermes Agent`)
|
||||
8. ✅ You are **@mentioning** the bot in your message
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
**Always set `SLACK_ALLOWED_USERS`** with the Member IDs of authorized users. Without this setting,
|
||||
the gateway will **deny all messages** by default as a safety measure. Never share your bot tokens —
|
||||
treat them like passwords.
|
||||
:::
|
||||
|
||||
- Tokens should be stored in `~/.hermes/.env` (file permissions `600`)
|
||||
- Rotate tokens periodically via the Slack app settings
|
||||
- Audit who has access to your Hermes config directory
|
||||
- Socket Mode means no public endpoint is exposed — one less attack surface
|
||||
175
hermes_code/website/docs/user-guide/messaging/sms.md
Normal file
175
hermes_code/website/docs/user-guide/messaging/sms.md
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
---
|
||||
sidebar_position: 8
|
||||
title: "SMS (Twilio)"
|
||||
description: "Set up Hermes Agent as an SMS chatbot via Twilio"
|
||||
---
|
||||
|
||||
# SMS Setup (Twilio)
|
||||
|
||||
Hermes connects to SMS through the [Twilio](https://www.twilio.com/) API. People text your Twilio phone number and get AI responses back — same conversational experience as Telegram or Discord, but over standard text messages.
|
||||
|
||||
:::info Shared Credentials
|
||||
The SMS gateway shares credentials with the optional [telephony skill](/docs/reference/skills-catalog). If you've already set up Twilio for voice calls or one-off SMS, the gateway works with the same `TWILIO_ACCOUNT_SID`, `TWILIO_AUTH_TOKEN`, and `TWILIO_PHONE_NUMBER`.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Twilio account** — [Sign up at twilio.com](https://www.twilio.com/try-twilio) (free trial available)
|
||||
- **A Twilio phone number** with SMS capability
|
||||
- **A publicly accessible server** — Twilio sends webhooks to your server when SMS arrives
|
||||
- **aiohttp** — `pip install 'hermes-agent[sms]'`
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Get Your Twilio Credentials
|
||||
|
||||
1. Go to the [Twilio Console](https://console.twilio.com/)
|
||||
2. Copy your **Account SID** and **Auth Token** from the dashboard
|
||||
3. Go to **Phone Numbers → Manage → Active Numbers** — note your phone number in E.164 format (e.g., `+15551234567`)
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Configure Hermes
|
||||
|
||||
### Interactive setup (recommended)
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Select **SMS (Twilio)** from the platform list. The wizard will prompt for your credentials.
|
||||
|
||||
### Manual setup
|
||||
|
||||
Add to `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
TWILIO_AUTH_TOKEN=your_auth_token_here
|
||||
TWILIO_PHONE_NUMBER=+15551234567
|
||||
|
||||
# Security: restrict to specific phone numbers (recommended)
|
||||
SMS_ALLOWED_USERS=+15559876543,+15551112222
|
||||
|
||||
# Optional: set a home channel for cron job delivery
|
||||
SMS_HOME_CHANNEL=+15559876543
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Configure Twilio Webhook
|
||||
|
||||
Twilio needs to know where to send incoming messages. In the [Twilio Console](https://console.twilio.com/):
|
||||
|
||||
1. Go to **Phone Numbers → Manage → Active Numbers**
|
||||
2. Click your phone number
|
||||
3. Under **Messaging → A MESSAGE COMES IN**, set:
|
||||
- **Webhook**: `https://your-server:8080/webhooks/twilio`
|
||||
- **HTTP Method**: `POST`
|
||||
|
||||
:::tip Exposing Your Webhook
|
||||
If you're running Hermes locally, use a tunnel to expose the webhook:
|
||||
|
||||
```bash
|
||||
# Using cloudflared
|
||||
cloudflared tunnel --url http://localhost:8080
|
||||
|
||||
# Using ngrok
|
||||
ngrok http 8080
|
||||
```
|
||||
|
||||
Set the resulting public URL as your Twilio webhook.
|
||||
:::
|
||||
|
||||
The webhook port defaults to `8080`. Override with:
|
||||
|
||||
```bash
|
||||
SMS_WEBHOOK_PORT=3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Start the Gateway
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
You should see:
|
||||
|
||||
```
|
||||
[sms] Twilio webhook server listening on port 8080, from: +1555***4567
|
||||
```
|
||||
|
||||
Text your Twilio number — Hermes will respond via SMS.
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `TWILIO_ACCOUNT_SID` | Yes | Twilio Account SID (starts with `AC`) |
|
||||
| `TWILIO_AUTH_TOKEN` | Yes | Twilio Auth Token |
|
||||
| `TWILIO_PHONE_NUMBER` | Yes | Your Twilio phone number (E.164 format) |
|
||||
| `SMS_WEBHOOK_PORT` | No | Webhook listener port (default: `8080`) |
|
||||
| `SMS_ALLOWED_USERS` | No | Comma-separated E.164 phone numbers allowed to chat |
|
||||
| `SMS_ALLOW_ALL_USERS` | No | Set to `true` to allow anyone (not recommended) |
|
||||
| `SMS_HOME_CHANNEL` | No | Phone number for cron job / notification delivery |
|
||||
| `SMS_HOME_CHANNEL_NAME` | No | Display name for the home channel (default: `Home`) |
|
||||
|
||||
---
|
||||
|
||||
## SMS-Specific Behavior
|
||||
|
||||
- **Plain text only** — Markdown is automatically stripped since SMS renders it as literal characters
|
||||
- **1600 character limit** — Longer responses are split across multiple messages at natural boundaries (newlines, then spaces)
|
||||
- **Echo prevention** — Messages from your own Twilio number are ignored to prevent loops
|
||||
- **Phone number redaction** — Phone numbers are redacted in logs for privacy
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
**The gateway denies all users by default.** Configure an allowlist:
|
||||
|
||||
```bash
|
||||
# Recommended: restrict to specific phone numbers
|
||||
SMS_ALLOWED_USERS=+15559876543,+15551112222
|
||||
|
||||
# Or allow all (NOT recommended for bots with terminal access)
|
||||
SMS_ALLOW_ALL_USERS=true
|
||||
```
|
||||
|
||||
:::warning
|
||||
SMS has no built-in encryption. Don't use SMS for sensitive operations unless you understand the security implications. For sensitive use cases, prefer Signal or Telegram.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Messages not arriving
|
||||
|
||||
1. Check your Twilio webhook URL is correct and publicly accessible
|
||||
2. Verify `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` are correct
|
||||
3. Check the Twilio Console → **Monitor → Logs → Messaging** for delivery errors
|
||||
4. Ensure your phone number is in `SMS_ALLOWED_USERS` (or `SMS_ALLOW_ALL_USERS=true`)
|
||||
|
||||
### Replies not sending
|
||||
|
||||
1. Check `TWILIO_PHONE_NUMBER` is set correctly (E.164 format with `+`)
|
||||
2. Verify your Twilio account has SMS-capable numbers
|
||||
3. Check Hermes gateway logs for Twilio API errors
|
||||
|
||||
### Webhook port conflicts
|
||||
|
||||
If port 8080 is already in use, change it:
|
||||
|
||||
```bash
|
||||
SMS_WEBHOOK_PORT=3001
|
||||
```
|
||||
|
||||
Update the webhook URL in Twilio Console to match.
|
||||
200
hermes_code/website/docs/user-guide/messaging/telegram.md
Normal file
200
hermes_code/website/docs/user-guide/messaging/telegram.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
title: "Telegram"
|
||||
description: "Set up Hermes Agent as a Telegram bot"
|
||||
---
|
||||
|
||||
# Telegram Setup
|
||||
|
||||
Hermes Agent integrates with Telegram as a full-featured conversational bot. Once connected, you can chat with your agent from any device, send voice memos that get auto-transcribed, receive scheduled task results, and use the agent in group chats. The integration is built on [python-telegram-bot](https://python-telegram-bot.org/) and supports text, voice, images, and file attachments.
|
||||
|
||||
## Step 1: Create a Bot via BotFather
|
||||
|
||||
Every Telegram bot requires an API token issued by [@BotFather](https://t.me/BotFather), Telegram's official bot management tool.
|
||||
|
||||
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., "Hermes Agent") — this can be anything
|
||||
4. Choose a **username** — this must be unique and end in `bot` (e.g., `my_hermes_bot`)
|
||||
5. BotFather replies with your **API token**. It looks like this:
|
||||
|
||||
```
|
||||
123456789:ABCdefGHIjklMNOpqrSTUvwxYZ
|
||||
```
|
||||
|
||||
:::warning
|
||||
Keep your bot token secret. Anyone with this token can control your bot. If it leaks, revoke it immediately via `/revoke` in BotFather.
|
||||
:::
|
||||
|
||||
## Step 2: Customize Your Bot (Optional)
|
||||
|
||||
These BotFather commands improve the user experience. Message @BotFather and use:
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `/setdescription` | The "What can this bot do?" text shown before a user starts chatting |
|
||||
| `/setabouttext` | Short text on the bot's profile page |
|
||||
| `/setuserpic` | Upload an avatar for your bot |
|
||||
| `/setcommands` | Define the command menu (the `/` button in chat) |
|
||||
| `/setprivacy` | Control whether the bot sees all group messages (see Step 3) |
|
||||
|
||||
:::tip
|
||||
For `/setcommands`, a useful starting set:
|
||||
|
||||
```
|
||||
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 a **privacy mode** that is **enabled by default**. This is the single most common source of confusion when using bots in groups.
|
||||
|
||||
**With privacy mode ON**, your bot can only see:
|
||||
- Messages that start with a `/` command
|
||||
- Replies directly to the bot's own messages
|
||||
- Service messages (member joins/leaves, pinned messages, etc.)
|
||||
- Messages in channels where the bot is an admin
|
||||
|
||||
**With privacy mode OFF**, the bot receives every message in the group.
|
||||
|
||||
### How to disable privacy mode
|
||||
|
||||
1. Message **@BotFather**
|
||||
2. Send `/mybots`
|
||||
3. Select your bot
|
||||
4. Go to **Bot Settings → Group Privacy → Turn off**
|
||||
|
||||
:::warning
|
||||
**You must remove and re-add the bot to any group** after changing the privacy setting. Telegram caches the privacy state when a bot joins a group, and it will not update until the bot is removed and re-added.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
An alternative to disabling privacy mode: promote the bot to **group admin**. Admin bots always receive all messages regardless of the privacy setting, and this avoids needing to toggle the global privacy mode.
|
||||
:::
|
||||
|
||||
## Step 4: Find Your User ID
|
||||
|
||||
Hermes Agent uses numeric Telegram user IDs to control access. Your user ID is **not** your username — it's a number like `123456789`.
|
||||
|
||||
**Method 1 (recommended):** Message [@userinfobot](https://t.me/userinfobot) — it instantly replies with your user ID.
|
||||
|
||||
**Method 2:** Message [@get_id_bot](https://t.me/get_id_bot) — another reliable option.
|
||||
|
||||
Save this number; you'll need it for the next step.
|
||||
|
||||
## Step 5: Configure Hermes
|
||||
|
||||
### Option A: Interactive Setup (Recommended)
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Select **Telegram** when prompted. The wizard asks for your bot token and allowed user IDs, then writes the configuration for you.
|
||||
|
||||
### Option B: Manual Configuration
|
||||
|
||||
Add the following to `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrSTUvwxYZ
|
||||
TELEGRAM_ALLOWED_USERS=123456789 # Comma-separated for multiple users
|
||||
```
|
||||
|
||||
### Start the Gateway
|
||||
|
||||
```bash
|
||||
hermes gateway
|
||||
```
|
||||
|
||||
The bot should come online within seconds. Send it a message on Telegram to verify.
|
||||
|
||||
## Home Channel
|
||||
|
||||
Use the `/sethome` command in any Telegram chat (DM or group) to designate it as the **home channel**. Scheduled tasks (cron jobs) deliver their results to this channel.
|
||||
|
||||
You can also set it manually in `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
TELEGRAM_HOME_CHANNEL=-1001234567890
|
||||
TELEGRAM_HOME_CHANNEL_NAME="My Notes"
|
||||
```
|
||||
|
||||
:::tip
|
||||
Group chat IDs are negative numbers (e.g., `-1001234567890`). Your personal DM chat ID is the same as your user ID.
|
||||
:::
|
||||
|
||||
## Voice Messages
|
||||
|
||||
### Incoming Voice (Speech-to-Text)
|
||||
|
||||
Voice messages you send on Telegram are automatically transcribed by Hermes's configured STT provider and injected as text into the conversation.
|
||||
|
||||
- `local` uses `faster-whisper` on the machine running Hermes — no API key required
|
||||
- `groq` uses Groq Whisper and requires `GROQ_API_KEY`
|
||||
- `openai` uses OpenAI Whisper and requires `VOICE_TOOLS_OPENAI_KEY`
|
||||
|
||||
### Outgoing Voice (Text-to-Speech)
|
||||
|
||||
When the agent generates audio via TTS, it's delivered as native Telegram **voice bubbles** — the round, inline-playable kind.
|
||||
|
||||
- **OpenAI and ElevenLabs** produce Opus natively — no extra setup needed
|
||||
- **Edge TTS** (the default free provider) outputs MP3 and requires **ffmpeg** to convert to Opus:
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt install ffmpeg
|
||||
|
||||
# macOS
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
Without ffmpeg, Edge TTS audio is sent as a regular audio file (still playable, but uses the rectangular player instead of a voice bubble).
|
||||
|
||||
Configure the TTS provider in your `config.yaml` under the `tts.provider` key.
|
||||
|
||||
## Group Chat Usage
|
||||
|
||||
Hermes Agent works in Telegram group chats with a few considerations:
|
||||
|
||||
- **Privacy mode** determines what messages the bot can see (see [Step 3](#step-3-privacy-mode-critical-for-groups))
|
||||
- When privacy mode is on, **@mention the bot** (e.g., `@my_hermes_bot what's the weather?`) or **reply to its messages** to interact
|
||||
- When privacy mode is off (or bot is admin), the bot sees all messages and can participate naturally
|
||||
- `TELEGRAM_ALLOWED_USERS` still applies — only authorized users can trigger the bot, even in groups
|
||||
|
||||
## Recent Bot API Features (2024–2025)
|
||||
|
||||
- **Privacy policy:** Telegram now requires bots to have a privacy policy. Set one via BotFather with `/setprivacy_policy`, or Telegram may auto-generate a placeholder. This is particularly important if your bot is public-facing.
|
||||
- **Message streaming:** Bot API 9.x added support for streaming long responses, which can improve perceived latency for lengthy agent replies.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| Bot not responding at all | Verify `TELEGRAM_BOT_TOKEN` is correct. Check `hermes gateway` logs for errors. |
|
||||
| Bot responds with "unauthorized" | Your user ID is not in `TELEGRAM_ALLOWED_USERS`. Double-check with @userinfobot. |
|
||||
| Bot ignores group messages | Privacy mode is likely on. Disable it (Step 3) or make the bot a group admin. **Remember to remove and re-add the bot after changing privacy.** |
|
||||
| Voice messages not transcribed | Verify STT is available: install `faster-whisper` for local transcription, or set `GROQ_API_KEY` / `VOICE_TOOLS_OPENAI_KEY` in `~/.hermes/.env`. |
|
||||
| Voice replies are files, not bubbles | Install `ffmpeg` (needed for Edge TTS Opus conversion). |
|
||||
| Bot token revoked/invalid | Generate a new token via `/revoke` then `/newbot` or `/token` in BotFather. Update your `.env` file. |
|
||||
|
||||
## Exec Approval
|
||||
|
||||
When the agent tries to run a potentially dangerous command, it asks you for approval in the chat:
|
||||
|
||||
> ⚠️ This command is potentially dangerous (recursive delete). Reply "yes" to approve.
|
||||
|
||||
Reply "yes"/"y" to approve or "no"/"n" to deny.
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
Always set `TELEGRAM_ALLOWED_USERS` to restrict who can interact with your bot. Without it, the gateway denies all users by default as a safety measure.
|
||||
:::
|
||||
|
||||
Never share your bot token publicly. If compromised, revoke it immediately via BotFather's `/revoke` command.
|
||||
|
||||
For more details, see the [Security documentation](/user-guide/security). You can also use [DM pairing](/user-guide/messaging#dm-pairing-alternative-to-allowlists) for a more dynamic approach to user authorization.
|
||||
310
hermes_code/website/docs/user-guide/messaging/webhooks.md
Normal file
310
hermes_code/website/docs/user-guide/messaging/webhooks.md
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
---
|
||||
sidebar_position: 13
|
||||
title: "Webhooks"
|
||||
description: "Receive events from GitHub, GitLab, and other services to trigger Hermes agent runs"
|
||||
---
|
||||
|
||||
# Webhooks
|
||||
|
||||
Receive events from external services (GitHub, GitLab, JIRA, Stripe, etc.) and trigger Hermes agent runs automatically. The webhook adapter runs an HTTP server that accepts POST requests, validates HMAC signatures, transforms payloads into agent prompts, and routes responses back to the source or to another configured platform.
|
||||
|
||||
The agent processes the event and can respond by posting comments on PRs, sending messages to Telegram/Discord, or logging the result.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Enable via `hermes gateway setup` or environment variables
|
||||
2. Define webhook routes in `config.yaml`
|
||||
3. Point your service at `http://your-server:8644/webhooks/<route-name>`
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
There are two ways to enable the webhook adapter.
|
||||
|
||||
### Via setup wizard
|
||||
|
||||
```bash
|
||||
hermes gateway setup
|
||||
```
|
||||
|
||||
Follow the prompts to enable webhooks, set the port, and set a global HMAC secret.
|
||||
|
||||
### Via environment variables
|
||||
|
||||
Add to `~/.hermes/.env`:
|
||||
|
||||
```bash
|
||||
WEBHOOK_ENABLED=true
|
||||
WEBHOOK_PORT=8644 # default
|
||||
WEBHOOK_SECRET=your-global-secret
|
||||
```
|
||||
|
||||
### Verify the server
|
||||
|
||||
Once the gateway is running:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8644/health
|
||||
```
|
||||
|
||||
Expected response:
|
||||
|
||||
```json
|
||||
{"status": "ok", "platform": "webhook"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuring Routes {#configuring-routes}
|
||||
|
||||
Routes define how different webhook sources are handled. Each route is a named entry under `platforms.webhook.extra.routes` in your `config.yaml`.
|
||||
|
||||
### Route properties
|
||||
|
||||
| Property | Required | Description |
|
||||
|----------|----------|-------------|
|
||||
| `events` | No | List of event types to accept (e.g. `["pull_request"]`). If empty, all events are accepted. Event type is read from `X-GitHub-Event`, `X-GitLab-Event`, or `event_type` in the payload. |
|
||||
| `secret` | **Yes** | HMAC secret for signature validation. Falls back to the global `secret` if not set on the route. Set to `"INSECURE_NO_AUTH"` for testing only (skips validation). |
|
||||
| `prompt` | No | Template string with dot-notation payload access (e.g. `{pull_request.title}`). If omitted, the full JSON payload is dumped into the prompt. |
|
||||
| `skills` | No | List of skill names to load for the agent run. |
|
||||
| `deliver` | No | Where to send the response: `github_comment`, `telegram`, `discord`, `slack`, `signal`, `sms`, or `log` (default). |
|
||||
| `deliver_extra` | No | Additional delivery config — keys depend on `deliver` type (e.g. `repo`, `pr_number`, `chat_id`). Values support the same `{dot.notation}` templates as `prompt`. |
|
||||
|
||||
### Full example
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
extra:
|
||||
port: 8644
|
||||
secret: "global-fallback-secret"
|
||||
routes:
|
||||
github-pr:
|
||||
events: ["pull_request"]
|
||||
secret: "github-webhook-secret"
|
||||
prompt: |
|
||||
Review this pull request:
|
||||
Repository: {repository.full_name}
|
||||
PR #{number}: {pull_request.title}
|
||||
Author: {pull_request.user.login}
|
||||
URL: {pull_request.html_url}
|
||||
Diff URL: {pull_request.diff_url}
|
||||
Action: {action}
|
||||
skills: ["github-code-review"]
|
||||
deliver: "github_comment"
|
||||
deliver_extra:
|
||||
repo: "{repository.full_name}"
|
||||
pr_number: "{number}"
|
||||
deploy-notify:
|
||||
events: ["push"]
|
||||
secret: "deploy-secret"
|
||||
prompt: "New push to {repository.full_name} branch {ref}: {head_commit.message}"
|
||||
deliver: "telegram"
|
||||
```
|
||||
|
||||
### Prompt Templates
|
||||
|
||||
Prompts use dot-notation to access nested fields in the webhook payload:
|
||||
|
||||
- `{pull_request.title}` resolves to `payload["pull_request"]["title"]`
|
||||
- `{repository.full_name}` resolves to `payload["repository"]["full_name"]`
|
||||
- Missing keys are left as the literal `{key}` string (no error)
|
||||
- Nested dicts and lists are JSON-serialized and truncated at 2000 characters
|
||||
|
||||
If no `prompt` template is configured for a route, the entire payload is dumped as indented JSON (truncated at 4000 characters).
|
||||
|
||||
The same dot-notation templates work in `deliver_extra` values.
|
||||
|
||||
---
|
||||
|
||||
## GitHub PR Review (Step by Step) {#github-pr-review}
|
||||
|
||||
This walkthrough sets up automatic code review on every pull request.
|
||||
|
||||
### 1. Create the webhook in GitHub
|
||||
|
||||
1. Go to your repository → **Settings** → **Webhooks** → **Add webhook**
|
||||
2. Set **Payload URL** to `http://your-server:8644/webhooks/github-pr`
|
||||
3. Set **Content type** to `application/json`
|
||||
4. Set **Secret** to match your route config (e.g. `github-webhook-secret`)
|
||||
5. Under **Which events?**, select **Let me select individual events** and check **Pull requests**
|
||||
6. Click **Add webhook**
|
||||
|
||||
### 2. Add the route config
|
||||
|
||||
Add the `github-pr` route to your `~/.hermes/config.yaml` as shown in the example above.
|
||||
|
||||
### 3. Ensure `gh` CLI is authenticated
|
||||
|
||||
The `github_comment` delivery type uses the GitHub CLI to post comments:
|
||||
|
||||
```bash
|
||||
gh auth login
|
||||
```
|
||||
|
||||
### 4. Test it
|
||||
|
||||
Open a pull request on the repository. The webhook fires, Hermes processes the event, and posts a review comment on the PR.
|
||||
|
||||
---
|
||||
|
||||
## GitLab Webhook Setup {#gitlab-webhook-setup}
|
||||
|
||||
GitLab webhooks work similarly but use a different authentication mechanism. GitLab sends the secret as a plain `X-Gitlab-Token` header (exact string match, not HMAC).
|
||||
|
||||
### 1. Create the webhook in GitLab
|
||||
|
||||
1. Go to your project → **Settings** → **Webhooks**
|
||||
2. Set the **URL** to `http://your-server:8644/webhooks/gitlab-mr`
|
||||
3. Enter your **Secret token**
|
||||
4. Select **Merge request events** (and any other events you want)
|
||||
5. Click **Add webhook**
|
||||
|
||||
### 2. Add the route config
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
enabled: true
|
||||
extra:
|
||||
routes:
|
||||
gitlab-mr:
|
||||
events: ["merge_request"]
|
||||
secret: "your-gitlab-secret-token"
|
||||
prompt: |
|
||||
Review this merge request:
|
||||
Project: {project.path_with_namespace}
|
||||
MR !{object_attributes.iid}: {object_attributes.title}
|
||||
Author: {object_attributes.last_commit.author.name}
|
||||
URL: {object_attributes.url}
|
||||
Action: {object_attributes.action}
|
||||
deliver: "log"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Delivery Options {#delivery-options}
|
||||
|
||||
The `deliver` field controls where the agent's response goes after processing the webhook event.
|
||||
|
||||
| Deliver Type | Description |
|
||||
|-------------|-------------|
|
||||
| `log` | Logs the response to the gateway log output. This is the default and is useful for testing. |
|
||||
| `github_comment` | Posts the response as a PR/issue comment via the `gh` CLI. Requires `deliver_extra.repo` and `deliver_extra.pr_number`. The `gh` CLI must be installed and authenticated on the gateway host (`gh auth login`). |
|
||||
| `telegram` | Routes the response to Telegram. Uses the home channel, or specify `chat_id` in `deliver_extra`. |
|
||||
| `discord` | Routes the response to Discord. Uses the home channel, or specify `chat_id` in `deliver_extra`. |
|
||||
| `slack` | Routes the response to Slack. Uses the home channel, or specify `chat_id` in `deliver_extra`. |
|
||||
| `signal` | Routes the response to Signal. Uses the home channel, or specify `chat_id` in `deliver_extra`. |
|
||||
| `sms` | Routes the response to SMS via Twilio. Uses the home channel, or specify `chat_id` in `deliver_extra`. |
|
||||
|
||||
For cross-platform delivery (telegram, discord, slack, signal, sms), the target platform must also be enabled and connected in the gateway. If no `chat_id` is provided in `deliver_extra`, the response is sent to that platform's configured home channel.
|
||||
|
||||
---
|
||||
|
||||
## Security {#security}
|
||||
|
||||
The webhook adapter includes multiple layers of security:
|
||||
|
||||
### HMAC signature validation
|
||||
|
||||
The adapter validates incoming webhook signatures using the appropriate method for each source:
|
||||
|
||||
- **GitHub**: `X-Hub-Signature-256` header — HMAC-SHA256 hex digest prefixed with `sha256=`
|
||||
- **GitLab**: `X-Gitlab-Token` header — plain secret string match
|
||||
- **Generic**: `X-Webhook-Signature` header — raw HMAC-SHA256 hex digest
|
||||
|
||||
If a secret is configured but no recognized signature header is present, the request is rejected.
|
||||
|
||||
### Secret is required
|
||||
|
||||
Every route must have a secret — either set directly on the route or inherited from the global `secret`. Routes without a secret cause the adapter to fail at startup with an error. For development/testing only, you can set the secret to `"INSECURE_NO_AUTH"` to skip validation entirely.
|
||||
|
||||
### Rate limiting
|
||||
|
||||
Each route is rate-limited to **30 requests per minute** by default (fixed-window). Configure this globally:
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
extra:
|
||||
rate_limit: 60 # requests per minute
|
||||
```
|
||||
|
||||
Requests exceeding the limit receive a `429 Too Many Requests` response.
|
||||
|
||||
### Idempotency
|
||||
|
||||
Delivery IDs (from `X-GitHub-Delivery`, `X-Request-ID`, or a timestamp fallback) are cached for **1 hour**. Duplicate deliveries (e.g. webhook retries) are silently skipped with a `200` response, preventing duplicate agent runs.
|
||||
|
||||
### Body size limits
|
||||
|
||||
Payloads exceeding **1 MB** are rejected before the body is read. Configure this:
|
||||
|
||||
```yaml
|
||||
platforms:
|
||||
webhook:
|
||||
extra:
|
||||
max_body_bytes: 2097152 # 2 MB
|
||||
```
|
||||
|
||||
### Prompt injection risk
|
||||
|
||||
:::warning
|
||||
Webhook payloads contain attacker-controlled data — PR titles, commit messages, issue descriptions, etc. can all contain malicious instructions. Run the gateway in a sandboxed environment (Docker, VM) when exposed to the internet. Consider using the Docker or SSH terminal backend for isolation.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting {#troubleshooting}
|
||||
|
||||
### Webhook not arriving
|
||||
|
||||
- Verify the port is exposed and accessible from the webhook source
|
||||
- Check firewall rules — port `8644` (or your configured port) must be open
|
||||
- Verify the URL path matches: `http://your-server:8644/webhooks/<route-name>`
|
||||
- Use the `/health` endpoint to confirm the server is running
|
||||
|
||||
### Signature validation failing
|
||||
|
||||
- Ensure the secret in your route config exactly matches the secret configured in the webhook source
|
||||
- For GitHub, the secret is HMAC-based — check `X-Hub-Signature-256`
|
||||
- For GitLab, the secret is a plain token match — check `X-Gitlab-Token`
|
||||
- Check gateway logs for `Invalid signature` warnings
|
||||
|
||||
### Event being ignored
|
||||
|
||||
- Check that the event type is in your route's `events` list
|
||||
- GitHub events use values like `pull_request`, `push`, `issues` (the `X-GitHub-Event` header value)
|
||||
- GitLab events use values like `merge_request`, `push` (the `X-GitLab-Event` header value)
|
||||
- If `events` is empty or not set, all events are accepted
|
||||
|
||||
### Agent not responding
|
||||
|
||||
- Run the gateway in foreground to see logs: `hermes gateway run`
|
||||
- Check that the prompt template is rendering correctly
|
||||
- Verify the delivery target is configured and connected
|
||||
|
||||
### Duplicate responses
|
||||
|
||||
- The idempotency cache should prevent this — check that the webhook source is sending a delivery ID header (`X-GitHub-Delivery` or `X-Request-ID`)
|
||||
- Delivery IDs are cached for 1 hour
|
||||
|
||||
### `gh` CLI errors (GitHub comment delivery)
|
||||
|
||||
- Run `gh auth login` on the gateway host
|
||||
- Ensure the authenticated GitHub user has write access to the repository
|
||||
- Check that `gh` is installed and on the PATH
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables {#environment-variables}
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `WEBHOOK_ENABLED` | Enable the webhook platform adapter | `false` |
|
||||
| `WEBHOOK_PORT` | HTTP server port for receiving webhooks | `8644` |
|
||||
| `WEBHOOK_SECRET` | Global HMAC secret (used as fallback when routes don't specify their own) | _(none)_ |
|
||||
200
hermes_code/website/docs/user-guide/messaging/whatsapp.md
Normal file
200
hermes_code/website/docs/user-guide/messaging/whatsapp.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
---
|
||||
sidebar_position: 5
|
||||
title: "WhatsApp"
|
||||
description: "Set up Hermes Agent as a WhatsApp bot via the built-in Baileys bridge"
|
||||
---
|
||||
|
||||
# WhatsApp Setup
|
||||
|
||||
Hermes connects to WhatsApp through a built-in bridge based on **Baileys**. This works by emulating a WhatsApp Web session — **not** through the official WhatsApp Business API. No Meta developer account or Business verification is required.
|
||||
|
||||
:::warning Unofficial API — Ban Risk
|
||||
WhatsApp does **not** officially support third-party bots outside the Business API. Using a third-party bridge carries a small risk of account restrictions. To minimize risk:
|
||||
- **Use a dedicated phone number** for the bot (not your personal number)
|
||||
- **Don't send bulk/spam messages** — keep usage conversational
|
||||
- **Don't automate outbound messaging** to people who haven't messaged first
|
||||
:::
|
||||
|
||||
:::warning WhatsApp Web Protocol Updates
|
||||
WhatsApp periodically updates their Web protocol, which can temporarily break compatibility
|
||||
with third-party bridges. When this happens, Hermes will update the bridge dependency. If the
|
||||
bot stops working after a WhatsApp update, pull the latest Hermes version and re-pair.
|
||||
:::
|
||||
|
||||
## Two Modes
|
||||
|
||||
| Mode | How it works | Best for |
|
||||
|------|-------------|----------|
|
||||
| **Separate bot number** (recommended) | Dedicate a phone number to the bot. People message that number directly. | Clean UX, multiple users, lower ban risk |
|
||||
| **Personal self-chat** | Use your own WhatsApp. You message yourself to talk to the agent. | Quick setup, single user, testing |
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Node.js v18+** and **npm** — the WhatsApp bridge runs as a Node.js process
|
||||
- **A phone with WhatsApp** installed (for scanning the QR code)
|
||||
|
||||
Unlike older browser-driven bridges, the current Baileys-based bridge does **not** require a local Chromium or Puppeteer dependency stack.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Run the Setup Wizard
|
||||
|
||||
```bash
|
||||
hermes whatsapp
|
||||
```
|
||||
|
||||
The wizard will:
|
||||
|
||||
1. Ask which mode you want (**bot** or **self-chat**)
|
||||
2. Install bridge dependencies if needed
|
||||
3. Display a **QR code** in your terminal
|
||||
4. Wait for you to scan it
|
||||
|
||||
**To scan the QR code:**
|
||||
|
||||
1. Open WhatsApp on your phone
|
||||
2. Go to **Settings → Linked Devices**
|
||||
3. Tap **Link a Device**
|
||||
4. Point your camera at the terminal QR code
|
||||
|
||||
Once paired, the wizard confirms the connection and exits. Your session is saved automatically.
|
||||
|
||||
:::tip
|
||||
If the QR code looks garbled, make sure your terminal is at least 60 columns wide and supports
|
||||
Unicode. You can also try a different terminal emulator.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Getting a Second Phone Number (Bot Mode)
|
||||
|
||||
For bot mode, you need a phone number that isn't already registered with WhatsApp. Three options:
|
||||
|
||||
| Option | Cost | Notes |
|
||||
|--------|------|-------|
|
||||
| **Google Voice** | Free | US only. Get a number at [voice.google.com](https://voice.google.com). Verify WhatsApp via SMS through the Google Voice app. |
|
||||
| **Prepaid SIM** | $5–15 one-time | Any carrier. Activate, verify WhatsApp, then the SIM can sit in a drawer. Number must stay active (make a call every 90 days). |
|
||||
| **VoIP services** | Free–$5/month | TextNow, TextFree, or similar. Some VoIP numbers are blocked by WhatsApp — try a few if the first doesn't work. |
|
||||
|
||||
After getting the number:
|
||||
|
||||
1. Install WhatsApp on a phone (or use WhatsApp Business app with dual-SIM)
|
||||
2. Register the new number with WhatsApp
|
||||
3. Run `hermes whatsapp` and scan the QR code from that WhatsApp account
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Configure Hermes
|
||||
|
||||
Add the following to your `~/.hermes/.env` file:
|
||||
|
||||
```bash
|
||||
# Required
|
||||
WHATSAPP_ENABLED=true
|
||||
WHATSAPP_MODE=bot # "bot" or "self-chat"
|
||||
WHATSAPP_ALLOWED_USERS=15551234567 # Comma-separated phone numbers (with country code, no +)
|
||||
```
|
||||
|
||||
Optional behavior settings in `~/.hermes/config.yaml`:
|
||||
|
||||
```yaml
|
||||
unauthorized_dm_behavior: pair
|
||||
|
||||
whatsapp:
|
||||
unauthorized_dm_behavior: ignore
|
||||
```
|
||||
|
||||
- `unauthorized_dm_behavior: pair` is the global default. Unknown DM senders get a pairing code.
|
||||
- `whatsapp.unauthorized_dm_behavior: ignore` makes WhatsApp stay silent for unauthorized DMs, which is usually the better choice for a private number.
|
||||
|
||||
Then start the gateway:
|
||||
|
||||
```bash
|
||||
hermes gateway # Foreground
|
||||
hermes gateway install # Install as a user service
|
||||
sudo hermes gateway install --system # Linux only: boot-time system service
|
||||
```
|
||||
|
||||
The gateway starts the WhatsApp bridge automatically using the saved session.
|
||||
|
||||
---
|
||||
|
||||
## Session Persistence
|
||||
|
||||
The Baileys bridge saves its session under `~/.hermes/whatsapp/session`. This means:
|
||||
|
||||
- **Sessions survive restarts** — you don't need to re-scan the QR code every time
|
||||
- The session data includes encryption keys and device credentials
|
||||
- **Do not share or commit this session directory** — it grants full access to the WhatsApp account
|
||||
|
||||
---
|
||||
|
||||
## Re-pairing
|
||||
|
||||
If the session breaks (phone reset, WhatsApp update, manually unlinked), you'll see connection
|
||||
errors in the gateway logs. To fix it:
|
||||
|
||||
```bash
|
||||
hermes whatsapp
|
||||
```
|
||||
|
||||
This generates a fresh QR code. Scan it again and the session is re-established. The gateway
|
||||
handles **temporary** disconnections (network blips, phone going offline briefly) automatically
|
||||
with reconnection logic.
|
||||
|
||||
---
|
||||
|
||||
## Voice Messages
|
||||
|
||||
Hermes supports voice on WhatsApp:
|
||||
|
||||
- **Incoming:** Voice messages (`.ogg` opus) are automatically transcribed using the configured STT provider: local `faster-whisper`, Groq Whisper (`GROQ_API_KEY`), or OpenAI Whisper (`VOICE_TOOLS_OPENAI_KEY`)
|
||||
- **Outgoing:** TTS responses are sent as MP3 audio file attachments
|
||||
- Agent responses are prefixed with "⚕ **Hermes Agent**" by default. You can customize or disable this in `config.yaml`:
|
||||
|
||||
```yaml
|
||||
# ~/.hermes/config.yaml
|
||||
whatsapp:
|
||||
reply_prefix: "" # Empty string disables the header
|
||||
# reply_prefix: "🤖 *My Bot*\n──────\n" # Custom prefix (supports \n for newlines)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| **QR code not scanning** | Ensure terminal is wide enough (60+ columns). Try a different terminal. Make sure you're scanning from the correct WhatsApp account (bot number, not personal). |
|
||||
| **QR code expires** | QR codes refresh every ~20 seconds. If it times out, restart `hermes whatsapp`. |
|
||||
| **Session not persisting** | Check that `~/.hermes/whatsapp/session` exists and is writable. If containerized, mount it as a persistent volume. |
|
||||
| **Logged out unexpectedly** | WhatsApp unlinks devices after long inactivity. Keep the phone on and connected to the network, then re-pair with `hermes whatsapp` if needed. |
|
||||
| **Bridge crashes or reconnect loops** | Restart the gateway, update Hermes, and re-pair if the session was invalidated by a WhatsApp protocol change. |
|
||||
| **Bot stops working after WhatsApp update** | Update Hermes to get the latest bridge version, then re-pair. |
|
||||
| **Messages not being received** | Verify `WHATSAPP_ALLOWED_USERS` includes the sender's number (with country code, no `+` or spaces). |
|
||||
| **Bot replies to strangers with a pairing code** | Set `whatsapp.unauthorized_dm_behavior: ignore` in `~/.hermes/config.yaml` if you want unauthorized DMs to be silently ignored instead. |
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
:::warning
|
||||
**Always set `WHATSAPP_ALLOWED_USERS`** with phone numbers (including country code, without the `+`)
|
||||
of authorized users. Without this setting, the gateway will **deny all incoming messages** as a
|
||||
safety measure.
|
||||
:::
|
||||
|
||||
By default, unauthorized DMs still receive a pairing code reply. If you want a private WhatsApp number to stay completely silent to strangers, set:
|
||||
|
||||
```yaml
|
||||
whatsapp:
|
||||
unauthorized_dm_behavior: ignore
|
||||
```
|
||||
|
||||
- The `~/.hermes/whatsapp/session` directory contains full session credentials — protect it like a password
|
||||
- Set file permissions: `chmod 700 ~/.hermes/whatsapp/session`
|
||||
- Use a **dedicated phone number** for the bot to isolate risk from your personal account
|
||||
- If you suspect compromise, unlink the device from WhatsApp → Settings → Linked Devices
|
||||
- Phone numbers in logs are partially redacted, but review your log retention policy
|
||||
Loading…
Add table
Add a link
Reference in a new issue