Compare commits

...

3 Commits

Author SHA1 Message Date
3c9a1d9010 feat(matrix): Add Matrix CLI commands for fleet ops
Some checks failed
Forge CI / smoke-and-build (pull_request) Failing after 31s
Part of #274. Adds `hermes matrix` command with test, send, status, and setup subcommands.
2026-04-14 00:38:06 +00:00
56f5df8ade feat(matrix): Add fleet ops channel documentation
Part of #274. Comprehensive guide for setting up Matrix fleet ops channel to replace Telegram notifications.
2026-04-14 00:34:56 +00:00
b68d6c7259 feat(matrix): Add Matrix configuration to .env.example
Part of #274. Adds Matrix environment variables for fleet ops channel setup.
2026-04-14 00:34:22 +00:00
3 changed files with 300 additions and 50 deletions

View File

@@ -257,6 +257,42 @@ BROWSER_INACTIVITY_TIMEOUT=120
# TELEGRAM_WEBHOOK_PORT=8443
# TELEGRAM_WEBHOOK_SECRET= # Recommended for production
# =============================================================================
# MATRIX INTEGRATION
# =============================================================================
# Matrix is the sovereign messaging protocol: federated, E2EE, no corporation.
#
# For fleet ops notifications (replacing Telegram), configure these variables.
# See docs/matrix-setup.md for full setup guide.
# Required: Your homeserver URL
# MATRIX_HOMESERVER=https://matrix-client.matrix.org # Public homeserver for testing
# MATRIX_HOMESERVER=https://matrix.your.domain.com # Self-hosted (recommended)
# Required: Bot access token (get from Element: Settings -> Help & About -> Advanced)
# MATRIX_ACCESS_TOKEN=***
# Required: Bot user ID
# MATRIX_USER_ID=@hermes-bot:matrix.org
# Required: Device ID (from login response)
# MATRIX_DEVICE_ID=HERMES_BOT
# Fleet ops channel: Where system notifications, cron results, and alerts go
# Create a room in Element, invite the bot, then get the room ID from Room Settings
# MATRIX_HOME_CHANNEL=!room-id:matrix.org
# MATRIX_HOME_CHANNEL_NAME=Fleet Ops
# Crisis channel: For SOUL.md protocol and urgent alerts
# MATRIX_CRISIS_CHANNEL=!crisis-room-id:matrix.org
# MATRIX_CRISIS_CHANNEL_NAME=Crisis Room
# E2EE Support: Install matrix-nio with encryption support
# pip install "matrix-nio[e2e]"
# Requires libolm C library: brew install libolm (macOS) or apt install libolm-dev (Linux)
# =============================================================================
# WhatsApp (built-in Baileys bridge — run `hermes whatsapp` to pair)
# WHATSAPP_ENABLED=false
# WHATSAPP_ALLOWED_USERS=15551234567

View File

