Skip to content

Architecture

Repowire is a local-first routing daemon plus thin transport adapters for each agent runtime.

Agent runtime
  ├─ hooks + MCP (Claude Code, Codex, Gemini)
  ├─ plugin + WebSocket (OpenCode)
  ├─ extension (Pi)
  └─ channel / ACP transport (Claude Code experimental)
HTTP/WebSocket daemon on 127.0.0.1:8377
Dashboard, Telegram, Slack, orchestrator peers, relay, and other peers

The daemon is the single routing hub. It does not care whether a peer arrived through hooks, an OpenCode plugin, the Pi extension path, a bot, relay traffic, or experimental channel/ACP delivery. Every peer is represented in the registry and routes messages through the same core message layer.

Repowire architecture diagram

Core modules

Area Files Responsibility
Agent backends repowire/agent_types.py, repowire/agent_backends.py Serialized backend identity plus setup, spawn, resume, MCP config, and post-spawn behavior dispatch
Daemon app repowire/daemon/app.py, repowire/daemon/deps.py FastAPI app factory, dependency wiring, dashboard/static serving
Peer state repowire/daemon/peer_registry.py Registration, liveness, circles, roles, lazy repair
Message routing repowire/daemon/peer_delivery.py, repowire/daemon/transport_router.py, repowire/daemon/message_router.py, repowire/daemon/websocket_transport.py Delivery orchestration, ACP-before-WebSocket transport selection, and wire delivery over connected peers
Ask lifecycle repowire/daemon/ask_tracker.py, repowire/daemon/routes/asks.py Open ask state, pending reminders, close/ack handling
Session controls repowire/daemon/session_controls.py, repowire/daemon/routes/sessions.py Session-binding resolution and session-targeted control capability routes
Session acquisition repowire/daemon/session_control.py, repowire/daemon/state/operations.py Durable operation records and live-executor acquisition for session/job work
Schedules repowire/daemon/scheduler.py, repowire/daemon/schedule_store.py, repowire/daemon/routes/schedules.py One-shot and recurring cron deliveries
Jobs repowire/daemon/job_runner.py, repowire/daemon/state/work.py, repowire/daemon/state/calendar.py, repowire/daemon/routes/work.py Durable tracked work, recurring calendar templates, and spawned job dispatch
Hooks repowire/hooks/ Runtime event adapters, tmux injection, transcript/chat extraction
MCP server repowire/mcp/server.py Agent-facing tools over stdio
Control surfaces web/, repowire/telegram/bot.py, repowire/slack/bot.py Dashboard and human/service peers
Relay repowire/relay/server.py, repowire/daemon/relay_client.py Hosted remote dashboard and cross-machine tunnel

Transports

Hooks + MCP

Claude Code, Codex, and Gemini use lifecycle hooks for registration/status/chat extraction and MCP tools for outbound commands. The hook adapter normalizes each runtime's event names and response fields.

Default message delivery still uses tmux injection plus Stop-hook reminders for unacked asks. The MCP server lazily registers on tool calls so runtimes that initialize late, especially Codex, still get a peer identity before routing.

OpenCode plugin and Pi extension

OpenCode does not expose the same hook shape, so Repowire installs a TypeScript plugin. The plugin holds a WebSocket connection to the daemon and bridges OpenCode session events into the same peer/message model. Pi uses Repowire's extension path when setup detects the pi CLI or config.

Channel / ACP transport

repowire setup --experimental-channels installs Claude Code's experimental channel/ACP transport. Messages arrive as <channel source="repowire"> tags, while legacy query replies route through a channel reply tool. The normal repowire mcp server remains installed for stable tools such as ask, ack, notify_peer, schedules, and peer listing. This requires Claude Code support, claude.ai login, and bun. Treat this path as experimental; hooks + MCP remain the default.

Relay

The daemon connects outbound to the hosted relay over WSS. The relay tunnels dashboard HTTP/SSE calls and bridges WebSocket traffic without requiring inbound access to the user's machine.

Lazy repair

Repowire avoids polling loops. Liveness repair, persistence flushes, and ghost cleanup are piggy-backed on user-visible requests, bounded by cooldowns. The design rule is: repair when needed, not on timers.

When lazy repair detects a self-inconsistent peer (e.g. online but no live WebSocket, a missing pane, or a dead agent pid) it emits a peer_contradiction event once per transition, so silent failures surface in the dashboard stream. repowire peer doctor <peer> is the explicit, operator-triggered counterpart: it runs the same reconciliation, then reports identity, inbound reachability, pending asks, and contradictions on demand. repowire peer rehook <peer> is the non-destructive recovery (re-establish the inbound ws-hook without killing the pane). A delivery trace ledger (repowire trace <id>) records per-message ask/notify stages for post-hoc "where did it go" inspection, persisted in a dedicated SQLite table rather than the bounded dashboard event buffer.

v0.14 session-native direction

The current stable surface is peer-oriented, but the v0.14 architecture train is moving toward a session-first mesh:

  • Sessions become the durable unit of work.
  • Peers remain runtime executors.
  • Ask/notify delivery now goes through a delivery service plus transport router; WebSocket hooks, experimental ACP, relay, and future transports continue moving toward transport-neutral routing.
  • The dashboard currently shows a selected peer/session timeline, merging Claude transcript history where available with realtime events.
  • The first session-targeted control routes resolve repowire_session_id bindings to an active executor or explicit resume capability status.
  • Broader composer actions, scheduling, approval handling, and backend/model controls move toward the same shared session command surface.

This is a roadmap. Current routes and tools still expose peers, circles, asks, notifications, and schedules. The ask/notify delivery-service and transport-router extraction has landed, but ACP remains experimental and not every route/control path is transport-neutral yet.

Knowledge graph

graphify-out/GRAPH_REPORT.md summarizes the codebase graph. The current report identifies the main hubs as AgentType, Config, PeerRegistry, MessageRouter, and WebSocketTransport, with communities around daemon routing, CLI/setup, channel installer, Telegram, attachments, hook normalization, relay auth, and peer lifecycle.

Keep generated graph JSON and cache files out of prose docs. Link or summarize the report when useful; do not paste large graph artifacts into README or hand-written docs.