Nanobot is a lightweight, Python-based local AI assistant framework inspired by OpenClaw. It initializes from a JSON configuration file (default: ~/.nanobot/config.json) and sets up a dedicated workspace (~/.nanobot/workspace).
On startup, Nanobot initializes several core subsystems:
It also populates the workspace with bootstrap files:
AGENTS.mdSOUL.mdUSER.mdHEARTBEAT.mdmemory/These define agent identity, behavioral constraints, and long-term memory.
Incoming messages flow through:
User → Channel → MessageBus → AgentLoop → LLM → Tools → Response
The AgentLoop constructs a full execution context consisting of:
memory/*.md)sessions/*.jsonl)The LLM is invoked with this context. If tool calls are generated (e.g., shell, web search), they are executed and the results are fed back into the loop until completion.
Nanobot enforces runtime safety through configuration:
tools.restrictToWorkspace=true
→ confines all filesystem and shell operations
Channel access control via allowFrom whitelist
Secrets stored in config.json with strict permissions
Sessions stored locally under:
workspace/sessions/*.jsonl
Default config path:
~/.nanobot/config.json
Override via CLI:
--config <path>
Optional interactive setup:
nanobot --wizard
If the workspace does not exist:
~/.nanobot/workspace/
Nanobot creates it and syncs template files:
AGENTS.md, SOUL.md)USER.md)memory/)Key components instantiated:
MessageBus()SessionManager(workspace)CronService(jobs.json)ChannelManager(config, bus)HeartbeatService (optional)agent = AgentLoop(
bus=bus,
provider=provider,
workspace=config.workspace_path,
model=config.agents.defaults.model,
max_iterations=config.agents.defaults.max_tool_iterations,
exec_config=config.tools.exec,
web_search_config=config.tools.web.search,
cron_service=cron,
restrict_to_workspace=config.tools.restrict_to_workspace,
session_manager=session_manager,
mcp_servers=config.tools.mcp_servers,
channels_config=config.channels
)
CLI Mode
agent.run()
Gateway Mode
Starts:
InboundMessageEach session is processed sequentially:
session_key = "cli:direct" | "slack:<id>"
Commands like:
/help
/tool
are executed immediately without LLM invocation.
Context includes:
Before execution:
Old messages may be summarized
Stored in:
memory/HISTORY.md
memory/MEMORY.md
Loop runs until:
Saved to session:
workspace/sessions/*.jsonl
Returned via MessageBus → Channel → User
Any exception results in:
"Sorry, I encountered an error."
System logs the full stack trace.
config = load_config(config_path)
workspace = get_workspace_path(config.workspace_path)
workspace.mkdir(parents=True, exist_ok=True)
sync_workspace_templates(workspace)
messages = context.build_messages(
history=session.get_history(),
current_message=msg.content
)
final_content = run_agent_loop(messages)
try:
response = process_message(msg)
except Exception:
logger.exception(...)
return "Sorry, I encountered an error."
User: What is 2+2?
Bot: 4
User: List files
Bot: (exec → ls)
Bot: file1.txt, file2.txt
Session log includes:
User: My favorite color is blue
Bot: Noted
User: What is my favorite color?
Bot: Blue
Memory stored in:
memory/MEMORY.md
Python asyncio concurrency
https://docs.python.org/3/library/asyncio.html
JSON Lines format
https://jsonlines.org/
LLM tool-calling patterns
https://platform.openai.com/docs/guides/function-calling
Event-driven architecture
https://martinfowler.com/articles/201701-event-driven.html