ccr-cloud: remote control and optional conversational dispatcher

Design document. The web app and the Discord adapter described here are available. The conversational LLM dispatcher is proposed and not implemented. For using ccr-cloud today, see the documentation home.

#Product scope

An optional managed-connection service is planned for users who want stable HTTPS access to their existing hosts without configuring networking. It requires no AI gateway or coding plan and is not implemented yet. See the managed-connection design and roadmap. The self-hosted core stays independent.

Control persistent Claude Code tasks on your own server from an installable web app or an optional chat integration. A dedicated EC2/Unix SSH server with working Claude and Git authentication is the primary onboarding case. Docker + CCR is an additional execution mode, not a requirement for host execution.

The eventual product includes an LLM dispatcher: one conversational entry point that can find sessions, discuss projects, start requested work, forward follow-ups and summarize progress across multiple Claude sessions. This is an explicit roadmap commitment. It must remain optional; core remote control never requires another model base URL, API key, or model call.

#Layers

PWA                     Discord (first optional chat adapter)
  |                                  |
  +------------ dispatcher ----------+
                |             |
          explicit actions    optional conversational LLM (future)
                |             | typed session-control tools
                +-------------+
                       |
        deterministic control plane + persisted session/event store
                       |
              multiple Claude runners
                  host / Docker

These layers initially live in one service on the execution server. A separate hosted relay or dispatcher service is not necessary. Discord uses an outbound Gateway connection, so enabling chat does not require exposing the server API. Today the browser still needs a VPN, HTTPS proxy, or SSH tunnel. Managed connection is a separate, optional future access path, not a hosted dispatcher.

#Independent options and progressive onboarding

  1. Required core: server setup, existing Claude credentials, PWA, explicit session selection. No dispatcher model configuration.
  2. Optional chat: bot token and allowed Discord user ID. The adapter translates explicit commands to the same operations as the PWA. Messages addressed to a session go straight to Claude; that worker understands natural language.
  3. Optional LLM dispatcher (planned): natural-language session selection and cross-session discussion. Offer a configured model endpoint/key; investigate a dedicated Claude Code dispatcher using existing server authentication to avoid extra credentials. That backend is not yet implemented or promised to have identical capabilities, cost, or reliability.

The deterministic layer owns authorization, permission decisions, IDs, resource limits, lifecycle and persistence. An LLM may propose typed operations but cannot bypass that layer or approve a worker's permission prompt on the user's behalf. Ordinary buttons and explicit chat commands never require inference.

#First chat implementation

Discord is opt-in and single-owner. A configured Discord user ID is the identity binding; possession of a bot token or membership in its guild does not authorize control. Every command and approval checks the invoking user. No message-content privileged intent is required. Slash commands specify the task/session explicitly; ambient channel messages do not run commands.

  • /ccr new prompt [repo]: create a session.
  • /ccr sessions: list recent sessions, state and IDs.
  • /ccr status session: show progress and unresolved permission requests.
  • /ccr say session text: send a natural-language follow-up directly to Claude.
  • /ccr stop|resume|interrupt session: explicit lifecycle actions.
  • /ccr permission session request decision: answer an exact pending request.
  • Optional owner DM notifications for completed turns, failures and pending approvals, with buttons for the exact request. No raw token appears in links.

Guild command responses are ephemeral. Notifications go only to the configured owner's DM, never a shared channel. Notifications may include code, commands and model output; enabling Discord intentionally sends that data to Discord. Suppress mentions in all generated content. Stale or already-queued approvals are rejected regardless of which surface submitted them. Discord command retries are deduped within the adapter's process; crash-safe exactly-once execution is not claimed.

The first adapter is command based. Thread/reply bindings, interactive pairing, voice input and other chat platforms are follow-up work. Do not silently select an arbitrary session when a request is ambiguous.

#PWA control

The web app provides task creation, a session list, replayable incremental activity, follow-ups, exact tool approvals, stop/resume/interrupt, deep links to a session and visible network state. User messages and queued decisions are visible across surfaces. The owner token remains in memory unless the user explicitly chooses to remember it for this browser tab. Disconnect clears that choice.

A manifest, icons and a service worker provide an installable shell on supported browsers. Installation requires HTTPS or localhost. Cache only public static shell assets; never cache API responses, credentials or transcripts. Offline mode is read-only and must not imply commands were delivered. Web Push is available from the host. Background task execution in the browser and offline command queues are not supported. Server runners continue when the app closes.

#Future conversational dispatcher

Use a provider-independent interface that receives owner conversation history and tools: list/get/create session, send message, interrupt/stop/resume, read events. Keep dispatcher conversation separate from worker conversation. Persist any conversation-to-session binding. Name the target task in action acknowledgements; ask for clarification when several sessions fit. Summaries must distinguish worker-reported results from independently verified results.

Start with user-directed delegation and explicit limits. Autonomous decomposition, worker spawning, dependency scheduling, merging changes and multi-host scheduling are a later phase. Before enabling these, define concurrency/budget policies, workspace conflict rules, cancellation propagation and durable action execution.

#Validation and release boundaries

Tests must cover authorization before side effects, cross-surface control, stale approval rejection, persisted event replay, bot delivery failures and browser reconnect behavior. Model-free fixtures exercise the session protocol. A real Discord account/token and a real Claude-authenticated server are required for release smoke testing; local mocks are not evidence those integrations worked.

Current runner replay buffers are in memory, host tmux sessions do not survive reboot, and host mode is not an isolation boundary. See onboarding.md for the execution and deployment limits. Rich diff review and PR creation remain on the core roadmap alongside the eventual LLM dispatcher.

#Saved projects and repository onboarding

The PWA distinguishes repository tasks from scratch tasks. Saved projects contain an ID, display name, credential-free SSH/HTTPS clone URL, and the last access-check result/time. Tasks resolve a project ID to its URL server-side. Re-adding the same URL updates its saved project. Initial endpoints are authenticated: GET /api/setup, GET /api/projects, POST /api/projects/check, and POST /api/projects. Saving runs its own check rather than trusting client results.

A check runs in the execution driver's environment: the host account for host mode, or a disposable instance of the task image for Docker. Checks are bounded and noninteractive. Read access, author identity, GitHub API authentication and unverified push access are separate capabilities. No clone, push or key upload is performed. Authentication secrets are not accepted in project URLs. GitHub API checking currently targets github.com; custom hosts and SSH aliases remain explicitly unverified for API access.

Claude model access is not inferred from API authentication or Git access. The setup panel tells the user to run a scratch task to verify model access without silently spending model usage during onboarding. GitHub App connection, project browsing/deletion and automatic publishing remain future work.

#Local projects

Host mode exposes authenticated GET /api/local-repositories and POST /api/projects/local. Default discovery roots are ~/src, configurable via CCR_CLOUD_REPO_ROOTS. Discovery is bounded, skips symlink directories, redacts remote credentials and recognizes .git directories/files. Saved local project paths are canonically revalidated at task creation. No arbitrary browser-supplied path is passed straight to a runner.

HostDriver owns local worktree creation/resumption and clean removal. A task uses a new ccr/session branch at the selected repository's committed HEAD by default. The original checkout is never used as the task workspace. Uncommitted changes and ignored files are not copied. Dirty worktree deletion is rejected. Remote projects retain the existing clone workflow; Docker does not mount host repos.

Health reports API version 2 so doctor can detect a stale backend after updates. Repository-check diagnostic IDs connect PWA errors to sanitized server logs.

Edit this page on GitHub ↗