Rohit Malhotraandopenhands e408deea9e fix: bind static-server dual-stack to fix Docker E2E ECONNREFUSED on ::1 (#1032)
* fix: bind static-server dual-stack to fix Docker E2E ECONNREFUSED on ::1

The Docker mock-LLM E2E tests were flaky because static-server.mjs
bound to 0.0.0.0 (IPv4 only), but Playwright and Chromium often
resolve `localhost` to ::1 (IPv6) on Ubuntu CI runners, causing
intermittent ECONNREFUSED.

The non-Docker tests didn't have this problem because they go
through ingress.mjs, which calls server.listen(port) without a
host argument — Node.js defaults to :: (dual-stack: IPv4 + IPv6).

Changes:
- static-server.mjs: default host from "0.0.0.0" to null; when
  null, call server.listen(port) without host so Node binds to ::
- docker/entrypoint.sh: drop --host 0.0.0.0 from both
  static-server invocations so they use the dual-stack default
- playwright.mock-llm.config.ts: drop --host 0.0.0.0 from the
  public-mode static server (tests hit it directly via localhost)

Callers behind ingress.mjs (dev-with-automation, dev-static) still
pass --host 0.0.0.0 explicitly, which is fine since the ingress
itself is already dual-stack.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: use 127.0.0.1 in Docker E2E config to avoid IPv6 ECONNREFUSED

The Docker mock-LLM E2E tests fail with ECONNREFUSED ::1:18300
because Playwright/Chromium resolve `localhost` to ::1 (IPv6) on
Ubuntu CI runners, but the Docker container's static-server binds to
0.0.0.0 (IPv4 only).

The non-Docker tests don't have this issue because they go through
ingress.mjs which binds to :: (dual-stack: IPv4 + IPv6).

Rather than changing the server binding (which could have
side-effects inside the Docker container), this fix changes the
Docker Playwright config to use 127.0.0.1 directly for all URLs:
INGRESS_URL, MOCK_LLM_BACKEND_URL, MOCK_LLM_PUBLIC_MODE_URL, and
the webServer health-check probe. This bypasses DNS resolution
entirely and connects via IPv4.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: keep Docker container alive when a backend service exits

The Docker entrypoint used `wait -n` which exits the entire
container when ANY child process exits. After heavy automation
tests (which spawn multiple conversations), the agent-server or
automation backend could exit, taking down the static-server with
it — causing ECONNREFUSED for subsequent tests.

In the non-Docker path, each service is an independent host process,
so one crashing doesn't affect the others. The ingress proxy
returns 502 for the dead backend but stays up.

Change the entrypoint to monitor children in a loop: log crashes
but keep the container running as long as any service is still
alive. Only exit when ALL tracked children are dead. The SIGTERM
trap still handles clean shutdown.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: simplify entrypoint keep-alive to avoid wait -n interaction issues

Replace the complex PID monitoring loop with a simple sleep loop.
The previous wait -n based loop regressed automation tests (5/14
vs 8/14 on the simpler IPv4-only commit), likely due to bash
wait -n signal handling interacting poorly with child processes.

The sleep loop keeps the container alive indefinitely. The existing
SIGTERM/SIGINT trap handles clean shutdown.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: address review feedback on entrypoint and docs

- Wait on STATIC_PID only (not all children): the static-server is
  the critical ingress process. If it dies the container exits with
  a meaningful exit code. Backend crashes (agent-server, automation)
  are tolerated — the proxy returns 502.  (Copilot feedback)

- Add `exit 0` to cleanup(): ensures the script terminates after a
  SIGTERM-triggered trap return instead of falling through. The
  `wait` builtin is signal-interruptible so SIGTERM is processed
  immediately — no stale sleep blocking delivery.  (all-hands-bot)

- Update AGENTS.md to match the actual behavior (wait on static-server
  PID, not a monitoring loop or infinite sleep).  (Copilot feedback)

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: use signal-safe sleep-wait loop with static-server liveness check

The bare `wait "$STATIC_PID"` approach failed the same way as
the original `wait -n` (8/14 — conversation/model-switch tests
get ECONNREFUSED after automation).  The `while true; do sleep
86400; done` pattern from the previous commit was the only one
that passed 14/14, but had two issues flagged in review:

1. Bare `sleep` as foreground blocks SIGTERM delivery (all-hands-bot)
2. Container stays alive forever even if static-server dies (Copilot)

This commit addresses both:
- `sleep 10 & wait $!` — `wait` (builtin) is the foreground op,
  so SIGTERM interrupts it immediately and the trap fires.
- `while kill -0 "$STATIC_PID"` — loop exits when the critical
  ingress process dies; container exits with a meaningful code.
- `cleanup()` keeps `exit 0` so the script terminates after a
  signal-triggered trap return.

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-06-02 18:33:39 +00:00
2026-04-24 17:33:22 -04:00

agent-canvas

Warning

This project is in the Beta phase. It may be vibecoded, untested, or out of date. OpenHands takes no responsibility for the code or its support. Learn more.

Project Status: Beta

OpenHands is a platform for orchestrating coding agents across different environments. You can:

  • ⌨️ prompt agents manually
  • 🕐 run agents on a schedule
  • ⚡ trigger agents automatically — e.g. from Slack, GitHub, or Datadog.

Agents can run anywhere:

  • 🧑‍💻 on your laptop
  • 🖥️ on a remote virtual machine
  • ☁️ in our hosted cloud
  • 🏢 or inside your company’s infrastructure

The same Agent Canvas frontend can swap between each of these environments, so you can see everything in one place.

OpenHands works with any agent harness (e.g. Claude Code, Codex) or connect directly to an LLM (e.g. Anthropic, OpenAI, Gemini, Mistral, Minimax, Kimi).

If you have questions or feedback, please open a GitHub issue or join the #proj-agent-canvas channel in Slack.

Screenshot 2026-05-11 at 10 13 19 AM

Project ownership and support

  • Current status: Beta.
  • Support channel: #proj-agent-canvas.
  • Support level: Best effort while the project remains in Beta.

Quickstart

You can install OpenHands to run agents on any machine: on your laptop, on a dedicated computer like a Mac Mini, or on a server in the cloud.

The most powerful way to run OpenHands is on a server in the cloud. This allows your agents to continue running even when your laptop is shut, and makes it easier to trigger your agents through third-party services like Slack, GitHub, and Datadog. See SELF_HOSTING.md for details, especially with respect to security hardening.

Notably, you can run the backend in multiple different environments, and switch between them from the same Agent Canvas frontend. E.g. you can share an Agent Server with your team for agents doing code review and dependency updates, then have your personal agents running on your laptop.

Option 1: Without a Sandbox

Warning

This runs the agent-server directly on the machine you're installing on — the agent will have full access to your filesystem!

Prerequisites: Node.js 22.12.x or later, uv

npm install -g @openhands/agent-canvas
agent-canvas

Option 2: With a Docker Sandbox

Prerequisites:

  • Docker: Docker Desktop on macOS/Windows, or Docker Engine/Docker Desktop on Linux.
  • A host directory for PROJECTS_PATH containing the project folders you want the agent to access. Create it before starting the container.

macOS / Linux:

docker pull ghcr.io/openhands/agent-canvas:1.0.0-alpha.10

export PROJECTS_PATH="$HOME/projects"  # directory containing your project folders
mkdir -p "$PROJECTS_PATH" "$HOME/.openhands"

docker run -it --rm \
  -p 8000:8000 \
  -v "$HOME/.openhands:/home/openhands/.openhands" \
  -v "${PROJECTS_PATH}:/projects" \
  ghcr.io/openhands/agent-canvas:1.0.0-alpha.10

Windows (PowerShell / Windows Terminal): See README.windows.md for the equivalent commands.

The agent will be able to access any project under PROJECTS_PATH.

Option 3: From Source

Warning

This runs the agent-server directly on the machine you're installing on — the agent will have full access to your filesystem!

Prerequisites: Node.js 22.12.x or later, npm, uv (for running the agent server via uvx)

git clone https://github.com/OpenHands/agent-canvas.git
cd agent-canvas
npm install
npm run dev

Access the UI at http://localhost:8000. You can add additional backends directly from the UI.

Architecture

Agent Canvas is powered by the OpenHands Agent Server, a REST API for running multiple agents on a single machine. Each Agent Server runs on a single host/port; the Agent Canvas can connect to multiple Agent Servers and easily flip between them.

You can run an Agent Server anywhere:

  • Directly on your laptop (be careful!)
  • On a dedicated machine like a Mac Mini
  • On a virtual machine in the cloud
  • Inside OpenHands Cloud (our commercial offering)

The Agent Server is often paired with an Automation Server, which lets you set up agents that run on a schedule or in response to events.

image

More documentation

S
Languages
TypeScript 93.7%
JavaScript 4.7%
Python 0.9%
Shell 0.3%
CSS 0.2%
Other 0.1%