Skip to content

Architecture Overview

Edit page

System architecture for ReCursor: a Flutter mobile app with OpenCode-like UI. Bridge-first, no-login: connects to your user-controlled desktop bridge via secure tunnel.


flowchart TB
subgraph Mobile["📱 ReCursor Flutter App"]
UI["OpenCode-like UI Layer"]
State["Riverpod State Management"]
WSClient["WebSocket Client"]
end
subgraph Desktop["💻 Development Machine"]
Bridge["ReCursor Bridge Server\n(TypeScript/Node.js)"]
subgraph Integration["Claude Code Integration"]
Hooks["Hooks System\n(HTTP Event Observer)"]
AgentSDK["Agent SDK\n(Parallel Session)"]
CC["Claude Code CLI"]
end
end
subgraph Anthropic["☁️ Anthropic Services"]
API["Claude API"]
end
UI <--> State
State <--> WSClient
WSClient <-->|wss:// (Tailscale/WireGuard)| Bridge
Bridge <-->|HTTP POST| Hooks
Hooks -->|Observes| CC
Bridge <-->|Optional| AgentSDK
AgentSDK <-->|API Calls| API
CC <-->|Internal| API

ApproachStatusNotes
Direct Remote Control Protocol❌ Not AvailableFirst-party only (claude.ai/code, official apps). No public API for third-party clients.
Claude Code Hooks✅ SupportedHTTP-based event observation (one-way)
Agent SDK✅ SupportedParallel agent sessions (not mirroring)
MCP (Model Context Protocol)✅ SupportedTool interoperability

Selected Architecture: Hybrid approach using Hooks for event observation + Agent SDK for parallel sessions. ReCursor does not claim to mirror or control existing Claude Code sessions.

ReCursor adopts OpenCode’s UI patterns for the mobile interface:

  • Tool Cards: Rich, interactive cards for tool use and results
  • Diff Viewer: Syntax-highlighted unified/side-by-side diffs
  • Session Timeline: Visual timeline of agent actions and decisions
  • Chat Interface: Streaming text with markdown rendering
Mobile App <--WebSocket--> Bridge Server <--HTTP--> Claude Code Hooks
↑ |
| ↓
└────────────────────────────────────── Observes Events
  • WebSocket: Bidirectional, real-time communication between mobile and bridge
  • HTTP Hooks: One-way event streaming from Claude Code to bridge
  • No Direct Mobile-to-Claude-Code: All communication flows through the bridge

ComponentResponsibility
UI LayerRender OpenCode-style tool cards, diff viewer, timeline
State ManagementRiverpod providers for sessions, messages, connection
WebSocket ClientConnect to bridge, handle reconnections, heartbeat
Local StorageDrift for persistence, Hive for caching
ComponentResponsibility
WebSocket ServerAccept mobile connections, manage sessions
Event QueueBuffer events from Hooks, replay on reconnect
HTTP EndpointReceive events from Claude Code Hooks
Agent SDK AdapterOptional parallel session management
ComponentResponsibility
HooksPOST events to bridge (tool use, messages, session state)
Agent SDKParallel agent session (if user wants independent agent)
Claude Code CLISource of truth for actual coding session

flowchart LR
subgraph Network["Network Layers"]
WireGuard["WireGuard/Tailscale\n(Network Layer)"]
TLS["TLS 1.3\n(Transport Layer)"]
Auth["Device Pairing Token\n(Application Layer)"]
end
Phone["📱 Mobile"] --> WireGuard
WireGuard --> TLS
TLS --> Auth
Auth --> Bridge["Bridge Server"]
  1. Network Layer: Tailscale/WireGuard mesh VPN (or your preferred secure tunnel)
  2. Transport Layer: WSS (WebSocket Secure) with TLS 1.3
  3. Application Layer: Device pairing token on WebSocket handshake (no user accounts, no login)

After establishing the WebSocket connection, the bridge analyzes the connection to determine the connection mode. This informs the user of the security posture and triggers appropriate warnings:

ModeDetection CriteriaSecurity LevelUser Experience
Local-onlyLoopback address (127.0.0.1, ::1)✅ SecureHouse icon indicator
Private networkRFC1918 private IP (10.x, 172.16-31.x, 192.168.x)✅ SecureWiFi icon indicator
Secure remoteTailscale/WireGuard IP (100.x.x.x) or validated tunnel domain✅ SecureShield icon indicator
Direct public remotePublic IP or domain without tunnel validation⚠️ WarningWarning triangle, requires acknowledgment
Misconfiguredws:// instead of wss://, or other insecure setup❌ BlockedError screen, connection refused

Health Verification: After connection_ack, the client sends a health_check message. The bridge responds with health_status including the detected connection mode. The app displays the mode indicator and, for “direct public remote,” requires explicit user acknowledgment before proceeding to the main shell.


  1. User sends message from mobile app
  2. Message queued locally (if offline)
  3. WebSocket transmits to bridge
  4. Bridge forwards to Agent SDK session (if active)
  5. Agent SDK calls Claude API
  1. Claude Code executes tool/action
  2. Hooks POST event to bridge
  3. Bridge queues event (if mobile disconnected)
  4. WebSocket transmits to mobile
  5. UI renders OpenCode-style component

ConstraintImpactMitigation
Hooks are one-wayCannot inject messages into Claude CodeUse Agent SDK for parallel session
No session mirroringMobile sees events but not full contextHooks include rich event metadata
Requires local Claude CodeCannot work without desktop agentClear user messaging, offline queue


Last updated: 2026-03-17