Full activity streaming — thinking, tool calls, and responses in real-time. Self-hosted, open-source.
Quick Start • Why Remo Code • Architecture • Deploy
OpenClaw popularized the idea of talking to an AI agent from your phone. But it requires you to trust a third-party runtime with shell access to your machine, and security researchers have already found real data exfiltration in community-contributed OpenClaw skills.
Remo Code gives you the same "chat with your agent from anywhere" workflow, but with full activity streaming and complete control:
| OpenClaw | Claude Code Remote Control | Remo Code | |
|---|---|---|---|
| Self-hosted | Partial (local agent, cloud relay) | No (Anthropic relay) | Yes, fully |
| Open source | Yes | No | Yes (MIT) |
| Activity streaming | No | No | Yes (thinking, tool calls, text) |
| Web UI | No (messaging apps only) | Yes (claude.ai) | Yes (your own domain) |
| Multi-session | No | No | Yes |
| File attachments | No | No | Yes (images + text files) |
| Auth & data storage | Third-party servers | Anthropic servers | Your Supabase instance |
- Developers who want to check on long-running Claude Code tasks from their phone
- Teams who want a shared dashboard for multiple Claude Code sessions
- Security-conscious users who don't want third-party tools with shell access to their machines
- Self-hosters who want full control over their data and infrastructure
- Bun runtime
- Claude Code CLI installed
- A Supabase project (free tier works)
git clone https://github.com/finedesignz/remo-code.git
cd remo-code
bun installCreate a Supabase project and run the migrations in supabase/migrations/ via the SQL Editor.
cp hub/.env.example hub/.env
# Edit: SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, HUB_ALLOWED_ORIGINS
cp web/.env.example web/.env
# Edit: VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY, VITE_HUB_URL# Terminal 1 — Hub server (port 3040)
bun run dev:hub
# Terminal 2 — Web dev server (port 5173)
bun run dev:webOpen http://localhost:5173 and create your account on the setup form.
Generate an API key in Settings, then run the agent in your project directory:
npx remo-code-agent --api-key YOUR_API_KEY --local-outputThat's it. The agent spawns a Claude Code process, and everything streams to your browser in real-time — thinking, tool calls, file edits, and responses. You get both terminal output and web UI.
Each agent = one Claude Code session. To connect multiple projects, run the agent in each project directory. Each gets its own session in the web UI. Sessions auto-resume by project directory.
Add an alias so you can run claude-remote instead of the full command:
Windows (PowerShell)
Add to your PowerShell profile ($PROFILE):
# Open profile in editor (creates it if it doesn't exist)
if (!(Test-Path $PROFILE)) { New-Item -Path $PROFILE -Force }
notepad $PROFILEAdd this line:
function claude-remote { npx remo-code-agent --api-key YOUR_API_KEY --local-output }Reload: . $PROFILE or open a new terminal.
macOS / Linux (bash)
Add to ~/.bashrc (or ~/.bash_profile on macOS):
alias claude-remote='npx remo-code-agent --api-key YOUR_API_KEY --local-output'Reload: source ~/.bashrc
macOS / Linux (zsh)
Add to ~/.zshrc:
alias claude-remote='npx remo-code-agent --api-key YOUR_API_KEY --local-output'Reload: source ~/.zshrc
fish
Add to ~/.config/fish/config.fish:
alias claude-remote 'npx remo-code-agent --api-key YOUR_API_KEY --local-output'Reload: source ~/.config/fish/config.fish
Then just run claude-remote in any project directory — same as running claude but with remote streaming to the web UI.
If you don't want to self-host, use the hosted hub at app.remo-code.com:
npx remo-code-agent --api-key YOUR_API_KEY --local-outputThe default hub URL is https://app.remo-code.com — no --hub-url needed.
Browser (React SPA)
↕ WebSocket + REST API
Hub Server (Bun + Hono)
↕ WebSocket
Local Agent (one per project)
↕ subprocess stdin/stdout (stream-json)
Claude Code CLI
Four packages in a Bun workspace:
- hub/ — Bun + Hono server handling auth (Supabase JWT), message relay, and session management. Broadcasts Claude's activity events (thinking, tool use, text) to subscribed browsers.
- web/ — React 19 + Vite + Tailwind CSS 4 chat UI with activity feed, session switching, file attachments, light/dark theme, and unread badges.
- agent/ — Local streaming agent that runs on your dev machine. Spawns a persistent Claude Code CLI process with
--input-format stream-json --output-format stream-json, parses events, and relays to the hub. Published asremo-code-agenton npm. - channel/ — (Legacy) Claude Code channel plugin. Kept for backward compatibility.
- You run
npx remo-code-agent --api-key xxxin your project directory - The agent connects to the hub via WebSocket and registers a session
- The agent spawns
claude --input-format stream-json --output-format stream-json --verbose - When you send a message in the web UI, the hub forwards it to the agent
- The agent writes the message to Claude's stdin as JSON
- Claude responds — thinking, tool calls, text stream out via stdout
- The agent parses the stream-json events and relays them to the hub
- The hub broadcasts to all subscribed browsers in real-time
Session resume: The agent reuses existing sessions by matching the project directory. Restart the agent and it reconnects to the same session with full message history.
Conversation memory: The agent keeps a single persistent Claude process — full conversation context is maintained across messages, just like the terminal.
- Activity streaming — see Claude's thinking, tool calls, and text responses in real-time
- File attachments — paste images (Ctrl+V) or attach text files in the chat
- Multi-session — run agents in multiple project directories, switch between them
- Session resume — restart the agent and reconnect to the same session
- Scheduled tasks — fire prompts, skills, or supervisor commands on a cron cadence against one session, one supervisor, or all of either. Dropdown cron builder (every N min/hour/day/month, daily, weekly multi-select, monthly with "Last day", custom) with plain-English summary + next-3-runs preview. Schedules list has name search + enabled/disabled + task-type filters and last-run cost/duration chips per row. Runs drawer has status filter chips + summary stats (success rate, total cost, avg duration). Every scheduled run ends with a one-line
Summary:from Claude, and the chat shows the trigger as aScheduled: <task name>pill above the prompt. Per-target run history, daily cost cap, offline-grace replay, and post-run actions (chain, email, telegram, web push, webhook). See docs/scheduled-tasks.md. - Error capture — Sentry-style intake at
/api/sentry/:project_id/envelope/that fingerprints + dedupes + rate-limits + caps runtime errors from your deployed apps, then routes them as a structureduser_messageinto the Claude session bound to that repo so Claude can investigate, fix, commit, and push in-session. Includes one-click Sentry SDK auto-install for Node+Express / Node+Next.js / Python+FastAPI / Python+Django (supervisor git-ops + Coolify env PATCH). See docs/error-capture.md. - Grid View — watch up to 12 Claude Code sessions side-by-side at
#/grid. User-named tabs persist per account (chat_tabs+chat_tab_sessions), each with a layout mode (3x3,4x3, orauto-fit). One WebSocket subscribes to many sessions in one frame, message lists are virtualized, and streaming text is RAF-coalesced. On phones the grid auto-swaps to a single-pane accordion (only one chat mounted at a time). See docs/grid-view.md. - Coolify deployment self-heal (Phase 06, partial) — a public HMAC-signed webhook endpoint (
POST /api/coolify/webhook/:user_id) turnsdeployment.failedevents into structuredtriageruns that ask Claude to emit a typedTriageResult(error_type, severity, root_cause, suggested_fix, confidence). Agithub_issuepost-run action then files a labelled issue on the failing repo via the gateway-pair credentials (noGITHUB_TOKENon the hub) with 24-hour idempotency. Webhook ingress, secret rotation, triage schema, and the GitHub-issue action are shipped; the final session-routing wire-up (pickSessionTarget) is pending the Phase 04 self-heal-routing plan landing. See docs/coolify-webhook-migration.md and the "Coolify webhook ingress" / "GitHub-issue post-run action" sections in docs/scheduled-tasks.md. - Codex CLI + rootless ambient sessions — sessions can run either Claude Code or Codex (
cli_kindcolumn). Each agent can also host "ambient" rootless sessions (one Claude + one Codex per host) with no project directory required, lazy-spawned on first message. Global instruction files (~/.claude/CLAUDE.md,~/.codex/AGENTS.md,~/.codex/config.toml) sync from the hub on connect viacreate_if_absent— the agent never overwrites local files. Edit blobs in Settings → Instructions. See docs/codex-and-rootless.md. Requiresnpm i -g @openai/codex+codex login(orOPENAI_API_KEY). - Unread badges — know when sessions have new messages
- Light/dark theme — toggle in the header
- Mobile-first — responsive design with safe-area support for notched devices
Build and deploy with Docker:
docker build -t remo-code .
docker run -p 3040:3040 \
-e SUPABASE_URL=... \
-e SUPABASE_ANON_KEY=... \
-e SUPABASE_SERVICE_ROLE_KEY=... \
-e HUB_ALLOWED_ORIGINS=https://your-domain.com \
remo-codeThe Docker image builds the web frontend and serves it from the hub — one container, one port. The agent runs on your dev machine, not on the server.
├── hub/ # Bun + Hono server (HTTP, WebSocket, auth)
│ └── src/
│ ├── api/ # REST endpoints (sessions, messages, profile, setup)
│ ├── auth/ # JWT + API key verification middleware
│ ├── db/ # Supabase clients and data access layer
│ ├── middleware/ # Rate limiting
│ ├── utils/ # Shared utilities (token generation)
│ └── ws/ # WebSocket handlers (agent, channel, client) + Zod schemas
├── web/ # React 19 + Vite + Tailwind CSS 4 SPA
│ └── src/
│ ├── components/ # Layout, ChatPanel, ActivityFeed, Sidebar, etc.
│ └── hooks/ # useAuth, useWebSocket, useSessions, useChat, useActivity
├── agent/ # Local streaming agent (npm: remo-code-agent)
│ └── src/
│ ├── index.ts # Entry point — wires hub client, Claude runner
│ ├── claude-runner.ts # Persistent Claude CLI process management
│ ├── hub-client.ts # WebSocket client to hub
│ └── config.ts # Config loading (CLI args, env vars, config file)
├── channel/ # (Legacy) Claude Code channel plugin
├── supervisor/ # Local supervisor (Bun) + Tauri 2 desktop tray app (Phase 06)
│ └── tauri/ # Windows tray shell (Rust + WebView2) — wraps Bun supervisor as sidecar
├── supabase/ # Database migrations
└── Dockerfile # Multi-stage production build
- Supabase JWT auth on all API and WebSocket endpoints
- Row-Level Security on all database tables — multi-tenant by default
- API keys stored as SHA-256 hashes with timing-safe comparison
- CSP, HSTS, and security headers on all responses
- Rate limiting on API routes, setup endpoints, and WebSocket messages
- Per-IP connection limits on WebSocket endpoints
- Path traversal protection on static file serving
- Non-root Docker user in production
- Setup endpoint mutex preventing race conditions
Your data stays in your Supabase instance. Your Claude Code sessions stay on your machine. The hub is just a relay — and you own it.
The hub exposes its REST surface as an OpenAPI 3.1 spec at /openapi.json and a Scalar-rendered reference UI at /docs (e.g. http://localhost:3040/docs). The committed snapshots live at docs/openapi.json (machine) and docs/api.md (human). Regenerate after touching the OpenAPI sample route:
bun run docs:syncCI fails PRs that drift the snapshots — see .github/workflows/docs-drift.yml. Only /api/profile/cost-today is in the spec today; other routes will be migrated incrementally.
Apache-2.0
