OpenHands/agent-canvas

Document migration from OpenHands OSS Local GUI to Agent Canvas

Closed

#1,054 opened on Jun 3, 2026

 (2 comments) (0 reactions) (0 assignees)TypeScript (26 forks)github user discovery
documentationgood first issue

Repository metrics

Stars
 (43 stars)
PR merge metrics
 (Avg merge 2d 10h) (106 merged PRs in 30d)

Description

Context

Agent Canvas is intended to replace the OSS Local GUI experience from OpenHands/OpenHands. This issue tracks the verified migration behavior and documentation/work needed for users moving from the old OSS Local GUI to Agent Canvas.

Investigation snapshots:

  • OpenHands/agent-canvas: 10d0a1bf (2026-06-03, "Add frontend-only and backend-only agent-canvas modes")
  • OpenHands/OpenHands: 8fdf41d (2026-06-02, "Avoid Keycloak full scan in Resend sync")
  • OpenHands/software-agent-sdk: e3c9828, package version 1.24.0, matching agent-canvas/config/defaults.json

This issue was created and updated by an AI agent (OpenHands) on behalf of the user.

TL;DR

Agent Canvas does not perform a complete in-place migration from OpenHands OSS Local GUI state. It reuses a narrow subset of the old ~/.openhands/settings.json data, ignores several old top-level settings, stores LLM profiles in a different location/shape, and currently does not reuse old plaintext secret values when Agent Canvas starts with its default generated OH_SECRET_KEY.

The migration guide needs to be explicit about the following:

  1. Back up ~/.openhands before first Agent Canvas launch.
  2. Re-enter or migrate LLM API keys and custom secret values unless a migration helper is added.
  3. Recreate old Local GUI LLM profiles in Agent Canvas, except for the single auto-created profile described below.
  4. Recreate git provider tokens / repository integrations; old provider_tokens are ignored by the local agent-server used by Agent Canvas.
  5. Review app preferences (language, analytics consent, sound notifications, git identity), disabled skills, search/Tavily setup, workspace paths, and conversation history.

Verified persistence behavior

OpenHands OSS Local GUI persistence

OpenHands Local GUI stores durable single-user state through the app-server file store:

  • Persistence directory selection: OH_PERSISTENCE_DIR -> FILE_STORE_PATH -> ~/.openhands
    • OpenHands/openhands/app_server/config.py
  • Settings file: settings.json
    • OpenHands/openhands/app_server/settings/file_settings_store.py
  • Secrets file: secrets.json
    • OpenHands/openhands/app_server/secrets/file_secrets_store.py
  • Docker compose mounts host ~/.openhands into the app container.
    • OpenHands/docker-compose.yml

The old file stores serialize secrets in plaintext by passing context={'expose_secrets': True}.

Agent Canvas / agent-server persistence

Agent Canvas launches openhands-agent-server version 1.24.0 by default:

  • Version source of truth: agent-canvas/config/defaults.json
  • Dev/npm launcher sets OH_PERSISTENCE_DIR to the parent of ~/.openhands/agent-canvas, i.e. ~/.openhands.
    • agent-canvas/scripts/dev-safe.mjs
  • Docker entrypoint sets OH_PERSISTENCE_DIR=${HOME}/.openhands and stores Agent Canvas conversations/bash events under ~/.openhands/agent-canvas/....
    • agent-canvas/docker/entrypoint.sh
  • Agent Canvas always configures an OH_SECRET_KEY in the normal launcher paths: generated/persisted in ~/.openhands/agent-canvas/secret-key.txt unless the user supplies one.
    • agent-canvas/scripts/dev-safe.mjs
    • agent-canvas/docker/entrypoint.sh

This means Agent Canvas points at the old Local GUI settings.json / secrets.json files by default, but the new agent-server model only imports the fields listed below.

Data compatibility matrix

