6 min read

OpenHands Agent Canvas: Run Coding Agents Yourself (GitHub, Scanned)

A self-hosted control centre for coding agents: run OpenHands, Claude Code or Codex locally or in Docker, and automate them.

OpenHands repository on GitHub

OpenHands, formerly OpenDevin, is the reference open-source coding agent: give it a task and a repository and it plans, edits files, runs commands, reads the output and tries again, inside a sandbox rather than on your machine. This repository is where the project now lives for users. It has become Agent Canvas, a self-hosted control centre: a web and desktop app that starts conversations with the OpenHands agent, and with Claude Code, Codex, Gemini or any agent that speaks the Agent Client Protocol, across backends on your laptop, in Docker, on a VM or in the cloud.

The canvas adds what a terminal agent lacks: automations that run on a schedule or on a webhook (decompose a GitHub issue into tasks, post a report to Slack), a way to switch between backends without losing your place, and a model picker that takes any LLM. The agent itself, the sandbox and the Python SDK live in the separate software-agent-sdk repository; this one is the TypeScript application that drives them. Both are MIT-licensed and backed by the same company.

  • Repository: github.com/OpenHands/OpenHands
  • Licence: MIT (MIT License)
  • Language: TypeScript. Stars: 88.1K. Forks: 11.6K. Last push: Sep 16, 2026.
  • Scan: clean, read the notes, Sep 15, 2026, commit 82203bb
🔍
Scan: clean, read the notes. Nothing malicious in this repository, which is the control-centre application; the agent runtime it drives lives in a separate SDK repository this scan does not cover. The npm install runs an agent with full access to your files, by design.

Who it is for

Developers who want an always-on agent working in their repositories with a model of their choosing, teams that want one place to run several coding agents against local or private infrastructure, and anyone who wants to read how a production agent stack is put together.

Getting started

1. Install with npm (Node 22.12 or newer and uv required); this runs the agent on your machine with full access to your files

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

2. Or run it in Docker, confined to a folder of projects

export PROJECTS_PATH="$HOME/projects" && 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.18.0

3. Open the canvas, add a model under Settings, and start a conversation

open http://localhost:8000

The README carries the same warning twice: the npm and from-source installs run the agent server directly on the host, where it can reach everything you can. Use the Docker option on a machine you share, and give it a branch rather than production keys.

Safety scan

We cloned OpenHands/OpenHands at commit 82203bb on Sep 15, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.

  • No secrets: the eight candidates are fake GitHub and Slack tokens in the tests for a secret-redaction feature. No self-decoding code, no bare-IP URLs, no committed binaries.
  • The pattern hits are setup scripts installing uv from astral.sh, in development and end-to-end test helpers.
  • Two npm lifecycle scripts: a postinstall that prints a welcome message when installed globally, and prepare, which installs husky git hooks. Neither fetches anything.
  • Known advisories: 30 in the single lockfile, none critical, and every high one in @xmldom/xmldom, a transitive frontend dependency. The Python side, and so its dependencies, live in the software-agent-sdk repository.
  • 19 workflows. Four use pull_request_target: the PR checks are written to avoid checking out the pull request, and the artifacts job runs only after an approved review and skips forks. 3 of 14 third-party actions pinned to a commit; Dependabot on.
  • No security policy file in the repository or in the organisation's .github repository at the time of the scan.
  • The npm quickstart runs the agent server directly on your machine with full filesystem access, and the README says so in a warning. The Docker option confines the agent to the project folders you mount.

What the scanner counted

CheckResult
Secrets8 candidates found and read; see the notes above.
Suspicious code5 pattern hits found and read; every one is listed under the raw findings.
Install-time code2 npm lifecycle scripts. 1 installer script (one fetches and runs a remote script)
Committed binariesNone.
CI workflows19 workflows. 4 use pull_request_target, none check out the pull request head. 11 of 14 third-party actions pinned to a tag rather than a commit.
Network hosts40 distinct hosts referenced from source; most often app.all-hands.dev, github.com, runtime.example.com, cloud.example.com. No URLs to bare IP addresses.
Known vulnerabilities30 advisories across 1,447 pinned packages: 0 critical, 18 high, 12 moderate, 0 low. package-lock.json: 1,507 packages, 30 advisories.
Project hygieneHas automated dependency updates, licence file. Missing security policy, CodeQL, contributing guide.
OpenSSF ScorecardNot scored: the project is not in Scorecard's weekly index.

The raw findings

Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.

Secret candidates (8, redacted)
WhereRuleMatch
__tests__/utils/redact-mcp-secrets.test.ts:43github-tokengithub…TUV (33 chars)
__tests__/utils/redact-mcp-secrets.test.ts:47github-tokengithub…TUV (33 chars)
tests/e2e/mock-llm/automations/mock-llm-preset-automation.spec.ts:169slack-tokenxoxb-t…ken (15 chars)
tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:185slack-tokenxoxb-i…ken (18 chars)
tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:216slack-tokenxoxb-v…ope (28 chars)
tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:239slack-tokenxoxb-s…ken (15 chars)
tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:252slack-tokenxoxb-s…ret (18 chars)
tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:286slack-tokenxoxb-s…ret (18 chars)
Pattern hits (5)
WhereRuleMatch
.openhands/setup.sh:12download-piped-to-shellcurl -LsSf https://astral.sh/uv/install.sh | sh
__tests__/scripts/dev-safe.test.ts:372download-piped-to-shell (test/example)"curl -LsSf https://astral.sh/uv/install.sh | sh",
__tests__/scripts/dev-safe.test.ts:981download-piped-to-shell (test/example)expect(output).toContain("curl -LsSf https://astral.sh/uv/install.sh | sh");
scripts/dev-safe.mjs:340download-piped-to-shell" curl -LsSf https://astral.sh/uv/install.sh | sh",
tests/e2e/live/scripts/run-live-e2e.mjs:193download-piped-to-shell (test/example)"Install uv with: `curl -LsSf https://astral.sh/uv/install.sh | sh`.",
npm lifecycle scripts (2)
  • package.json postinstall: node -e "if (process.env.npm_config_global === 'true') { console.log('\n\x1b[32m\x1b[1m\u2713 @openhands/agent-canvas installed!\x1b[0m\n\nTo start Agent Canvas, run:\n\n \x1b[36m\x1b[1magent-canvas\x…
  • package.json prepare: husky
