← Home

Minimal OpenHands app_server architecture

Implementation notes for enyst/app-server, the extracted control-plane bridge for Agent Canvas and sandbox-hosted agent-server runtimes. Prepared by OpenHands (AI) on behalf of the user, June 2026.

Short version: the minimal app_server is a session-key-protected FastAPI control plane. It stores sandbox and app-conversation metadata, starts conversations on a sandbox-hosted agent-server, then exposes HTTP and WebSocket gateway routes so Agent Canvas does not need direct browser access to the sandbox.

Shape

flowchart LR
  AC["Agent Canvas"] -->|X-Session-API-Key| AS["minimal app_server"]
  AS --> Store[("app_server state: sandboxes, conversations, start tasks")]
  AS -->|Docker provider creates / pauses / resumes / deletes| D["Docker container"]
  D --> RT["sandbox-hosted agent-server"]
  AS -->|X-Session-API-Key: runtime key| RT
  RT --> WS["/sockets/events + /sockets/bash-events"]
  AS -->|/ws/events + /ws/bash-events| AC
  AS -->|temporary only| Temp["settings/secrets compatibility store"]
    
FastAPI session-key auth HTTP proxy WebSocket tunnel agent-server runtime Docker sandbox lifecycle temporary settings bridge

Responsibilities

Owns

App-conversation records, start-task records, Docker sandbox lifecycle, sandbox status, runtime URL/session-key discovery, and gateway routing.

Proxies

Runtime conversation events, confirmations, ask-agent calls, pause/run calls, git changes/diff, event history, and event/bash WebSockets.

Temporary

/api/v1/settings and /api/v1/secrets exist only for current Agent Canvas cloud-style compatibility. MCP config is stored as opaque agent_settings.mcp_config data here.

Does not own

Agent execution, LLM profile semantics, canonical MCP/server settings, long-term saved secrets, billing, org management, old OpenHands frontend routes, or SaaS account chrome.

Conversation start

sequenceDiagram
  participant C as Agent Canvas
  participant A as app_server
  participant R as agent-server

  C->>A: POST /api/v1/app-conversations
resolved runtime settings + initial message A->>A: create app-conversation/start-task records A->>A: DockerSandboxService.start_sandbox
run agent-server container + inject OH_SESSION_API_KEYS_0 A->>R: POST /api/conversations
same payload + runtime session key R-->>A: ConversationInfo A-->>C: AppConversationStartTask READY C->>A: GET /api/v1/app-conversations?ids=... A-->>C: conversation_url, sandbox_status, websocket_url C->>A: WS /ws/events/{id} A->>R: WS /sockets/events/{id} R-->>C: SDK events via tunnel

API surface

NeedRouteNotes
Conversation start/list/api/v1/app-conversations*Creates metadata and forwards start payload to agent-server.
Sandbox statePOST /api/v1/sandboxes/{id}/pause|resumeCalls the sandbox provider. Docker mode maps these to container pause/unpause or restart.
Runtime HTTP/api/conversations/{id}/..., /api/v1/git/...Thin pass-through using sandbox runtime session key.
Runtime WebSocket/ws/events/{id}, /ws/bash-events/{id}Tunnels frames to runtime agent-server sockets.
Compatibility settings/api/v1/settings, /api/v1/secretsMarked temporary in source; not the long-term owner.

Testing policy

The port was written test-first. The repo includes a fake agent-server runtime and tests covering session-key auth, conversation start and metadata, HTTP proxies, sandbox pause/resume, event and bash WebSocket gateways, and temporary settings/secrets including MCP config round-trip. It also ports/adapts OpenHands/OpenHands non-enterprise Docker sandbox lifecycle tests for container metadata translation, session-key injection, port mapping, search/pagination, get-by-session-key, pause/resume, and delete.

python -m ruff check .
python -m pytest
← Back to studies