@@ -214,58 +214,98 @@ matrix:
- "!room1:matrix.org"
```
## Fleet Ops Channel (Phase 4)
The fleet ops channel replaces Telegram for system notifications, cron job results, and operational alerts.
### 1. Create Fleet Ops Room
1. Open Element (or any Matrix client)
2. Create a new room named "Fleet Ops" or "Hermes Ops"
3. Set room to **invite-only** (not public)
4. Invite your bot: `@hermes-bot:your.domain.com`
5. Get the room ID from Room Settings -> Advanced -> Internal Room ID
- Format: `!randomstring:your.domain.com`
### 2. Configure Environment
Add to `~/.hermes/.env`:
```bash
# Fleet ops channel
MATRIX_HOME_CHANNEL=!your-room-id:matrix.org
MATRIX_HOME_CHANNEL_NAME=Fleet Ops
```
### 3. Test Fleet Ops
```bash
# Test Matrix connection
hermes matrix test
# Send a test message to fleet ops channel
hermes matrix send "Fleet ops channel test"
# Check cron job delivery
hermes cron list
# Jobs with deliver=origin will now deliver to Matrix if configured
```
### 4. Configure Cron Delivery
Cron jobs can deliver results to Matrix instead of Telegram:
```bash
# Create a job that delivers to Matrix
hermes cron create "Check system health" --schedule "every 1h" --deliver matrix:home
# Or deliver to a specific Matrix room
hermes cron create "Backup status" --schedule "every 6h" --deliver matrix:!room-id:matrix.org
```
### 5. Fleet Ops Notifications
The following notifications go to the fleet ops channel:
- **Cron job results**: Success/failure of scheduled tasks
- **System alerts**: Memory, disk, GPU usage warnings
- **Agent status**: Model changes, provider switches, errors
- **Security events**: Auth failures, suspicious activity
### 6. Crisis Room (Optional)
For urgent alerts (SOUL.md protocol), create a separate crisis room:
```bash
# Crisis channel for urgent alerts
MATRIX_CRISIS_CHANNEL=!crisis-room-id:matrix.org
MATRIX_CRISIS_CHANNEL_NAME=Crisis Room
```
### 7. Migrate from Telegram
Once Matrix is working:
1. Update cron jobs to deliver to Matrix: `--deliver matrix:home`
2. Test all critical notifications
3. Disable Telegram delivery for migrated jobs
4. Monitor both channels during transition
## Troubleshooting
### "Matrix: need MATRIX_ACCESS_TOKEN or MATRIX_USER_ID + MATRIX_PASSWORD"
### Bot not receiving messages
- Check bot has permission to read room
- Verify E2EE is working (see E2EE section)
- Check `MATRIX_HOME_CHANNEL` is set correctly
Neither auth method is configured. Set `MATRIX_ACCESS_TOKEN` in `~/.hermes/.env`
or provide `MATRIX_USER_ID` + `MATRIX_PASSWORD`.
### Messages not sending
- Verify `MATRIX_ACCESS_TOKEN` is valid
- Check homeserver is reachable
- Look at gateway logs: `hermes gateway logs`
### "Matrix: whoami failed"
### E2EE issues
- Install: `pip install "matrix-nio[e2e]"`
- Install libolm: `brew install libolm` (macOS) or `apt install libolm-dev` (Linux)
- Restart gateway after installing
The access token is invalid or expired. Generate a new one via the login API.
### "Matrix: E2EE dependencies are missing"
Install libolm and matrix-nio with E2EE support:
```bash
brew install libolm # macOS
pip install "matrix-nio[e2e]"
```
### "Matrix: login failed"
- Check username and password.
- Ensure the account exists on the target homeserver.
- Some homeservers require admin approval for new registrations.
### Bot Not Responding in Rooms
1. Check `MATRIX_REQUIRE_MENTION` — if `true` (default), messages must
@mention the bot.
2. Check `MATRIX_ALLOWED_USERS` — if set, only listed users can interact.
3. Check logs: `tail -f ~/.hermes/logs/gateway.log`
### E2EE Rooms Show "Unable to Decrypt"
1. Ensure `MATRIX_DEVICE_ID` is set to a stable value.
2. Check that `~/.hermes/platforms/matrix/store/` has read/write permissions.
3. Verify libolm is installed: `python -c "from nio.crypto import ENCRYPTION_ENABLED; print(ENCRYPTION_ENABLED)"`
### Slow Message Delivery
Matrix federation can add latency. For faster responses:
- Use the same homeserver for the bot and users.
- Set `MATRIX_HOME_ROOM` to a local room.
- Check network connectivity between Hermes and the homeserver.
## Quick Start (Automated)
Run the interactive setup script:
```bash
python scripts/setup_matrix.py
```
This guides you through homeserver selection, authentication, and verification.

View File

@@ -662,6 +662,153 @@ def cmd_chat(args):
sys.exit(1)
def cmd_matrix(args):
"""Handle Matrix integration commands."""
from hermes_cli.colors import Colors, color
import os
subcmd = getattr(args, 'matrix_command', None)
if subcmd is None or subcmd == "status":
# Show Matrix configuration status
print(color("\n=== Matrix Integration Status ===\n", Colors.CYAN))
homeserver = os.getenv("MATRIX_HOMESERVER", "")
access_token = os.getenv("MATRIX_ACCESS_TOKEN", "")
user_id = os.getenv("MATRIX_USER_ID", "")
device_id = os.getenv("MATRIX_DEVICE_ID", "")
home_channel = os.getenv("MATRIX_HOME_CHANNEL", "")
crisis_channel = os.getenv("MATRIX_CRISIS_CHANNEL", "")
if not homeserver:
print(color("✗ Matrix not configured", Colors.RED))
print(" Set MATRIX_HOMESERVER, MATRIX_ACCESS_TOKEN, MATRIX_USER_ID in ~/.hermes/.env")
print(" See docs/matrix-setup.md for setup guide")
return 1
print(color("✓ Homeserver:", Colors.GREEN), homeserver)
print(color("✓ User ID:", Colors.GREEN) if user_id else color("✗ User ID: not set", Colors.RED))
print(color("✓ Access Token:", Colors.GREEN) if access_token else color("✗ Access Token: not set", Colors.RED))
print(color("✓ Device ID:", Colors.GREEN) if device_id else color("✗ Device ID: not set", Colors.YELLOW))
print()
print(color("Channels:", Colors.CYAN))
if home_channel:
print(color(" ✓ Fleet Ops:", Colors.GREEN), home_channel)
else:
print(color(" ✗ Fleet Ops: not configured", Colors.YELLOW))
print(" Set MATRIX_HOME_CHANNEL for system notifications")
if crisis_channel:
print(color(" ✓ Crisis Room:", Colors.GREEN), crisis_channel)
else:
print(color(" ○ Crisis Room: not configured", Colors.DIM))
# Check E2EE support
try:
import nio
print(color(" ✓ E2EE support: available", Colors.GREEN))
except ImportError:
print(color(" ✗ E2EE support: missing (pip install 'matrix-nio[e2e]')", Colors.YELLOW))
return 0
if subcmd == "test":
# Test Matrix connection
print(color("Testing Matrix connection...", Colors.CYAN))
homeserver = os.getenv("MATRIX_HOMESERVER", "")
access_token = os.getenv("MATRIX_ACCESS_TOKEN", "")
if not homeserver or not access_token:
print(color("✗ Matrix not configured", Colors.RED))
return 1
try:
import urllib.request, json, ssl
ctx = ssl.create_default_context()
# Test homeserver reachability
req = urllib.request.Request(f"{homeserver}/_matrix/client/versions")
resp = urllib.request.urlopen(req, context=ctx, timeout=10)
versions = json.loads(resp.read())
print(color("✓ Homeserver reachable", Colors.GREEN))
# Test authentication
req = urllib.request.Request(
f"{homeserver}/_matrix/client/v3/account/whoami",
headers={"Authorization": f"Bearer {access_token}"}
)
resp = urllib.request.urlopen(req, context=ctx, timeout=10)
whoami = json.loads(resp.read())
print(color(f"✓ Authenticated as: {whoami.get('user_id')}", Colors.GREEN))
# Test home channel if configured
home_channel = os.getenv("MATRIX_HOME_CHANNEL", "")
if home_channel:
print(color(f"✓ Fleet Ops channel configured: {home_channel}", Colors.GREEN))
print(color("\n✓ Matrix integration working!", Colors.GREEN))
return 0
except Exception as e:
print(color(f"✗ Matrix test failed: {e}", Colors.RED))
return 1
if subcmd == "send":
# Send message to Matrix
message = args.message
channel = getattr(args, 'channel', None) or os.getenv("MATRIX_HOME_CHANNEL", "")
if not channel:
print(color("✗ No channel specified and MATRIX_HOME_CHANNEL not set", Colors.RED))
return 1
print(color(f"Sending to Matrix channel: {channel}", Colors.CYAN))
# Use the send_message_tool
try:
import asyncio
from tools.send_message_tool import _send_matrix
homeserver = os.getenv("MATRIX_HOMESERVER", "")
access_token = os.getenv("MATRIX_ACCESS_TOKEN", "")
if not homeserver or not access_token:
print(color("✗ Matrix not configured", Colors.RED))
return 1
result = asyncio.run(_send_matrix(access_token, {"homeserver": homeserver}, channel, message))
if result.get("success"):
print(color("✓ Message sent", Colors.GREEN))
return 0
else:
print(color(f"✗ Send failed: {result.get('error')}", Colors.RED))
return 1
except Exception as e:
print(color(f"✗ Error: {e}", Colors.RED))
return 1
if subcmd == "setup":
print(color("Matrix Setup Wizard", Colors.CYAN))
print("\nTo set up Matrix integration:")
print("1. See docs/matrix-setup.md for full guide")
print("2. Set environment variables in ~/.hermes/.env:")
print(" MATRIX_HOMESERVER=https://your-homeserver.com")
print(" MATRIX_ACCESS_TOKEN=your-token")
print(" MATRIX_USER_ID=@bot:your-homeserver.com")
print(" MATRIX_HOME_CHANNEL=!room-id:your-homeserver.com")
print("3. Run: hermes matrix test")
print("4. Create cron jobs with --deliver matrix:home")
return 0
print(color(f"Unknown matrix command: {subcmd}", Colors.RED))
return 1
def cmd_gateway(args):
"""Gateway management commands."""
from hermes_cli.gateway import gateway_command
@@ -4272,6 +4419,33 @@ For more help on a command:
)
chat_parser.set_defaults(func=cmd_chat)
# =========================================================================
# matrix command
# =========================================================================
matrix_parser = subparsers.add_parser(
"matrix",
help="Matrix sovereign messaging integration",
description="Manage Matrix integration for fleet ops and notifications."
)
matrix_subparsers = matrix_parser.add_subparsers(dest="matrix_command")
# Matrix test command
matrix_test = matrix_subparsers.add_parser("test", help="Test Matrix connection and configuration")
# Matrix send command
matrix_send = matrix_subparsers.add_parser("send", help="Send a message to Matrix channel")
matrix_send.add_argument("message", help="Message to send")
matrix_send.add_argument("--channel", "-c", help="Channel to send to (default: home channel)")
# Matrix status command
matrix_status = matrix_subparsers.add_parser("status", help="Show Matrix integration status")
# Matrix setup command
matrix_setup = matrix_subparsers.add_parser("setup", help="Interactive Matrix setup wizard")
matrix_parser.set_defaults(func=cmd_matrix)
# =========================================================================
# model command
# =========================================================================