A clean-room, open-source node client for the rabbit r1 remote-control protocol —
a faster, hackable drop-in for the official rabbit-agent. It lets your rabbit r1 device
drive local CLIs (Claude Code, Hermes, OpenClaw, or anything else) over the same secure
control channel, and it removes the biggest source of command latency in the stock client.
Status: early. Core protocol client + improved execution model. Not affiliated with or endorsed by rabbit inc.
The latest openrabbit work expands Rabbit's existing Claude Code, Hermes, and OpenClaw integrations with a faster, more resilient local node and first-class multi-agent workflows:
- 🎙️ Switch Hermes agents by voice — say “switch to Fin,” “switch to Crypto,” or another allowed profile without leaving the current r1 conversation. One node can serve multiple isolated Hermes agents, each with its own model, tools, memory, and purpose.
- 🛡️ Self-healing routing and hardened sessions — recover from connected-but-unproductive control backends, stalled sessions, failed switches, and orphaned process trees without creating endless reconnect loops.
- 🔒 Privacy-safe Claude diagnostics — diagnose stream-json startup and compatibility problems using structural metadata only; prompts, credentials, raw payloads, and session identifiers are not logged.
- ⚡ Faster execution and warm sessions — direct in-process command spawning, optional pre-warmed sessions, and no per-command agent re-exec remove most avoidable startup overhead.
- 🧭 Multi-agent fleet supervision — run one selectable r1 node per agent, or combine the fleet behind one voice-switchable Hermes node, with Windows, macOS, and Linux supervision.
Jump to: Install · Configuration · Voice switching · Reliability · Benchmark
The official client is great, but every remote command it runs in "background job" mode re-launches the entire agent binary as a child process just to spawn one command, then streams output by polling files on disk. On a cold cache that's ~1.7 s of pure overhead before your CLI even starts. openrabbit-node keeps one long-lived process and spawns your command directly, with optional warm workers — so commands start noticeably faster.
This client was written from a protocol specification, not from rabbit's source. It
reimplements the documented wire protocol (endpoints, message shapes, ed25519 handshake)
for interoperability only. No proprietary code, assets, or binaries from the official
client are included or derived here. See docs/PROTOCOL.md.
If you are on the rabbit team and have concerns, please open an issue — this project exists to make the r1 experience better and is happy to align with your guidance.
- ✅ ed25519 device identity (generate, or reuse an existing
private.key) - ✅ Registration + signed control-WebSocket handshake
- ✅ Heartbeat + exponential-backoff reconnect
- ✅ Full control-message set: env detection, streaming processes, one-shot commands, file read/write, background jobs (create/attach/stdin/detach/delete/list)
- ⚡ No per-command re-exec — commands spawn directly in-process
- ⚡ Optional warm pool for frequently-used commands
- ⚡ Direct command resolution (skips the
cmd.exeshell hop where safe)
- Bun ≥ 1.3 — the only build/runtime dependency (there are zero runtime
npm deps). Install:
curl -fsSL https://bun.sh/install | bash, or on Windowspowershell -c "irm bun.sh/install.ps1 | iex". - A rabbit r1 device and a rabbithole account to register the node against.
- The CLI(s) you want to drive, installed and on your
PATH— openrabbit runs them, it does not install them. For example Claude Code (claude), Hermes (hermes), OpenClaw (openclaw), or any other command. - Platform: developed on Windows; macOS/Linux also work (use
fleet/openrabbit-fleet.shfor the fleet supervisor).
git clone https://github.com/vcruz305/openrabbit.git
cd openrabbit
bun install1. Register this machine as a node. Get a token at hole.rabbit.tech → Nodes → "Register node" (copy the token it shows you), then:
bun run src/index.ts register --token <REGISTRATION_TOKEN>This creates an ed25519 identity in ~/.openrabbit-node/ and claims the node id.
2. Run the node. It connects to the rabbit service and waits for your r1:
bun run src/index.ts run3. On the r1, the node now appears as a selectable "computer" — pick it and press-to-talk
to drive whichever CLI you point it at (see below). bun run src/index.ts doctor prints a
diagnostic health check any time.
doctor is automation-safe: it exits nonzero when registration, authenticated service access,
CLI resolution, or the Claude version probe fails. A successfully parsed Claude Code version
below stable 2.1.208 (including a 2.1.208-* prerelease) prints an advisory WARN but does not
change the exit code. The probe accepts Claude's semver forms such as 2.1.215 (Claude Code) and
Claude Code version 2.1.215; malformed or four-component versions are reported as probe failures.
Build a standalone binary (what the fleet supervisor runs):
bun run compile # -> dist/openrabbit-node(.exe)| Var | Default | Meaning |
|---|---|---|
NODE_SERVICE_URL |
https://os3-ns.rabbit.tech |
rabbit node service base URL |
OPENRABBIT_DATA_DIR |
~/.openrabbit-node |
where identity + jobs live |
OPENRABBIT_NODE_NAME |
(hostname) | name this node reports (how it shows on the r1) |
OPENRABBIT_HERMES_PROFILE |
`` | pin hermes sessions to a profile (hermes -p <profile> acp) |
OPENRABBIT_HERMES_SWITCH |
0 |
enable voice-switching Hermes profiles mid-session — say "switch to <profile>" (see below) |
OPENRABBIT_HERMES_PROFILES |
`` | comma-list of switchable profiles, e.g. default,crypto,fin (required for switching) |
OPENRABBIT_INTENT_ROUTING |
0 |
enable intent-based routing: classify every voice prompt against profile keyword sets and auto-swap to the best match — no "switch to" needed |
OPENRABBIT_ROUTING_PROFILES |
`` | per-profile keyword mapping, format: crypto:bitcoin,eth,defi;coding:review,bug,refactor (requires OPENRABBIT_INTENT_ROUTING=1) |
OPENRABBIT_CLAUDE_HOOK_FILTER |
1 |
drop hook_started/hook_response from Claude stream-json (see KNOWN-ISSUES) |
OPENRABBIT_SESSION_STALL_SECONDS |
45 |
kill + self-heal a session that gets a prompt but never answers (0 disables) |
OPENRABBIT_WARM_SESSIONS |
1 |
keep a pre-initialized session warm per command so repeats start near-instantly |
OPENRABBIT_FILE_ROOT |
`` | confine file read/write/list to this dir tree (see SECURITY.md) |
OPENRABBIT_LOG_LEVEL |
info |
debug | info | warn | error |
CONNECTION_TIMEOUT_SECONDS |
60 |
dead-connection timeout |
RECONNECT_BASE_DELAY_SECONDS |
1 |
reconnect backoff base |
RECONNECT_MAX_DELAY_SECONDS |
30 |
reconnect backoff cap |
OPENRABBIT_ROUTING_PROBATION_SECONDS |
90 |
rebind a startup socket that receives only ping/environment housekeeping; clamped to 0..3600 (0 disables) |
OPENRABBIT_ROUTING_PROBATION_REBINDS |
3 |
maximum consecutive unproven startup rebinds before remaining connected; integer-clamped to 0..10 |
OPENRABBIT_WARM_POOL |
`` | comma-list of commands to keep warm |
If you run several agents (e.g. multiple Hermes profiles), run one node per agent. Each
becomes its own selectable "computer" on the r1, pinned to a profile with a distinct name — so
you pick the agent by picking the computer. Managed by fleet/openrabbit-fleet.ps1:
# one-time per agent (get a token at hole.rabbit.tech > nodes > register node)
fleet\openrabbit-fleet.ps1 register crypto hermes-crypto <TOKEN>
fleet\openrabbit-fleet.ps1 register fin hermes-fin <TOKEN>
fleet\openrabbit-fleet.ps1 list # show nodes + status
fleet\openrabbit-fleet.ps1 install-service # auto-start all nodes at logon + keep alive
fleet\openrabbit-fleet.ps1 run # start now
fleet\openrabbit-fleet.ps1 stop all # stopThe supervisor restarts any node that dies and starts the whole fleet at logon. On macOS/Linux
use fleet/openrabbit-fleet.sh (same commands; install-service installs a launchd agent on
macOS or a systemd user service on Linux).
Instead of one node per Hermes profile, a single node can host all your Hermes agents and switch between them by voice — just say "switch to <profile>" mid-conversation and the node hot-swaps the profile behind the live session (Claude and OpenClaw on the same node are unaffected). This makes the separate per-agent nodes above optional: one neutrally-named node gives you Claude-as-Claude plus every Hermes agent on voice command.
Enable it per node:
OPENRABBIT_HERMES_SWITCH=1
OPENRABBIT_HERMES_PROFILES=default,crypto,fin,jerry
(or in fleet.json, add "hermesProfiles": ["default","crypto","fin","jerry"] to a node).
Say "switch to crypto", "switch to fin", … or "switch to default" for the unpinned profile.
Matching tolerates speech-to-text near-misses (e.g. "fint" → fin).
How it works: the r1 speaks the Agent Client Protocol
(newline-delimited JSON-RPC) to one Hermes session. openrabbit proxies it: on a switch phrase it
respawns hermes -p <profile> acp, replays the initialize/session/new handshake, and
translates the ACP sessionId on every frame so the r1 never sees the swap. See
src/acp/switch.ts; the scripts/simulate-r1.mjs harness exercises it end-to-end without a
device. A locally consumed switch prompt receives exactly one ACP terminal response after the
switch succeeds or fails; ordinary forwarded prompts are completed only by the active agent.
openrabbit treats a live WebSocket and a working job route as two different health signals.
During a bounded startup probation, housekeeping-only traffic can trigger a clean
re-registration so Rabbit can select another control backend. Rebinds are capped, so a healthy
but idle node does not reconnect forever. See
OPENRABBIT_ROUTING_PROBATION_SECONDS and
OPENRABBIT_ROUTING_PROBATION_REBINDS.
The execution layer also owns active and retiring process trees through shutdown, fences stale session generations, bounds output and write queues, and can replace a session that accepts a prompt but never responds. Hermes profile switches complete their intercepted ACP request exactly once on success or failure, preventing the r1 from remaining stuck in a loading state.
For Claude Code, debug diagnostics report only safe stream-json metadata such as byte count,
line-ending shape, message type, and whether content is empty. They do not log prompts, raw
payloads, credentials, or session identifiers. doctor also warns—without failing the node—when
the installed Claude Code version predates the stable Windows stream-json fix in 2.1.208.
bench/latency.mjs — same command through each client's per-job path: ~24 ms (openrabbit)
vs ~1663 ms (stock), i.e. ~1.6 s saved per command. See bench/RESULTS.md.
src/
config.ts env-overridable configuration
logger.ts structured JSON logging
identity.ts ed25519 keypair + challenge signing
protocol.ts control-message types
environment.ts os/arch/shell/runtime detection
registration.ts REST: register + fetch control-WS URL
controlWs.ts WebSocket client: heartbeat, reconnect, dispatch
executor.ts command / streamProcess / backgroundJob (no re-exec)
warmPool.ts optional pre-warmed workers
warmSessions.ts warm per-signature session pool
acp/switch.ts voice-switching of Hermes agents (ACP proxy)
index.ts CLI entry (register / run / doctor)
scripts/simulate-r1.mjs local r1 simulator — test switching without a device
fleet/ multi-node supervisor (openrabbit-fleet.ps1 / .sh)
docs/ PROTOCOL.md (wire spec) · ROADMAP.md (what's next)
bench/ latency A/B vs the stock client
Planned work (voice-friendly output shaping, tool-approval-by-voice, a dangerous-command
guard, transcripts, and more) lives in docs/ROADMAP.md. Issues and PRs
welcome — see CONTRIBUTING.md.
Licensed under the Apache License, Version 2.0 © 2026 Victor Cruz. See LICENSE and NOTICE. Not affiliated with or endorsed by rabbit inc.