Installer scripts (1)
Worst known vulnerabilities (24 of 30)
AdvisorySeverityPackageSummary
GHSA-27p8-2357-5qqvhigh@xmldom/xmldom@0.8.13xmldom: DocType `name` Injection Bypasses requireWellFormed
GHSA-4w3w-2rp5-g8jmhigh@xmldom/xmldom@0.8.13xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed
GHSA-8344-3jmq-59r6high@xmldom/xmldom@0.8.13xmldom: Quadratic-time attribute deduplication
GHSA-93r5-fhx6-vmg9high@xmldom/xmldom@0.8.13xmldom: Quadratic-time parsing via the malformed-input recovery path - `parseElementStartPart` re-scan and `normalize()`…
GHSA-965w-775f-mr7ghigh@xmldom/xmldom@0.8.13xmldom: Quadratic-memory consumption
GHSA-c7q8-3ch8-vqpvhigh@xmldom/xmldom@0.8.13xmldom: Processing Instruction Target Injection Bypasses requireWellFormed
GHSA-w2rr-34g9-rvrjhigh@xmldom/xmldom@0.8.13xmldom: Element name injection via createElement() bypasses requireWellFormed
GHSA-x4fp-j954-r2f4high@xmldom/xmldom@0.8.13xmldom: End-tag Whitespace-Trim Regex ReDoS - quadratic backtracking in the 0.8.x end-tag parser
GHSA-73wf-gq98-2v4ghighbrowserslist@4.28.2Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.json custom stats (normalizeStats)
GHSA-c83g-rgw3-j3cxhighbrowserslist@4.28.2Browserslist: Unbounded memory growth (no cache eviction) via distinct query results, leading to eventual OOM
GHSA-7pqw-9j4j-h8q3highextract-zip@2.0.1extract-zip allows arbitrary file writes through symlink archive entries
GHSA-jmr9-qjv8-65gvhighextract-zip@2.0.1extract-zip unvalidated symlink path traversal
GHSA-5jgf-p345-68v8highfast-uri@3.1.5fast-uri vulnerable to host confusion via skipped IDN canonicalization on scheme-relative references
GHSA-f65p-4m7j-42xchighfast-uri@3.1.5fast-uri vulnerable to server-side request forgery via malformed IPv6 normalization
GHSA-fph4-wmhf-6fwfhighfast-uri@3.1.5fast-uri vulnerable to server-side request forgery via repeated hostname percent-decoding
GHSA-jqff-g426-hqxphighfast-uri@3.1.5fast-uri vulnerable to host confusion via percent-encoded scheme normalization
GHSA-2883-xcg3-v3hhhighjs-yaml@4.3.1js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources
GHSA-2v37-7h3g-55p8highnanoid@3.3.17nanoid: custom generators can loop indefinitely when size is zero
GHSA-82fw-gwwq-j7x9moderate@vitest/mocker@4.1.10Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock
GHSA-6gmq-8vp8-gcm6moderate@xmldom/xmldom@0.8.13xmldom: XML fragment injection via invalid EntityReference.nodeName during requireWellFormed serialization
GHSA-6h8r-xr42-gp59moderate@xmldom/xmldom@0.8.13xmldom: Parser silently accepts a not-well-formed end tag whose name is followed by a line break and trailing content
GHSA-w5vr-8v7q-w6rvmoderatebaseline-browser-mapping@2.10.33baseline-browser-mapping process termination on invalid input causes denial of service
GHSA-r4w5-6pfg-jxp5moderateelectron@42.3.3Electron: ProtocolResponse.url reuses the default session cache instead of the registering session
GHSA-px8p-9vwx-vf98moderatefflate@0.4.8fflate unzipSync can enter an infinite loop when parsing malformed ZIP64 archives
Workflows worth a look

By the numbers

Stars88.1K
Forks11.6K
Contributors549
Commits8,234
Open issues417
Open pull requests400
Releases137
Latest releasev1.18.0
LicenceMIT
Main languageTypeScript
Project age2 years
Last pushSep 16, 2026
Tracked files2,278
Lines of code375.3K
Checkout size16 MB

Lines by language: TypeScript 306.5K, JSON 42.5K, JavaScript 13.4K, Markdown 4,394, YAML 4,011, Python 2,721.

Questions

Is OpenHands free?

The software is MIT-licensed and free. What costs money is the model: a long agent session can use millions of tokens, so a hosted model bill adds up quickly, and a local model needs serious hardware to be useful. OpenHands Cloud is the paid hosted version with credits included.

How does OpenHands differ from Claude Code or Codex?

Claude Code and Codex are agents from model vendors, tuned to their own models. OpenHands is model-agnostic and self-hosted: you choose the model, the sandbox runs where you say, and the whole agent loop is open to read and change. Agent Canvas can also run Claude Code and Codex as backends, so it is less a rival than a place to run all of them.

Is it safe to let OpenHands run commands?

It depends on how you install it. The Docker option runs the agent in a container that sees only the project folders you mount. The npm install runs the agent server on your host with your permissions, which the README warns about twice. Either way the agent can do unwise things with what it can reach, so give it a branch, review its diffs, and do not hand it production keys.


This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.