Learn the cloud-specific Canvas surface as one coherent system
Agent Canvas is not a second Cloud product hidden inside the repo. It is one UI that switches lanes: local mode works with folders and a nearby agent-server; cloud mode works with OpenHands Cloud control-plane records, Git integrations, org-scoped settings, and short-lived sandbox runtimes.
1Background: one Canvas, two backend kinds
Agent Canvas is the browser shell for conversations with software agents. In local development, the browser talks to a local agent-server and asks it about local workspaces, local settings, files, Git state, and WebSocket streams.
Cloud mode adds another layer. The browser first talks to the durable OpenHands Cloud app backend: organizations, app-conversations, API keys, Git providers, profiles, secrets, suggested tasks, automations, and sandbox metadata live there. A running conversation also has a sandbox runtime: a temporary agent-server inside the workspace where terminal/file/browser events happen.
Stored as kind: "local". Auth uses X-Session-API-Key. New conversations can start from a local folder/workspace. Settings are SDK agent-server settings.
Stored as kind: "cloud". Auth uses Authorization: Bearer .... New conversations can start from hosted Git repositories. Settings/profiles/secrets route to Cloud APIs.
Narrow background: the switch is explicit
The active backend kind is real UI state. Auth headers branch in backend-registry/auth.ts:1-14. The backend selector flattens local rows and cloud org rows in backend-selector.tsx:61-112. Most service wrappers ask one question: is the active backend local or cloud?
if (active.backend.kind === "cloud") {
// call a Cloud app endpoint, or a sandbox runtime through the proxy
} else {
// call the local agent-server / local automation sidecar
}2Intuition: cloud mode swaps “where is my computer?” for “where is my control plane?”
A good mental model is to imagine the same user action in two modes.
User selects /Users/me/repo. Canvas creates a local agent-server conversation with a working directory. File, terminal, and Git queries go to that local server.
User selects OpenHands/agent-canvas from connected Git providers. Canvas asks Cloud to start an app-conversation, waits for a sandbox, then streams from the runtime.
The Cloud lane has two stops. First, durable app state on the Cloud backend. Second, a runtime sandbox only after provisioning succeeds.
3Code walkthrough: five cloud seams to recognize
Seam A — connecting to Cloud is a backend-registry feature
The Add Backend UI offers manual local/self-hosted connection plus a Cloud login column. The Cloud login starts OAuth device flow and stores a backend as kind: "cloud" with the returned API key backend-form-modal.tsx:687-1000. The device flow itself calls /oauth/device/authorize and /oauth/device/token device-flow-client.ts:104-185.
Once connected, Cloud rows become org-aware. The selector fetches organizations and prefers the personal workspace when it can identify one backend-selector.tsx:206-232.
Seam B — creating a Cloud conversation is asynchronous
Local conversation creation returns a ready conversation. Cloud creation posts an app-conversation start request and may return a task before a runtime exists cloud/conversation-service.api.ts:104-130. The UI uses a temporary /conversations/task-... URL until useTaskPolling() sees READY and redirects to the real conversation id use-task-polling.ts:19-33, use-task-polling.ts:92-125.
Cloud create:
POST /api/v1/app-conversations
-> task id first
-> route /conversations/task-123
-> poll /api/v1/app-conversations/start-tasks?ids=123
-> READY: navigate /conversations/{app_conversation_id}Seam C — sandbox status shapes the conversation UI
Cloud sandboxes can pause, resume, disappear, or fail. Stopping a Cloud conversation pauses its sandbox; local stop interrupts the agent-server immediately conversation-mutation-utils.ts:36-61. Opening a paused Cloud conversation resumes the sandbox conversation.tsx:142-177. While paused, WebSocket connection is deliberately suppressed so the browser does not hit a stale runtime URL websocket-provider-wrapper.tsx:24-33.
Seam D — Cloud replaces workspace picking with repository picking
The New Conversation button chooses a different menu by backend kind: local shows a workspace picker; cloud shows a repository picker new-conversation-button.tsx:28-39, new-conversation-button.tsx:87-99. Repository search, installations, and branches all route through Cloud Git APIs cloud/git-service.api.ts:20-104. GitHub is installation-scoped only in Cloud mode utils.ts:176-204.
Seam E — settings are shared UI, different backend contracts
The same Settings UI is reused, but Cloud settings are mostly flat Cloud app fields. The adapter derives the nested shape the UI expects cloud/settings-service.api.ts:12-56. Saving settings also branches: app preferences are flattened for Cloud, while local stores them under agent-server misc settings settings-service.api.ts:398-437.
LLM profiles are org-scoped on Cloud, with mutation permission based on Cloud org role/permissions profiles-service.api.ts:12-43, use-can-manage-llm-profiles.ts:12-53.
4Feature map: what is cloud-specific today?
Identity and control plane
Cloud loginBearer authorg selectionlocked-to-cloud mode
Cloud backend connection, OAuth device flow, health checks, API-key org binding, and Cloud settings link.
Conversation lifecycle
start taskssandbox pause/resumeruntime URLarchived status
Async app-conversation creation, task polling, pending first messages, deferred attachments, paused WebSocket suppression, sandbox-exposed VS Code URLs.
Repository and Git integrations
repo pickerinstallationsbranchessuggested tasks
Cloud starts work from connected Git repositories, not local folders. Suggested tasks are Cloud-only and route to hosted Git provider issues/PRs.
Settings and user resources
flat Cloud settingsorg profilesCloud secretsCloud skills
Settings pages look shared, but their data comes from Cloud app endpoints when the active backend is cloud.
Cloud-only or Cloud-gated UI
Planner tabpublic sharingCloud VS Code URLCloud automations routing
The Planner tab is hidden locally. Public sharing only appears for Cloud. VS Code URL comes from sandbox exposed URLs.
Explicit non-goals in Canvas
billingaccount managementfull integrations admin
Those are not embedded as Canvas screens. The UI mostly links out to hosted Cloud settings/integrations when needed, for example cloud-settings-link.tsx:14-50.
Four important examples
| Feature | Cloud-specific behavior | Source |
|---|---|---|
| Public sharing | Only shown on cloud; updates the app-conversation public flag and builds URLs under the Cloud host. | context-menu.tsx:147-153, use-conversation-name-context-menu.ts:155-165 |
| Planner tab | Hidden unless backend.kind === "cloud"; plan conversations are sub-conversations with agentType: "plan". | conversation-tabs.tsx:137-145, use-handle-plan-click.ts:61-82 |
| Automations | The automation API can route through Cloud, but recommended automation creation is hidden on cloud and detail editing is local-only in MVP. | automation-service.api.ts:56-68, recommended-automations-launcher.tsx:220-222 |
| MCP marketplace | Cloud skips pre-conversation connectivity tests because MCP runs inside a future sandbox, not in the browser-reachable local agent-server. | mcp-service.api.ts:35-50, custom-server-editor.tsx:56-62 |
5Quiz: prove the model stuck
Choose an answer. Feedback explains the reasoning, not just the result.
1. Why does Cloud conversation creation use a task-... URL?
2. Which auth header does a Cloud backend use for app API calls?
3. Why does the New Conversation UI show repositories on Cloud but workspaces locally?
4. What does the UI do before connecting WebSocket to a paused Cloud sandbox?
5. Which feature is not embedded as a full Canvas cloud screen?
Score: 0 / 5 answered