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
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-canvas2. 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.03. Open the canvas, add a model under Settings, and start a conversation
open http://localhost:8000The 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
| Check | Result |
|---|---|
| Secrets | 8 candidates found and read; see the notes above. |
| Suspicious code | 5 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 2 npm lifecycle scripts. 1 installer script (one fetches and runs a remote script) |
| Committed binaries | None. |
| CI workflows | 19 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 hosts | 40 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 vulnerabilities | 30 advisories across 1,447 pinned packages: 0 critical, 18 high, 12 moderate, 0 low. package-lock.json: 1,507 packages, 30 advisories. |
| Project hygiene | Has automated dependency updates, licence file. Missing security policy, CodeQL, contributing guide. |
| OpenSSF Scorecard | Not 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)
| Where | Rule | Match |
|---|---|---|
| __tests__/utils/redact-mcp-secrets.test.ts:43 | github-token | github…TUV (33 chars) |
| __tests__/utils/redact-mcp-secrets.test.ts:47 | github-token | github…TUV (33 chars) |
| tests/e2e/mock-llm/automations/mock-llm-preset-automation.spec.ts:169 | slack-token | xoxb-t…ken (15 chars) |
| tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:185 | slack-token | xoxb-i…ken (18 chars) |
| tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:216 | slack-token | xoxb-v…ope (28 chars) |
| tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:239 | slack-token | xoxb-s…ken (15 chars) |
| tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:252 | slack-token | xoxb-s…ret (18 chars) |
| tests/e2e/mock-llm/mcp/mock-llm-mcp-slack-credentials.spec.ts:286 | slack-token | xoxb-s…ret (18 chars) |
Pattern hits (5)
| Where | Rule | Match |
|---|---|---|
| .openhands/setup.sh:12 | download-piped-to-shell | curl -LsSf https://astral.sh/uv/install.sh | sh |
| __tests__/scripts/dev-safe.test.ts:372 | download-piped-to-shell (test/example) | "curl -LsSf https://astral.sh/uv/install.sh | sh", |
| __tests__/scripts/dev-safe.test.ts:981 | download-piped-to-shell (test/example) | expect(output).toContain("curl -LsSf https://astral.sh/uv/install.sh | sh"); |
| scripts/dev-safe.mjs:340 | download-piped-to-shell | " curl -LsSf https://astral.sh/uv/install.sh | sh", |
| tests/e2e/live/scripts/run-live-e2e.mjs:193 | download-piped-to-shell (test/example) | "Install uv with: `curl -LsSf https://astral.sh/uv/install.sh | sh`.", |
npm lifecycle scripts (2)
package.jsonpostinstall: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.jsonprepare:husky
Installer scripts (1)
- .openhands/setup.sh, 41 lines, fetches and runs a remote script; talks to astral.sh
Worst known vulnerabilities (24 of 30)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-27p8-2357-5qqv | high | @xmldom/xmldom@0.8.13 | xmldom: DocType `name` Injection Bypasses requireWellFormed |
| GHSA-4w3w-2rp5-g8jm | high | @xmldom/xmldom@0.8.13 | xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed |
| GHSA-8344-3jmq-59r6 | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-time attribute deduplication |
| GHSA-93r5-fhx6-vmg9 | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-time parsing via the malformed-input recovery path - `parseElementStartPart` re-scan and `normalize()`… |
| GHSA-965w-775f-mr7g | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-memory consumption |
| GHSA-c7q8-3ch8-vqpv | high | @xmldom/xmldom@0.8.13 | xmldom: Processing Instruction Target Injection Bypasses requireWellFormed |
| GHSA-w2rr-34g9-rvrj | high | @xmldom/xmldom@0.8.13 | xmldom: Element name injection via createElement() bypasses requireWellFormed |
| GHSA-x4fp-j954-r2f4 | high | @xmldom/xmldom@0.8.13 | xmldom: End-tag Whitespace-Trim Regex ReDoS - quadratic backtracking in the 0.8.x end-tag parser |
| GHSA-73wf-gq98-2v4g | high | browserslist@4.28.2 | Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.json custom stats (normalizeStats) |
| GHSA-c83g-rgw3-j3cx | high | browserslist@4.28.2 | Browserslist: Unbounded memory growth (no cache eviction) via distinct query results, leading to eventual OOM |
| GHSA-7pqw-9j4j-h8q3 | high | extract-zip@2.0.1 | extract-zip allows arbitrary file writes through symlink archive entries |
| GHSA-jmr9-qjv8-65gv | high | extract-zip@2.0.1 | extract-zip unvalidated symlink path traversal |
| GHSA-5jgf-p345-68v8 | high | fast-uri@3.1.5 | fast-uri vulnerable to host confusion via skipped IDN canonicalization on scheme-relative references |
| GHSA-f65p-4m7j-42xc | high | fast-uri@3.1.5 | fast-uri vulnerable to server-side request forgery via malformed IPv6 normalization |
| GHSA-fph4-wmhf-6fwf | high | fast-uri@3.1.5 | fast-uri vulnerable to server-side request forgery via repeated hostname percent-decoding |
| GHSA-jqff-g426-hqxp | high | fast-uri@3.1.5 | fast-uri vulnerable to host confusion via percent-encoded scheme normalization |
| GHSA-2883-xcg3-v3hh | high | js-yaml@4.3.1 | js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources |
| GHSA-2v37-7h3g-55p8 | high | nanoid@3.3.17 | nanoid: custom generators can loop indefinitely when size is zero |
| GHSA-82fw-gwwq-j7x9 | moderate | @vitest/mocker@4.1.10 | Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock |
| GHSA-6gmq-8vp8-gcm6 | moderate | @xmldom/xmldom@0.8.13 | xmldom: XML fragment injection via invalid EntityReference.nodeName during requireWellFormed serialization |
| GHSA-6h8r-xr42-gp59 | moderate | @xmldom/xmldom@0.8.13 | xmldom: Parser silently accepts a not-well-formed end tag whose name is followed by a line break and trailing content |
| GHSA-w5vr-8v7q-w6rv | moderate | baseline-browser-mapping@2.10.33 | baseline-browser-mapping process termination on invalid input causes denial of service |
| GHSA-r4w5-6pfg-jxp5 | moderate | electron@42.3.3 | Electron: ProtocolResponse.url reuses the default session cache instead of the registering session |
| GHSA-px8p-9vwx-vf98 | moderate | fflate@0.4.8 | fflate unzipSync can enter an infinite loop when parsing malformed ZIP64 archives |
Workflows worth a look
- .github/workflows/pr-artifacts.yml: pull_request_target
- .github/workflows/pr-description-check.yml: pull_request_target
- .github/workflows/pr.yml: pull_request_target
- .github/workflows/release-ready.yml: pull_request_target
By the numbers
| Stars | 88.1K |
|---|---|
| Forks | 11.6K |
| Contributors | 549 |
| Commits | 8,234 |
| Open issues | 417 |
| Open pull requests | 400 |
| Releases | 137 |
| Latest release | v1.18.0 |
| Licence | MIT |
| Main language | TypeScript |
| Project age | 2 years |
| Last push | Sep 16, 2026 |
| Tracked files | 2,278 |
| Lines of code | 375.3K |
| Checkout size | 16 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.