nanobot
HKUDS · github.com/HKUDS/nanobot
nanobot is an MIT-licensed, self-hosted Python agent runtime that keeps all agent state in local files and sends nothing to its maintainers, which gives it strong exit, provider-independence and no-unilateral-change results. Its main gaps are auditability (no complete, tamper-evident or agent-protected action record), control (no approval gates, no per-recipient or per-domain limits, and in the default full-access mode the agent can edit its own config), and the lack of export, full-deletion and build-provenance tooling.
- Local-first state with no provider account or hosted store (P2, P5, T3)
- No telemetry and nothing held by the provider (T3)
- No auto-update or remote flags; owner-initiated upgrades only (M5)
- Single command stops all local agent activity (C3)
- Action record incomplete, prunable, unsigned and writable by the agent by default (A1, A2, A3)
- No owner approval gates, and limits the agent can edit away in default mode (C1, C2)
- No export/import command and no full-deletion command (P1, C6)
- Unattested PyPI wheels with a prebuilt TUI binary; memory-only change history (V1, V3)
All 26 findings
Release tag v0.3.5, commit 1bb712d3488915ca4ed9ccc1a93067ff722f5ab9 (2026-09-15); main was at 678cd8f4 on the evaluation date. Platform: Linux/macOS/Windows Python 3.11+ package with bundled WebUI and native TUI. Default configuration per nanobot/config/schema.py. Desk review only; evaluated 2026-09-30.
Portable · Can you leave, take the whole agent with you, and keep it alive?
70%- P1Owner-usable round trip
No export/import command exists. State is plain local files under ~/.nanobot (config, workspace, sessions) that the owner can copy at any time without approval, but restoring on another machine means reinstalling nanobot and moving folders by hand (one shortfall: restore is not a single documented action).
- Quick start: data locations
- Deployment: what must persist
- CLI reference
- Troubleshooting: sessions after moves
› How it could pass
Suggestions. Any change that meets the test counts.
- Ship a `nanobot export` / `nanobot import` pair (or WebUI equivalent) that bundles config, workspace, sessions and all OAuth token files
- Document a one-step move-to-another-machine procedure
Partial - P2Complete export
All agent state (config and credentials, instructions, Markdown memory with its source history, session JSONL, cron jobs and triggers, skills, plugins, files, channel sessions) is on the owner's disk, and HKUDS holds nothing. OAuth integrations re-link through the model vendors' own clients or MCP dynamic client registration, not an HKUDS-registered client; Codex and Copilot token files sit outside ~/.nanobot but can be recreated by logging in again.
Pass - P3Runs without the provider
The runtime is MIT-licensed and needs no HKUDS account, licence key, relay or telemetry, so the core agent runs with HKUDS unreachable. The CLI Apps capability (on by default) resolves apps through a hard-coded, HKUDS-hosted catalog (hkuds.github.io/CLI-Anything/registry.json) that the owner cannot repoint; without it or a cached copy, installing and running CLI Apps fails.
- LICENSE (MIT)
- CLI Apps catalog URL and fetch
- CLI Apps catalog fallback
- CLI Apps enabled by default
- On-demand PyPI version check
› How it could pass
Suggestions. Any change that meets the test counts.
- Make the CLI Apps catalog URL owner-configurable
- Let installed CLI Apps run from their local install record without a catalog lookup
Partial - P4Model independence
Ollama, vLLM, LM Studio and any OpenAI-compatible endpoint are documented, and tools, memory, cron and channels run in the runtime. However, no minimum model contract is documented, and some capabilities are lost off hosted models: voice transcription has only hosted backends, and reference-image editing is unsupported for the local image providers.
› How it could pass
Suggestions. Any change that meets the test counts.
- Document the minimum model contract (native tool calling) and test it against a local open-weight model
- Add a local speech-to-text backend
- Support reference-image edits for local image providers
Partial - P5Exit survives termination
The default offering is local-first: all agent state is written to the owner's disk, with no HKUDS account or hosted store, so provider termination or shutdown cannot remove access to it.
Pass
Transparent · Can you see everything the agent is, and everything shaping it?
75%- T1Readable without the provider
State is stored as JSONL, JSON, SQLite, Markdown and a git repo, with no nanobot-applied encryption. The docs give file locations and roles but no field-level schema for session records, run records, cron/trigger stores or the llm_usage SQLite tables.
› How it could pass
Suggestions. Any change that meets the test counts.
- Publish schemas for session JSONL (including provider_state and metadata lines), run records, jobs.json/action.jsonl, trigger stores and llm_usage tables
Partial - T2What runs is what you see
Model, tools and access settings are visible through /status, nanobot status, the WebUI and config.json, and compaction summaries and runtime-context blocks are persisted with the session. The assembled system prompt, built each turn from package templates, has no documented viewer: the WebUI context view shows only the session part, and full request capture needs a third-party Langfuse plugin (OpenAI-compatible providers only), which G3 caps at partial. NANOBOT_* environment overrides are not shown in status (edge gap, not counted).
› How it could pass
Suggestions. Any change that meets the test counts.
- Add a documented view or log of the exact assembled request for any model call
- Show the effective merged configuration including environment overrides
Partial - T3No shadow data
nanobot is self-hosted, and HKUDS operates no service that receives owner content or usage data. The code contains no telemetry or analytics, and the only HKUDS-related requests (an owner-triggered PyPI version check, the GitHub-hosted CLI Apps catalog, a TUI binary fallback) carry no content.
Pass - T4No hidden steering
The source shows no paid placement or self-preference in the agent's context, provider order, search default (DuckDuckGo) or tool choices, and the setup has no preselected partner provider. Partner and affiliate links appear only in the README and docs. The bundled clawhub skill is visible and can be switched off with disabledSkills.
Pass
Auditable · Can you reconstruct exactly what the agent did, and trust the record?
17%- A1Complete action record
Main, cron, heartbeat and trigger turns are saved to session JSONL with full tool-call arguments. However, subagent tool calls stay only in memory and Dream runs are ephemeral (unrecorded entry paths), nothing tells the owner when an action goes unrecorded, and by default tool results over 16,000 chars are cut to a 1,200-char preview whose full copy is deleted after 7 days, with run history capped at 20 entries (three shortfalls).
- Subagent run loop (in-memory messages)
- Dream ephemeral run
- Tool-result offload retention
- max_tool_result_chars default
- Trigger run-history cap
› How it could pass
Suggestions. Any change that meets the test counts.
- Persist every subagent and Dream tool call with arguments
- Alert the owner, or block the action, when recording fails
- Stop pruning offloaded tool outputs and run history without owner instruction
Fail - A2Tamper-evident, owner-checkable
Session JSONL is rewritten in full on each save, and sessions, run records and usage data carry no hashes, signatures or chain, so the record can be changed without trace.
› How it could pass
Suggestions. Any change that meets the test counts.
- Hash-chain or sign action-record entries and publish an open verifier
- Anchor checkpoints with the owner or an independent witness
Fail - A3Agent cannot rewrite its record
By default (restrictToWorkspace false, no exec sandbox, full access mode, shell guard skipped), the agent's shell and file tools can edit or delete its session JSONL, usage database and logs. The documented bwrap/seatbelt sandbox masks ~/.nanobot from the agent, but cron/trigger run records and tool-result offloads stay in the writable workspace, and Windows has no sandbox.
- restrict_to_workspace default
- Shell guard only in restricted mode
- bwrap masking
- Cron store paths
- Security docs
› How it could pass
Suggestions. Any change that meets the test counts.
- Write the action record from a component the agent's tools cannot reach, outside the workspace
- Make that protection the default on every platform
Partial
Verifiable · Can you prove what it runs, who it is, and what changed?
50%- V1Obtainable source, matching build
All components are open source: the core is MIT, the WebUI and TUI source ship in the repo, and the Bun runtime is LGPL with a source offer. The default PyPI wheels (with a prebuilt ~119 MB TUI binary and WebUI bundle) are uploaded by hand, with no attestations, provenance or reproducible-build claim, so the distributed build cannot be tied to source independently.
- LICENSE
- Release process (no automatic PyPI publication)
- TUI wheel bundler
- TUI source offer
- PyPI release metadata
› How it could pass
Suggestions. Any change that meets the test counts.
- Publish via PyPI Trusted Publishing with PEP 740 attestations or SLSA provenance, for wheels and TUI archives
- Make the WebUI and TUI builds reproducible and document how an owner verifies them
Partial - V2Owner-rooted identity
nanobot has no agent-level identity (no keypair, DID or agent card). The agent acts through owner-configured channel accounts, such as an email sender on the owner's domain, which HKUDS cannot revoke or impersonate, so attribution is per channel only.
› How it could pass
Suggestions. Any change that meets the test counts.
- Add an owner-held agent keypair or DID that signs outbound actions across channels
- Document how recipients verify it and how the owner rotates it
Partial - V3Change history and rollback
A dulwich git repo versions SOUL.md, USER.md and MEMORY.md, and /dream-restore reverts a Dream commit. All commits are attributed to Dream, owner or agent edits between Dream runs are folded into the next Dream commit, and config, skills, AGENTS.md, cron, the model and the runtime version have no history or rollback (agent self-changes via the my tool are only a log line).
› How it could pass
Suggestions. Any change that meets the test counts.
- Version all agent state, including config, skills, instructions and cron
- Attribute each change to its true actor
- Offer one-step restore to any earlier state
Partial
Modifiable · Can you change anything, and can only you change it?
60%- M1Instructions and logic
SOUL.md, USER.md, AGENTS.md, the Dream and evaluator prompts and built-in skills can be overridden or disabled from the workspace, and updates never overwrite owner files. The core system-prompt templates (identity, platform policy, tool contract and others) load only from the installed package, with no documented override.
› How it could pass
Suggestions. Any change that meets the test counts.
- Add a workspace override for every agent template, including the base prompt, that survives upgrades
Partial - M2Memory
SOUL.md, USER.md and memory/MEMORY.md are plain, editable Markdown. The cross-session memory/history.jsonl archive is documented as append-only, with no supported item-level edit or delete, and past sessions the agent can search and read can only be deleted as whole topics.
› How it could pass
Suggestions. Any change that meets the test counts.
- Provide item-level view, edit and delete for history.jsonl and reusable session transcripts
- Purge deleted memory from the git history, or document how
Partial - M3Tools
Owner tools can be added without review through MCP (with enabledTools filters), entry-point plugins, skills and CLI apps, and exec, file, web, my, CLI Apps and image generation each have an enable switch. Built-ins such as message, spawn, cron, goals and the session tools have no switch and register whenever their services exist.
› How it could pass
Suggestions. Any change that meets the test counts.
- Add a documented per-tool disable list covering every built-in
Partial - M4Owner has the final say
Sandbox, workspace restriction, SSRF guard (via ssrfWhitelist), exec timeout, rate limit and the exec deny list (via allowPatterns) are all owner-configurable. The http/https-only URL restriction for web_fetch and MCP HTTP, and the refusal to bind the WebUI to 0.0.0.0 without auth, have no owner override.
› How it could pass
Suggestions. Any change that meets the test counts.
- Make the remaining hard-coded protections overridable with a warning
- Document allowPatterns and the built-in deny list in the configuration reference
Partial - M5No unilateral change
There is no auto-update, remote flag or server-side setting. The PyPI version check runs only when the owner clicks it and installs nothing, and model, instruction and runtime changes happen only when the owner upgrades or edits config.json, so a self-hosted install stays pinned indefinitely.
Pass
Controllable · Is your word final?
33%- C1Boundaries
Instruction sources are limited per sender (allowFrom plus pairing), and spend is met under G11 (no payment capability). Contact and network egress are on/off only (no per-recipient or per-domain lists). By default the shell guard is skipped and the agent's file and shell tools can rewrite ~/.nanobot/config.json and pairing.json, changing its own permissions (two shortfalls, and limits can be overridden by model output).
- Channel sender check
- Message tool
- restrict_to_workspace default
- Shell guard only in restricted mode
- Network block list
- my tool protected keys
› How it could pass
Suggestions. Any change that meets the test counts.
- Add per-recipient and per-domain allow/deny lists for messaging, email and web egress
- Put config, pairing and the settings API out of reach of agent tools in every mode
- Enforce owner shell allow/deny patterns in full-access mode too
Fail - C2Approval gates
nanobot v0.3.5 has no mechanism for the owner to require approval before a tool call or action class runs. Access modes are scope limits, and approval exists only for pairing new senders.
› How it could pass
Suggestions. Any change that meets the test counts.
- Let the owner gate any tool or action class with approval shown at the time
- Make every path, including cron, heartbeat and subagents, honour the gate
Fail - C3Immediate halt
`nanobot gateway stop` terminates the single local process that runs turns, subagents, cron, heartbeat and shell sessions (SIGTERM, then SIGKILL after a 20 s default). Interrupted turns come back as awaiting_user rather than replaying. In-chat /stop covers one session only, and completion within seconds needs a hands-on check.
Pass - C4Data serves only the owner
nanobot sends no telemetry, and HKUDS receives no owner content. By default web search queries go to DuckDuckGo and every fetched URL goes to Jina Reader (useJinaReader true), third-party destinations the owner did not specifically choose, disclosed only in the configuration reference. Both can be switched off, and no data terms are published.
› How it could pass
Suggestions. Any change that meets the test counts.
- Make third-party search and reader services opt-in
- Publish a list of every outbound endpoint and binding data terms
Partial - C5Credential custody
All credentials are local, in plaintext config.json and OAuth token files, and HKUDS holds none. Each can be removed through `nanobot provider logout`, WebUI settings or MCP server removal. There is no single list of credentials (Codex and Copilot tokens sit in platform data folders outside ~/.nanobot), and after Codex logout the oauth-cli-kit dependency re-imports ~/.codex/auth.json if present.
› How it could pass
Suggestions. Any change that meets the test counts.
- List every stored credential in one place with a revoke control for each
- Stop re-importing Codex CLI tokens after logout
Partial - C6Full deletion
No uninstall-and-purge command or documented delete-all procedure exists; `gateway uninstall-service` removes only the service unit. Agent data spans ~/.nanobot, OAuth token folders outside it and the installed package environment, and nothing confirms removal.
› How it could pass
Suggestions. Any change that meets the test counts.
- Ship a `nanobot uninstall --purge` (or documented script) that removes all data, tokens, services and the package, and confirms completion
Fail
How this assessment was produced
One run, in a fresh container with stock Claude Code and no custom instructions, memory, hooks or plugins. The evaluator received only the prompt below.
Load the AFH sovereignty skill at https://agentsforhumanity.dev/skill.md and follow it to write a desk review of nanobot (https://github.com/HKUDS/nanobot).
During the run the skill was read from a local copy of this site with identical content; only the host in its URLs differed. The transcript records the exact prompt.
- Model
- claude-opus-5-5
- Harness
- Claude Code 2.1.285
- Ran
- 2026-09-30 15:38:07 UTC · 11 turns
- Criteria hash
- a9a46c32d9065380a3eeb09185cedd4734856fdc63b7ca509a15a1bbede75c6c
- Full transcript
- Download (JSONL, gzip) · every tool call, page read and reply
- Run protocol
- 2026-09-30-v1
Every agent evaluated once, each in its own fresh Podman container running stock Claude Code with Claude Opus 5.5. The container has no CLAUDE.md, AGENTS.md, memory, settings, hooks, plugins or MCP servers. The evaluator received only the one-line prompt and nothing else, and no published scores were available to compare against during the run.
- Human review
- pending
Load the AOH sovereignty skill at https://agentsofhumanity.ai/skill.md and follow it in verify mode for https://agentsofhumanity.ai/evaluations/nanobot.json.
Or grade it from scratch. See Check any score yourself.
Vendors and the public can dispute any finding with evidence. Disputes and their resolutions are published.