II.
Page JSON
Structured · livepage:docs-hermes-research-raw-gateway-internals
Gateway Internals json
Inspect the normalized record payload exactly as the atlas UI reads it.
{
"id": "page:docs-hermes-research-raw-gateway-internals",
"_kind": "Page",
"_file": "wiki/docs/hermes-research/raw/gateway-internals.md",
"_cluster": "wiki",
"attributes": {
"nodeKind": "Page",
"sourcePath": "docs/hermes-research/raw/gateway-internals.md",
"sourceKind": "repo-docs",
"title": "Gateway Internals",
"displayName": "Gateway Internals",
"slug": "docs/hermes-research/raw/gateway-internals",
"articlePath": "wiki/docs/hermes-research/raw/gateway-internals.md",
"article": "\n# Gateway Internals\n\n> Source: https://hermes-agent.nousresearch.com/docs/developer-guide/gateway-internals\n\n## Key Files\n\n| File | Purpose |\n|------|---------|\n| `gateway/run.py` | Main loop, slash commands, message dispatch |\n| `gateway/session.py` | Conversation persistence and session key construction |\n| `gateway/delivery.py` | Outbound message delivery to target platforms |\n| `gateway/pairing.py` | DM pairing flow for user authorization |\n| `gateway/channel_directory.py` | Maps chat IDs to human-readable names |\n| `gateway/hooks.py` | Hook discovery, loading, lifecycle dispatch |\n| `gateway/mirror.py` | Cross-session message mirroring |\n| `gateway/status.py` | Token lock management for gateway instances |\n| `gateway/platforms/` | Platform adapters (one per messaging platform) |\n\n## Architecture Overview\n\nThe messaging gateway connects Hermes to 20+ external messaging platforms through platform adapters that normalize events into a unified `MessageEvent` format. All adapters route through a central `GatewayRunner` that handles authorization, session management, and command dispatch.\n\n## Message Flow\n\nWhen a message arrives:\n\n1. Platform adapter normalizes raw event into `MessageEvent`\n2. Base adapter checks active session guard (queues if agent running)\n3. `GatewayRunner._handle_message()` resolves session key via format: `agent:main:{platform}:{chat_type}:{chat_id}`\n4. Authorization checks are performed\n5. Slash commands are dispatched to handlers\n6. Running agent checks intercept commands like `/stop`\n7. Response sent back through platform adapter\n\n## Two-Level Message Guard\n\n**Level 1 -- Base adapter**: Checks `_active_sessions`; if active, queues message and sets interrupt event.\n\n**Level 2 -- Gateway runner**: Checks `_running_agents`; intercepts specific commands (`/stop`, `/new`, `/queue`, `/status`, `/approve`, `/deny`) for appropriate routing.\n\nCommands reaching the runner while agent is blocked are dispatched inline via `await self._message_handler(event)` to avoid race conditions.\n\n## Authorization\n\nMulti-layer checks evaluated in order:\n\n1. Per-platform allow-all flag (e.g., `TELEGRAM_ALLOW_ALL_USERS`)\n2. Platform allowlist (e.g., `TELEGRAM_ALLOWED_USERS`)\n3. DM pairing (authenticated users pair new users)\n4. Global allow-all (`GATEWAY_ALLOW_ALL_USERS`)\n5. Default: deny\n\n### DM Pairing Flow\n\nAdmin initiates with `/pair`, gateway generates code (e.g., \"ABC123\"), new user shares code, gateway confirms pairing and authorizes user. Pairing state persists in `gateway/pairing.py`.\n\n## Slash Command Dispatch\n\n1. `resolve_command()` maps input to canonical name (handles aliases)\n2. Canonical name checked against `GATEWAY_KNOWN_COMMANDS`\n3. Handler dispatches based on canonical name\n4. Some commands gated on config via `gateway_config_gate`\n\nRunning agents reject non-bypass commands early with message: \"Agent is running -- wait or `/stop` first.\"\n\n## Config Sources\n\n| Source | Provides |\n|--------|----------|\n| `~/.hermes/.env` | API keys, bot tokens, credentials |\n| `~/.hermes/config.yaml` | Model settings, tool config, display options |\n| Environment variables | Overrides for above |\n\nGateway reads `config.yaml` directly via YAML loader, unlike CLI which uses hardcoded defaults.\n\n## Platform Adapters\n\n20+ adapters in `gateway/platforms/` including Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, SMS, Mattermost, and others.\n\nCommon interface:\n- `connect()` / `disconnect()` -- lifecycle management\n- `send_message()` -- outbound delivery\n- `on_message()` -- inbound normalization\n\n### Token Locks\n\nAdapters with unique credentials call `acquire_scoped_lock()` in `connect()` and `release_scoped_lock()` in `disconnect()` to prevent concurrent profile access with same bot token.\n\n## Delivery Path\n\nHandles:\n- Direct reply to originating chat\n- Home channel delivery for cron/background results\n- Explicit target delivery via `send_message` tool\n- Cross-platform delivery\n\nCron deliveries intentionally not mirrored into gateway session history.\n\n## Hooks\n\nPython modules responding to lifecycle events discovered from `gateway/builtin_hooks/` and `~/.hermes/hooks/`. Each hook has `HOOK.yaml` manifest and `handler.py`.\n\n### Hook Events\n\n`gateway:startup`, `session:start`, `session:end`, `session:reset`, `agent:start`, `agent:step`, `agent:end`, `command:*`\n\n## Memory Provider Integration\n\nWhen memory provider enabled: gateway creates `AIAgent` per message, `MemoryManager` initializes provider, provider tools routed through `AIAgent._invoke_tool()`, on session end/reset `on_session_end()` fires for cleanup.\n\n## Process Management\n\nGateway runs long-lived via:\n- `hermes gateway start` / `hermes gateway stop` -- manual control\n- `systemctl` (Linux) or `launchctl` (macOS) -- service management\n- PID file at `~/.hermes/gateway.pid` -- profile-scoped tracking\n\nProfile-scoped processes allow independent gateway instances; `--all` flag kills all profiles.\n",
"documents": []
},
"outgoingEdges": [],
"incomingEdges": [
{
"from": "page:docs-hermes-research",
"to": "page:docs-hermes-research-raw-gateway-internals",
"kind": "contains_page"
}
]
}