Old Local GUI data Verified Agent Canvas behavior Source evidence
settings.json.agent_settings Reused. The agent-server persisted settings model includes agent_settings; SDK migrations accept older agent settings, add agent_kind, canonicalize legacy agent_kind: "llm" to "openhands", and drop deprecated verification fields moved to conversation settings. software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py; software-agent-sdk/openhands-sdk/openhands/sdk/settings/model.py
LLM API key inside agent_settings.llm.api_key Not reused under default Agent Canvas launch when the old value is plaintext. The new server loads settings with a cipher because OH_SECRET_KEY is configured. validate_secret() attempts Fernet decrypt and returns None on plaintext/non-decryptable values. software-agent-sdk/openhands-sdk/openhands/sdk/utils/pydantic_secrets.py; software-agent-sdk/openhands-sdk/openhands/sdk/utils/cipher.py; agent-canvas/scripts/dev-safe.mjs
settings.json.conversation_settings Reused. The persisted model includes conversation_settings; SDK migration v0 -> v1 is applied. software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py; software-agent-sdk/openhands-sdk/openhands/sdk/settings/model.py
settings.json.llm_profiles Not imported. Agent-server PersistedSettings has schema_version, agent_settings, conversation_settings, and active_profile; it does not have llm_profiles. Pydantic ignores old extra top-level fields. software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py
Legacy single default LLM profile Conditionally auto-created in the new profile store when /api/profiles is listed and no profile files exist, but only if settings.llm_api_key_is_set is true after settings load. With old plaintext API keys plus default Agent Canvas OH_SECRET_KEY, that condition is false. software-agent-sdk/openhands-agent-server/openhands/agent_server/profiles_router.py
New Agent Canvas LLM profiles Stored as separate JSON files under ~/.openhands/profiles/*.json, not embedded in settings.json. Profile names must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. software-agent-sdk/openhands-sdk/openhands/sdk/llm/llm_profile_store.py
secrets.json.custom_secrets names/descriptions Loaded into the new Secrets model if the JSON shape is {custom_secrets: {NAME: {secret, description}}} or {custom_secrets: {NAME: "value"}}. software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py
secrets.json.custom_secrets.*.secret plaintext values Not usable under default Agent Canvas launch for the same reason as LLM API keys: OH_SECRET_KEY is configured, validate_secret() tries to decrypt, and plaintext values become None. Listing still returns names/descriptions; value lookup returns 404 when the loaded secret value is None. software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py; software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/store.py; software-agent-sdk/openhands-agent-server/openhands/agent_server/settings_router.py
secrets.json.provider_tokens Ignored by the local agent-server. The new Secrets model explicitly stores custom secrets only and comments that it does not store provider tokens because the agent-server does not integrate with OAuth providers directly. software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py
settings.json.secrets_store.provider_tokens legacy field Ignored by Agent Canvas / agent-server. Old OpenHands app-server migrated this field into old secrets.json.provider_tokens; the new agent-server does not read secrets_store and does not read provider tokens. OpenHands/openhands/app_server/settings/settings_router.py; software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py
Top-level old settings: language, enable_sound_notifications, user_consents_to_analytics, git_user_name, git_user_email Not reused from old settings.json. Agent Canvas persists these locally in browser localStorage key openhands-agent-server-app-preferences because local /api/settings only persists nested agent/conversation diffs. agent-canvas/src/api/app-preferences-store.ts; agent-canvas/src/api/settings-service/settings-service.api.ts
disabled_skills Not reused from old settings.json. Agent Canvas stores local disabled skills in browser localStorage key openhands-agent-server-disabled-skills. agent-canvas/src/api/settings-service/settings-service.api.ts
search_api_key, sandbox_api_key, max_budget_per_task, remote_runtime_resource_factor, sandbox_grouping_strategy, email fields Not reused by local Agent Canvas because the local agent-server persisted model does not include these top-level old app-server fields and Agent Canvas local settings transform only derives fields from agent_settings and conversation_settings. software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py; agent-canvas/src/api/settings-service/settings-service.api.ts
Old Local GUI conversation history and metadata Not automatically listed/imported by Agent Canvas. Old app-server event files use v1_conversations/{conversation_id} and SQL-backed app conversation metadata; Agent Canvas agent-server uses its own OH_CONVERSATIONS_PATH, defaulting to ~/.openhands/agent-canvas/conversations in launchers. OpenHands/openhands/app_server/event/event_service_base.py; OpenHands/openhands/app_server/conversation_paths.py; agent-canvas/scripts/dev-safe.mjs; agent-canvas/docker/entrypoint.sh
config.toml Not read by Agent Canvas or openhands-agent-server. Repository search finds no runtime TOML config loader in Agent Canvas or agent-server; old OpenHands docs describe config.toml setup mainly for headless/CLI defaults. agent-canvas/; software-agent-sdk/openhands-agent-server/; OpenHands/Development.md

Environment-variable mapping

OpenHands Local GUI env/config Agent Canvas equivalent / status
OH_PERSISTENCE_DIR Still honored by openhands-agent-server. Agent Canvas defaults it to ~/.openhands.
FILE_STORE_PATH Old Local GUI fallback only. Use OH_PERSISTENCE_DIR for Agent Canvas.
SESSION_API_KEY Agent-server still supports this as a legacy fallback, but Agent Canvas launchers use user-facing LOCAL_BACKEND_API_KEY and pass it to the server as OH_SESSION_API_KEYS_0.
VITE_BACKEND_BASE_URL="localhost:3000" Agent Canvas normal entry point is http://localhost:8000; frontend-only/dev setups use VITE_BACKEND_BASE_URL pointed at the active agent-server/ingress URL.
VITE_BACKEND_HOST="127.0.0.1:3000" Agent Canvas Vite proxy defaults to 127.0.0.1:8000.
WORKSPACE_BASE, workspace_base in config.toml Agent Canvas uses VITE_WORKING_DIR, stored local workspaces, per-conversation working dirs, and Docker /projects mounts.
RUNTIME=local/process/remote, SANDBOX_*, AGENT_SERVER_IMAGE_* These are old app-server sandbox orchestration knobs. Agent Canvas talks directly to openhands-agent-server; npm/local mode runs without a sandbox, Docker mode runs the all-in-one container, and custom agent-server versions are selected with OH_AGENT_SERVER_LOCAL_PATH, OH_AGENT_SERVER_GIT_REF, or OH_AGENT_SERVER_VERSION.
OPENHANDS_PROVIDER_BASE_URL, LLM_BASE_URL Agent Canvas primary user path is LLM Profiles in the UI. The direct SDK default model remains gpt-5.5, but Agent Canvas explicitly sends its own frontend default when no model is configured.
TAVILY_API_KEY / SEARCH_API_KEY Old OpenHands app-server had a Tavily MCP proxy path. Agent Canvas exposes MCP through the /mcp page and marketplace entries; Tavily is configured as a regular MCP server entry.
VITE_ENABLE_BROWSER_TOOLS Agent Canvas-specific. Set to false to omit browser_tool_set from new conversation payloads.
VITE_LOAD_PUBLIC_SKILLS Agent Canvas-specific. Defaults to enabled; set to false to stop loading public skills from OpenHands/extensions.
LOCAL_BACKEND_API_KEY Agent Canvas user-facing API key variable. Required in --public mode; auto-generated/persisted in local mode when unset.
OH_SECRET_KEY Critical in Agent Canvas. Generated/persisted by default. Also causes old plaintext secret fields to fail decryption unless a migration/import path handles them.

Launch and behavior changes users see

Old OpenHands Local GUI

  • Source/dev: make run
  • Backend: 127.0.0.1:3000
  • Frontend: 127.0.0.1:3001
  • Docker: make docker-run, compose exposes 3000
  • Default runtime orchestration is the OpenHands app-server managing sandboxed agent-server instances

Agent Canvas

  • npm/global: npm install -g @openhands/agent-canvas then agent-canvas
  • Source/dev: npm run dev
  • Main entry point: http://localhost:8000/
  • Full stack defaults: ingress 8000, agent-server 18000, automation backend 18001, Vite 3001 in dev
  • Partial modes: agent-canvas --frontend-only, agent-canvas --backend-only
  • Docker all-in-one image exposes 8000, persists under ~/.openhands, and expects project folders mounted at /projects

Additional changed behavior

  • Agent Canvas is a port of the OpenHands frontend that talks directly to openhands-agent-server / software-agent-sdk, not the full OpenHands app backend.
  • Hosted/account/org management UI is removed or hidden in the OSS Agent Canvas path.
  • Agent Canvas local mode auto-generates and injects a session API key. Public mode (--public) requires LOCAL_BACKEND_API_KEY and prompts the browser user for it.
  • OpenHands frontend default model in the inspected repo is openhands/claude-opus-4-5-20251101; Agent Canvas frontend default is openhands/minimax-m2.7; SDK/agent-server direct default is gpt-5.5.
  • Agent Canvas npm/local mode runs without a sandbox and has host filesystem access. Docker mode is the isolated path documented in the README.
  • Agent Canvas full stack includes the automation backend by default.
  • Agent Canvas has a top-level /mcp page and marketplace data from @openhands/extensions; local install filters out OAuth-only marketplace options.
  • Terminal tab is a read-only transcript of agent events, not an interactive shell.
  • Browser tab is screenshot/URL state from browser-tool events, not a full manually-driven browser UI.
  • Agent Canvas adds the canvas_ui tool and conditionally includes browser_tool_set / task_tool_set based on env, server tool metadata, and the sub-agent setting.
  • Local LLM profile switching and ACP model switching are local-backend features; cloud repository/search calls use the cloud proxy path.

Required migration work

  • Add a migration guide: "Migrating from OpenHands Local GUI to Agent Canvas".
  • Add a backup-first step for ~/.openhands.
  • Document that old plaintext LLM API keys and custom secret values are not reused under the default generated OH_SECRET_KEY flow.
  • Provide a one-time migration/import path for plaintext old secrets into encrypted agent-server storage, or explicitly require users to re-enter LLM API keys and custom secrets.
  • Provide a one-time importer for old embedded llm_profiles into ~/.openhands/profiles/*.json, or explicitly require users to recreate profiles.
  • Document that old provider_tokens are not consumed by local Agent Canvas and provide the replacement workflow for repo/git access.
  • Document app preferences and disabled skills as browser-local Agent Canvas settings that do not import from old settings.json.
  • Document the env-var mapping table above.
  • Document changed launch commands, ports, workspace behavior, sandbox/security model, default model, MCP/Tavily setup, and automation availability.
  • State that old conversation history is not automatically migrated/listed.
  • Add a fixture-based regression test with an old-style settings.json and secrets.json to lock the chosen migration behavior.

Suggested implementation approach

  1. Build a migration checker that runs before or during first Agent Canvas startup when ~/.openhands/settings.json or ~/.openhands/secrets.json exists.
  2. Detect old app-server fields:
    • llm_profiles
    • plaintext agent_settings.llm secret fields
    • secrets.json.custom_secrets plaintext secret fields
    • secrets.json.provider_tokens
    • app preference fields
    • disabled_skills
  3. For sensitive fields, import through agent-server APIs with plaintext exposure limited to local startup code, then save back through the new encrypted stores.
  4. Write imported LLM profiles through /api/profiles/{name} so files land in ~/.openhands/profiles/*.json with the current profile-store validation rules.
  5. Leave old files backed up or write a migration marker; do not silently overwrite old files with partial data.

Contributor guide