{
"$type": "site.standard.document",
"content": {
"$type": "site.subgraph.content.markdown",
"body": "\nA Rabbit R1 sits on my desk next to an M1 MacBook Pro. A few days ago I [got it talking to a Pinata-hosted OpenClaw agent](/posts/1774911232779-openclaw-and-the-rabbit-r1). The MacBook runs Hermes, a self-hosted AI agent on Opus 4.6 (switching between Anthropic and OpenAI as needed). It handles my Telegram messages, runs cron jobs, writes code, and remembers things across sessions. The whole stack runs on 8 cores and 16 GB of RAM under macOS 26.3.1. It's been up for 12 days.\n\nI wanted the R1 to talk to the same agent. Same memory, same tools, same conversation history. A proper channel into Hermes, like Telegram already is. The [shim is on GitHub](https://github.com/iammatthias/r1-hermes-shim) with an `llms.txt` that lets your Hermes agent handle the setup.\n\n## The Creation SDK Dead End\n\nRabbit has an open-source [Creations SDK](https://github.com/rabbit-hmi-oss/creations-sdk) for building tiny web apps on the R1's 240×282px screen. We built a working prototype: a chat UI backed by a Python server exposed over Tailscale Funnel. Got it running on the device in under an hour.\n\nCreations are apps. Sandboxed mini-programs you launch from a menu. We wanted Hermes to *be* the R1's brain, the thing it talks to when you hold the push-to-talk button.\n\n## The OpenClaw Shim\n\nRabbit ships a setup script at `rabbit.tech/r1-openclaw.sh` that pairs the R1 with an [OpenClaw](https://github.com/openclaw/openclaw) instance over WebSocket. The same pairing flow from [the Pinata setup](/posts/1774911232779-openclaw-and-the-rabbit-r1), minus the auth proxy workaround. OpenClaw is a multi-channel AI gateway. Hermes has a similar gateway architecture. We didn't need OpenClaw. We needed to speak the same protocol.\n\nThe script generates a QR code with a JSON bootstrap payload: the MacBook's LAN address, a port, and a token. The R1 scans it and connects over WebSocket.\n\nThe QR payload:\n```json\n{\n \"type\": \"clawdbot-gateway\",\n \"version\": 1,\n \"ips\": [\"192.168.50.42\"],\n \"port\": 18789,\n \"token\": \"***\",\n \"protocol\": \"ws\"\n}\n```\n\nWe pulled the OpenClaw npm package (182 MB unpacked, 21,000+ files) and read the source. The protocol is a challenge-response handshake that pairs the device, then messages flow as `chat.send` from the client, ack'd by the server, streamed back as delta/final event pairs.\n\nAuth runs two layers: a gateway token from the QR for the first connection, and a per-device token issued during pairing for every reconnect. The R1 caches its device token. If the server only accepts the gateway token, reconnection fails.\n\n## Two Iterations\n\n**Standalone Python server.** An `aiohttp` WebSocket server implementing enough protocol to pair and respond. The R1 connected, paired, sent a test message.\n\n**Gateway platform adapter.** The Hermes gateway runs as a launchd LaunchAgent on this Mac and manages platform connections. The R1 shim became another platform adapter, like Telegram.\n\n```\nTelegram Adapter ─┐\nDiscord Adapter ─┤── GatewayRunner._handle_message() ── AIAgent\nR1 Shim Adapter ─┘\n```\n\nEvery adapter converts platform-specific messages into a common event format, then calls the shared `_handle_message` pipeline. Hermes is open-source, and the gateway has a clean adapter pattern. Adding the R1 meant one new file (~300 lines) and small patches to three existing ones, all uncommitted on top of upstream `main`.\n\n## Five Bugs\n\nDevice token rejection: the R1 caches its pairing token and uses it on reconnect, but our first shim only accepted the gateway token.\n\nWrong response event type: we sent `event: \"agent\"` for responses, but the R1 expected `event: \"chat\"` with a specific message shape. The spinner spun forever.\n\nPlatform enum mismatch: the adapter registered as one platform type, the session source used another. Auth rejected everything. One-line fix.\n\nMissing toolset registration. Stale `.pyc` bytecache. Both quick fixes once spotted.\n\n## Where It Stands\n\nThe R1 talks to the same agent that answers my Telegram messages. Same persistent memory, same session store in SQLite, same tools.\n\n```\n✓ telegram connected\n✓ r1_shim connected\nGateway running with 2 platform(s)\n```\n\nThe whole stack runs on a single M1 MacBook Pro with 16 GB of RAM: gateway, both adapters, browser daemons, and a web API server running concurrently. 748 processes across the system, load average around 1.5.\n\nStreaming sends responses as a single delta+final pair. Token-by-token streaming would be better. Voice and camera aren't wired yet.\n\n## Architecture\n\n```\n┌──────────┐ WebSocket (OpenClaw protocol) ┌─────────────────┐\n│ Rabbit R1 │ ◄──────────────────────────────────► │ r1_shim adapter │\n│ (device) │ QR bootstrap → pair → chat.send │ (port 18789) │\n└──────────┘ └────────┬────────┘\n │\n _handle_message()\n │\n ┌────────▼────────┐\n │ GatewayRunner │\n │ Opus/Codex/etc │\n │ Python 3.11 │\n │ M1 MacBook Pro │\n └────────┬────────┘\n │\n ┌──────────────┼──────────────┐\n │ │ │\n ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐\n │ Sessions │ │ Memory │ │ Tools │\n │ (SQLite) │ │ (files) │ │ (terminal │\n └───────────┘ └───────────┘ │ web, etc)│\n └───────────┘\n```\n\n~300 lines of new Python for the shim. ~15 lines of patches across 3 upstream files. [Repo and `llms.txt` here.](https://github.com/iammatthias/r1-hermes-shim)\n"
},
"description": "I replaced the OpenClaw agent on my R1 with Hermes, running locally on a MacBook Pro.",
"publishedAt": "2026-04-01T18:59:34.473Z",
"site": "at://did:plc:p5xem22ammiafn5kxonaksfa/site.standard.publication/3mlozj4rwek2k",
"tags": [
"ai",
"rabbit-r1",
"agents",
"hardware",
"hermes",
"openclaw"
],
"textContent": "A Rabbit R1 sits on my desk next to an M1 MacBook Pro. A few days ago I got it talking to a Pinata-hosted OpenClaw agent. The MacBook runs Hermes, a self-hosted AI agent on Opus 4.6 (switching between Anthropic and OpenAI as needed). It handles my Telegram messages, runs cron jobs, writes code, and remembers things across sessions. The whole stack runs on 8 cores and 16 GB of RAM under macOS 26.3.1. It's been up for 12 days.\n\nI wanted the R1 to talk to the same agent. Same memory, same tools, same conversation history. A proper channel into Hermes, like Telegram already is. The shim is on GitHub with an llms.txt that lets your Hermes agent handle the setup.\n\nThe Creation SDK Dead End\n\nRabbit has an open-source Creations SDK for building tiny web apps on the R1's 240×282px screen. We built a working prototype: a chat UI backed by a Python server exposed over Tailscale Funnel. Got it running on the device in under an hour.\n\nCreations are apps. Sandboxed mini-programs you launch from a menu. We wanted Hermes to be the R1's brain, the thing it talks to when you hold the push-to-talk button.\n\nThe OpenClaw Shim\n\nRabbit ships a setup script at rabbit.tech/r1-openclaw.sh that pairs the R1 with an OpenClaw instance over WebSocket. The same pairing flow from the Pinata setup, minus the auth proxy workaround. OpenClaw is a multi-channel AI gateway. Hermes has a similar gateway architecture. We didn't need OpenClaw. We needed to speak the same protocol.\n\nThe script generates a QR code with a JSON bootstrap payload: the MacBook's LAN address, a port, and a token. The R1 scans it and connects over WebSocket.\n\nThe QR payload:\n\n{ \"type\": \"clawdbot-gateway\", \"version\": 1, \"ips\": [\"192.168.50.42\"], \"port\": 18789, \"token\": \"***\", \"protocol\": \"ws\" }\n\nWe pulled the OpenClaw npm package (182 MB unpacked, 21,000+ files) and read the source. The protocol is a challenge-response handshake that pairs the device, then messages flow as chat.send from the client, ack'd by the server, streamed back as delta/final event pairs.\n\nAuth runs two layers: a gateway token from the QR for the first connection, and a per-device token issued during pairing for every reconnect. The R1 caches its device token. If the server only accepts the gateway token, reconnection fails.\n\nTwo Iterations\n\nStandalone Python server. An aiohttp WebSocket server implementing enough protocol to pair and respond. The R1 connected, paired, sent a test message.\n\nGateway platform adapter. The Hermes gateway runs as a launchd LaunchAgent on this Mac and manages platform connections. The R1 shim became another platform adapter, like Telegram.\n\nTelegram Adapter ─┐ Discord Adapter ─┤── GatewayRunner._handle_message() ── AIAgent R1 Shim Adapter ─┘\n\nEvery adapter converts platform-specific messages into a common event format, then calls the shared _handle_message pipeline. Hermes is open-source, and the gateway has a clean adapter pattern. Adding the R1 meant one new file (~300 lines) and small patches to three existing ones, all uncommitted on top of upstream main.\n\nFive Bugs\n\nDevice token rejection: the R1 caches its pairing token and uses it on reconnect, but our first shim only accepted the gateway token.\n\nWrong response event type: we sent event: \"agent\" for responses, but the R1 expected event: \"chat\" with a specific message shape. The spinner spun forever.\n\nPlatform enum mismatch: the adapter registered as one platform type, the session source used another. Auth rejected everything. One-line fix.\n\nMissing toolset registration. Stale .pyc bytecache. Both quick fixes once spotted.\n\nWhere It Stands\n\nThe R1 talks to the same agent that answers my Telegram messages. Same persistent memory, same session store in SQLite, same tools.\n\n✓ telegram connected ✓ r1_shim connected Gateway running with 2 platform(s)\n\nThe whole stack runs on a single M1 MacBook Pro with 16 GB of RAM: gateway, both adapters, browser daemons, and a web API server running concurrently. 748 processes across the system, load average around 1.5.\n\nStreaming sends responses as a single delta+final pair. Token-by-token streaming would be better. Voice and camera aren't wired yet.\n\nArchitecture\n\n┌──────────┐ WebSocket (OpenClaw protocol) ┌─────────────────┐ │ Rabbit R1 │ ◄──────────────────────────────────► │ r1_shim adapter │ │ (device) │ QR bootstrap → pair → chat.send │ (port 18789) │ └──────────┘ └────────┬────────┘ │ _handle_message() │ ┌────────▼────────┐ │ GatewayRunner │ │ Opus/Codex/etc │ │ Python 3.11 │ │ M1 MacBook Pro │ └────────┬────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │ Sessions │ │ Memory │ │ Tools │ │ (SQLite) │ │ (files) │ │ (terminal │ └───────────┘ └───────────┘ │ web, etc)│ └───────────┘\n\n~300 lines of new Python for the shim. ~15 lines of patches across 3 upstream files. Repo and llms.txt here.",
"title": "Replacing a Rabbit's Brain: Connecting the R1 to Hermes"
}