The Backend Is More Than a URL

For OpenHands Agent Canvas, switching a backend is not the same as changing a server address in a settings form. This chapter follows the backend’s identity from registration through selection, health checks, edits, removal, and fallback, then shows why authentication, workspace paths, cached data, and conversation routes must change with it. The result is a practical way to predict what should move with the user’s intent, what must be rebuilt, and what must remain isolated.

A backend entry defines a namespace

A URL tells Canvas where to send a request. It does not tell Canvas what kind of authority answers at that address, which authentication scheme it expects, which organization is in scope, or whether a conversation identifier has meaning there. The backend registry therefore stores a richer identity: an ID and display name, a host, an API key, a local or Cloud kind, an authentication mode, and a connection revision that changes when connection credentials change.

That identity is easy to overlook because the user sees a dropdown labeled with a friendly name such as “Local” or a team server. Behind the label is a namespace. Settings belong to a backend. A repository listing belongs to a backend. A conversation ID belongs to a backend. A Cloud organization may further refine that namespace. If Canvas uses only the host as an informal hint, two distinct entries can be confused, and if it uses only the conversation ID, a valid-looking string can be sent to an authority that has never heard of it.

The registry persists the set of configured backends and the current selection in browser storage. It can seed a local entry when the launcher supplies a usable host and key, or create a locked Cloud entry when a deployment is intentionally limited to one Cloud host. The seeded entry is not a magical permanent connection. It becomes a normal registered entry that a user can rename, edit, or remove. That detail matters because runtime configuration and user configuration can both affect the same visible backend.

The registry also distinguishes the active backend from a backend that merely happens to be registered. Most of Canvas’s ordinary Agent Server services are eligible only when the active entry is local. A Cloud entry cannot silently borrow an unrelated local entry just because the browser has one available. That rule prevents a subtle but serious mismatch: a Cloud conversation screen might otherwise read settings or skills from the laptop while presenting them as if they belonged to the Cloud runtime.

The authentication difference makes the namespace concrete. A local Agent Server uses the local session-key convention, sent in an X-Session-API-Key header. A Cloud API normally uses a bearer token. A runtime sandbox associated with a Cloud conversation can use a session key through a different host override. Canvas’s request helpers choose among these paths from the active backend rather than asking each screen to guess. The host and the credential are therefore linked parts of the connection identity.

This richer identity also handles the difference between deployment defaults and user-managed connections. A packaged local launcher can inject a host and session key on first load, creating a usable default without asking the user to type connection details. A deployment locked to Cloud must do the opposite: it must not seed a local entry that would bypass the intended Cloud login path. In both cases, the registry converts startup configuration into an explicit entry that later participates in ordinary selection and recovery. The startup path is therefore a source of initial state, not a privileged bypass around the state model.

The connection revision adds another useful distinction. Changing a display name should not make a healthy backend look new, but changing a host or key should invalidate the old reachability result. If the registry kept only one undifferentiated “last checked” flag, a user could repair a connection while the interface continued to trust the health result for the previous credentials. Tying revalidation to the fields that drive communication makes the lifecycle responsive to the change that actually occurred.

This is the first useful rule for reading the product: a backend is an execution and data scope, not a location string. Once that rule is accepted, the rest of the registry lifecycle becomes easier to understand.

The lifecycle protects against stale authority

A backend can pass through several meaningful states. It may not exist yet, be registered but unselected, be active and healthy, become unavailable, be revalidated after a credential or host edit, or disappear and trigger a fallback. Each transition changes what Canvas is allowed to assume. The lifecycle is not merely a record of user interface events; it is the mechanism that keeps a request attached to the authority that can interpret it.

stateDiagram-v2 [*] --> NoBackend state "No backend" as NoBackend state "Registered local or Cloud backend" as Registered state "Active backend" as Active state "Revalidating connection" as Revalidating state "Unavailable backend" as Unavailable NoBackend --> Registered: add connection Registered --> Active: select backend Active --> Registered: switch selection Active --> Revalidating: host or key edit Revalidating --> Active: health succeeds Revalidating --> Unavailable: health fails Unavailable --> Revalidating: repair connection Active --> Registered: remove active entry Registered --> Active: fallback selects healthy local Unavailable --> Active: fallback selects another backend

On a fresh start, the registry can choose an active entry from stored state or from the launcher’s default. If no valid selection exists, it prefers a healthy local backend when one is known to be available, then another local backend, then a deterministic remaining entry. If there is no usable backend, Canvas uses an explicit “no backend” condition rather than pretending that an empty host is a working connection.

Selection is also scoped to the browser context. The active choice is kept in tab-scoped storage when possible, with a last-used value available for a newly opened tab or older persisted state. A link opened in a new tab can carry the backend identity that owns its conversation, so the new tab does not accidentally adopt a different last-used backend. This is a small but important correction to the assumption that “the current backend” is a single global browser fact. Two tabs can be looking at different authorities, and a conversation link needs to preserve that context.

Adding a backend has an intentionally immediate effect: the new entry becomes active. That makes sense from a user’s perspective—after testing and saving a connection, the next conversation should use it—but it creates a transition hazard for any page that was showing data from the previous backend. Canvas addresses that hazard by redirecting backend-scoped detail pages back to a list where necessary and by changing the identity used for data queries. The user’s place in the product can remain familiar while the data behind it is reloaded under the new authority.

Removing the active backend follows the same logic in reverse. The registry clears an invalid selection and falls back to a remaining local backend, preferring one whose persisted health state does not say it is disabled. The user is not left with an active selection pointing at a deleted entry, but neither does the system promise that every fallback is healthy at the moment of use. A stored health result is a recovery aid, not a guarantee. A subsequent request still has to succeed.

Editing a host or API key is a different transition from changing a display name. A cosmetic rename does not alter the connection being probed. Changing the host or key increments the connection revision and re-arms health checking, because the old reachability result no longer describes the new connection. This is a small example of state ownership done correctly: the part of the record that drives communication determines whether the system must revalidate.

The most important protection appears in query identity. Long-lived queries for settings, conversations, repositories, installations, and related resources include the active backend, and where needed the Cloud organization, in their keys. When the key changes, the data layer treats the result as belonging to a new namespace and fetches again. Canvas does not need a global “clear everything” operation for every component; the identity change gives each query a reason to resolve its own data under the new connection.

This design prevents a dangerous kind of stale success. Imagine a user viewing a conversation from a local server and selecting a Cloud backend. The conversation ID remains in the browser URL for a moment, but it is now foreign to the selected backend. If the page immediately fetched it without a guard, the new server might return a not-found or permission error. Worse, a cache that ignored backend identity could display the old conversation as if it were Cloud data. Canvas instead detects the backend transition, removes or redirects the conversation subtree, clears backend-specific runtime state, and allows the new selection to establish its own queries.

The route behavior is intentionally more cautious than a seamless animation. During a switch, the page can unmount before child queries re-fire. That prevents history, metrics, runtime information, or sub-conversation lookups from using an ID that belongs to the old backend. A redirect back to the conversations list is not a loss of continuity in the meaningful sense. It is the interface acknowledging that continuity of user intent does not imply continuity of backend identity.

There are two clocks during this transition. The selection store can change immediately, while network queries and React components resolve a moment later. If a child component kept rendering during that interval, it could combine the old URL with the new client options. Canvas’s explicit backend-change guard closes that timing window by removing the conversation subtree before its dependent requests run. Then the new backend gets a clean opportunity to establish its own conversation, events, metrics, and workspace state. Correctness here depends not only on what data is stored, but on when a component is allowed to ask for it.

Workspace paths create a similar boundary. A path selected on a laptop may be absolute and meaningful there, but meaningless or unwritable on a remote host. Canvas’s conversation logic resolves working directories with the active backend in mind and distinguishes an explicitly selected workspace from a default path that should be anchored by the backend. Reusing a host-specific absolute path after a backend switch can turn a valid conversation request into a permission failure before the agent receives its first message.

This is why backend switching is better understood as a controlled change of authority than as navigation. The browser remains the same, and the user’s task may remain the same, but the host, authentication mode, workspace, caches, and valid conversation set can all change. The registry gives those changes a common identity so the rest of Canvas can respond consistently.

The local and Cloud paths then diverge in a way that the registry must preserve. Local Agent Server operations use typed client options built from the active local entry. Cloud operations use Cloud-specific service modules and a proxy, because the browser may not be able to call a Cloud application or a per-conversation runtime host directly. The proxy can carry bearer authentication to the Cloud API or a session key to the runtime sandbox. The same conversation view may therefore be backed by different upstream hosts and credential modes at different points in its lifecycle.

That divergence does not make the abstraction meaningless. It defines what the abstraction is for. Canvas offers a common selection and state model while keeping transport differences explicit beneath it. A user can ask for the same kind of work, but the backend-specific adapter determines how that work is created, authenticated, resumed, and observed.

It also changes how an operator should interpret a successful connection test. A local entry can be reachable while its key lacks permission for a particular operation. A Cloud entry can authenticate to the application API while the runtime sandbox is paused or temporarily unavailable. A backend can be healthy for settings but unable to provide a newer feature such as a particular extension endpoint. The registry gives Canvas a place to represent connection identity and health, but it does not turn one probe into a guarantee that every downstream capability will work. Capability and reachability are related, not identical.

There is also a limit worth keeping visible: registered connection data is persisted in the browser, including the information needed to connect to a backend. The registry prevents accidental cross-backend use; it is not a zero-trust vault. The security of the browser environment, the host, the network, and the backend remains part of the deployment decision. State isolation is necessary for correctness, but it is not by itself a complete security boundary.

The practical conclusion is straightforward. When a backend changes, preserve the user’s intent but rebuild the authority-dependent state: re-resolve the connection, authenticate through the right scheme, scope queries and workspace data, and refuse to treat an old conversation identifier as universal. The next question is what this selected backend actually does with a valid request—how it turns the user’s intent into a local or Cloud conversation, a workspace, an agent, and a live event stream.

References