Moshe Krupper 94da3542f0 feat: guard seam — decision function, registration wrapping, grant-carrying replay (guarded-actions phase 2)
Every privileged action crossing the container or channel boundary now
passes one decision function before it executes (hub:
engineering/discovery/guarded-actions-decisions +
engineering/requirements/guarded-actions, phase 2).

- src/guard/ — domain-free leaf: guard({action, actor, resource?, payload,
  grant?}) → allow | hold(eligibility, scope, reason) | deny(reason).
  Registration-derived action catalog; tighten-only rule sources composed
  with the structural baseline under the strictest-wins lattice
  (deny > hold > allow; hold∧hold intersects approver eligibility, empty
  intersections escalate to the global chain). Fails closed.
- Registration wrapping on all four registries: every ncl command derives a
  catalog entry in register() (dispatch consults the guard; auto-fill,
  the sessions-get oracle and post-handler row filtering stay as mechanics);
  registerDeliveryAction wraps create_agent + self-mod with domain guard
  specs (precheck / hold builder / deny notify — the wrapped path is the
  only path); registerResponseHandler and registerMessageInterceptor wrap
  the channel-registration click and free-text name capture (the D4
  interceptor half — the reply is re-checked against the same eligibility).
- Structural baselines moved verbatim into module-edge guard adapters:
  cli_scope enforcement (src/cli/guard.ts), create_agent's scope branch
  (a2a guard.ts — out of create-agent.ts), a2a self-send/destination/target
  checks, self-mod unconditional hold, unknown_sender_policy, host as
  trusted caller in code. agent_message_policies becomes the guard's first
  rule source; the ghost-policy edge (policy row, no destination) still
  DENIES — deny beats hold, exactly today's outcome.
- Grant-carrying replay: approved: true is deleted. Approval handlers
  receive the verified approval row (ApprovalHandlerContext.approval) and
  re-enter their entry points with it as the grant; the guard treats a live,
  matching grant as hold-satisfied but re-runs the structural baseline.
  Absorbed defect fixes (intentional decision-outcome changes):
  D2 — a malformed replay caller context refuses the replay, never falls
  back to {caller: 'host'}; D3 — the a2a approve handler re-enters the
  guarded route, so approve-then-revoke no longer delivers.
- Conformance test walks the command + delivery-action registries: every
  mutating entry must map to a guard catalog entry (scheduling self-actions
  and the cli_request bridge are the declared exemption class).

Zero rules ship; the seeded posture is today's behavior. No other decision
outcomes change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 21:29:29 +03:00
2026-05-08 15:56:09 +03:00
2026-07-04 23:36:05 +03:00
2026-07-04 16:58:45 +00:00
2026-06-18 11:21:50 +09:00
2026-07-04 19:50:43 +03:00

NanoClaw

An AI assistant that runs agents securely in their own containers. Lightweight, built to be easily understood and completely customized for your needs.

nanoclaw.dev  •   docs  •   中文  •   日本語  •   한국어  •   Discord  •   repo tokens


Why I Built NanoClaw

OpenClaw is an impressive project, but I wouldn't have been able to sleep if I had given complex software I didn't understand full access to my life. OpenClaw has nearly half a million lines of code, 53 config files, and 70+ dependencies. Its security is at the application level (allowlists, pairing codes) rather than true OS-level isolation. Everything runs in one Node process with shared memory.

NanoClaw provides that same core functionality, but in a codebase small enough to understand: one process and a handful of files. Claude agents run in their own Linux containers with filesystem isolation, not merely behind permission checks.

Quick Start

git clone https://github.com/nanocoai/nanoclaw.git nanoclaw-v2
cd nanoclaw-v2
bash nanoclaw.sh

nanoclaw.sh walks you from a fresh machine to a named agent you can message. It installs Node, pnpm, and Docker if missing, registers your Anthropic credential with OneCLI, builds the agent container, and pairs your first channel (Telegram, Discord, WhatsApp, or a local CLI). If a step fails, Claude Code is invoked automatically to diagnose and resume from where it broke.

Migrating from NanoClaw v1?

Run from a fresh v2 checkout next to your v1 install:

git clone https://github.com/nanocoai/nanoclaw.git nanoclaw-v2
cd nanoclaw-v2
bash migrate-v2.sh

migrate-v2.sh finds your v1 install (sibling directory, or NANOCLAW_V1_PATH=/path/to/nanoclaw), migrates state into the v2 checkout, then execs into Claude Code to finish the parts that need judgment (owner seeding, CLAUDE.local.md cleanup, fork-customisation replay).

Run the script directly, not from inside a Claude session — the deterministic side needs interactive prompts and real shell I/O for Node/pnpm bootstrap, Docker, OneCLI, and the container build.

What it does: merges .env, seeds the v2 DB from registered_groups, copies group folders + session data + scheduled tasks, installs the channel adapters you select, copies channel auth state (including Baileys keystore + LID mappings for WhatsApp), builds the agent container.

What it doesn't: flip the system service. Pick "switch to v2" at the prompt, or do it manually after testing — your v1 install is left untouched.

See docs/v1-to-v2-changes.md for what's different and docs/migration-dev.md for development notes.

Philosophy

Small enough to understand. One process, a few source files and no microservices. If you want to understand the full NanoClaw codebase, just ask Claude Code to walk you through it.

Secure by isolation. Agents run in Linux containers and they can only see what's explicitly mounted. Bash access is safe because commands run inside the container, not on your host.

Built for the individual user. NanoClaw isn't a monolithic framework; it's software that fits each user's exact needs. Instead of becoming bloatware, NanoClaw is designed to be bespoke. You make your own fork and have Claude Code modify it to match your needs.

Customization = code changes. No configuration sprawl. Want different behavior? Modify the code. The codebase is small enough that it's safe to make changes.

AI-native, hybrid by design. The install and onboarding flow is an optimized scripted path, fast and deterministic. When a step needs judgment, whether a failed install, a guided decision, or a customization, control hands off to Claude Code seamlessly. Beyond setup there's no monitoring dashboard or debugging UI either: describe the problem in chat and Claude Code handles it.

Skills over features. Trunk ships the registry and infrastructure, not specific channel adapters or alternative agent providers. Channels (Discord, Slack, Telegram, WhatsApp, …) live on a long-lived channels branch; alternative providers (OpenCode, Ollama) live on providers. You run /add-telegram, /add-opencode, etc. and the skill copies exactly the module(s) you need into your fork. No feature you didn't ask for.

Best harness, best model. NanoClaw natively uses Claude Code via Anthropic's official Claude Agent SDK, so you get the latest Claude models and Claude Code's full toolset, including the ability to modify and expand your own NanoClaw fork. Other providers are drop-in options: /add-codex for OpenAI's Codex (ChatGPT subscription or API key), /add-opencode for OpenRouter, Google, DeepSeek and more via OpenCode, and /add-ollama-provider for local open-weight models. Provider is configurable per agent group.

What It Supports

  • Multi-channel messaging — WhatsApp, Telegram, Discord, Slack, Microsoft Teams, iMessage, Matrix, Google Chat, Webex, Linear, GitHub, WeChat, and email via Resend. Installed on demand with /add-<channel> skills. Run one or many at the same time.
  • Flexible isolation — connect each channel to its own agent for full privacy, share one agent across many channels for unified memory with separate conversations, or fold multiple channels into a single shared session so one conversation spans many surfaces. Pick per channel via /manage-channels. See docs/isolation-model.md.
  • Per-agent workspace — each agent group has its own CLAUDE.md, its own memory, its own container, and only the mounts you allow. Nothing crosses the boundary unless you wire it to.
  • Scheduled tasks — recurring jobs that run Claude and can message you back
  • Web access — search and fetch content from the web
  • Container isolation — agents are sandboxed in Docker (macOS/Linux/WSL2), with optional Docker Sandboxes micro-VM isolation
  • Credential security — agents never hold raw API keys. Outbound requests route through OneCLI's Agent Vault, which injects credentials at request time and enforces per-agent policies and rate limits.
  • Agent templates: stamp a ready-to-run agent (instructions + MCP tools + skills, no secrets) from a reusable bundle, via the setup wizard or ncl groups create --template <ref>. Load from the public library, a local folder, or any git repo. See docs/templates.md.

Usage

Talk to your assistant with the trigger word (default: @Andy):

@Andy send an overview of the sales pipeline every weekday morning at 9am (has access to my Obsidian vault folder)
@Andy review the git history for the past week each Friday and update the README if there's drift
@Andy every Monday at 8am, compile news on AI developments from Hacker News and TechCrunch and message me a briefing

From a channel you own or administer, you can manage groups and tasks:

@Andy list all scheduled tasks across groups
@Andy pause the Monday briefing task
@Andy join the Family Chat group

Customizing

NanoClaw doesn't use configuration files. To make changes, just tell Claude Code what you want:

  • "Change the trigger word to @Bob"
  • "Remember in the future to make responses shorter and more direct"
  • "Add a custom greeting when I say good morning"
  • "Store conversation summaries weekly"

Or run /customize for guided changes.

The codebase is small enough that Claude can safely modify it.

Contributing

Don't add features. Add skills.

If you want to add a new channel or agent provider, don't add it to trunk. New channel adapters land on the channels branch; new agent providers land on providers. Users install them in their own fork with /add-<name> skills, which copy the relevant module(s) into the standard paths, wire the registration, and pin dependencies.

This keeps trunk as pure registry and infra, and every fork stays lean — users get the channels and providers they asked for and nothing else.

RFS (Request for Skills)

Skills we'd like to see:

Communication Channels

  • /add-signal — Add Signal as a channel

Requirements

  • macOS or Linux (Windows via WSL2)
  • Node.js 20+ and pnpm 10+ (the installer will install both if missing)
  • Docker Desktop (macOS/Windows) or Docker Engine (Linux)
  • Claude Code for /customize, /debug, error recovery during setup, and all /add-<channel> skills

Architecture

messaging apps → host process (router) → inbound.db → container (Bun, Claude Agent SDK) → outbound.db → host process (delivery) → messaging apps

A single Node host orchestrates per-session agent containers. When a message arrives, the host routes it via the entity model (user → messaging group → agent group → session), writes it to the session's inbound.db, and wakes the container. The agent-runner inside the container polls inbound.db, runs Claude, and writes responses to outbound.db. The host polls outbound.db and delivers back through the channel adapter.

Two SQLite files per session, each with exactly one writer — no cross-mount contention, no IPC, no stdin piping. Channels and alternative providers self-register at startup; trunk ships the registry and the Chat SDK bridge, while the adapters themselves are skill-installed per fork.

For the full architecture writeup see docs/architecture.md; for the three-level isolation model see docs/isolation-model.md.

Key files:

  • src/index.ts — entry point: DB init, channel adapters, delivery polls, sweep
  • src/router.ts — inbound routing: messaging group → agent group → session → inbound.db
  • src/delivery.ts — polls outbound.db, delivers via adapter, handles system actions
  • src/host-sweep.ts — 60s sweep: stale detection, due-message wake, recurrence
  • src/session-manager.ts — resolves sessions, opens inbound.db / outbound.db
  • src/container-runner.ts — spawns per-agent-group containers, OneCLI credential injection
  • src/db/ — central DB (users, roles, agent groups, messaging groups, wiring, migrations)
  • src/channels/ — channel adapter infra (adapters installed via /add-<channel> skills)
  • src/providers/ — host-side provider config (claude baked in; others via skills)
  • container/agent-runner/ — Bun agent-runner: poll loop, MCP tools, provider abstraction
  • groups/<folder>/ — per-agent-group filesystem (CLAUDE.md, skills, container config)

FAQ

Why Docker?

Docker provides cross-platform support (macOS, Linux and Windows via WSL2) and a mature ecosystem. For additional isolation, Docker Sandboxes run each container inside a micro VM.

Can I run this on Linux or Windows?

Yes. Docker is the default runtime and works on macOS, Linux, and Windows (via WSL2). Just run bash nanoclaw.sh.

Is this secure?

Agents run in containers, not behind application-level permission checks. They can only access explicitly mounted directories. Credentials never enter the container — outbound API requests route through OneCLI's Agent Vault, which injects authentication at the proxy level and supports rate limits and access policies. You should still review what you're running, but the codebase is small enough that you actually can. See the security documentation for the full security model.

Why no configuration files?

We don't want configuration sprawl. Every user should customize NanoClaw so that the code does exactly what they want, rather than configuring a generic system. If you prefer having config files, you can tell Claude to add them.

Can I use third-party or open-source models?

Yes. The supported path is /add-opencode (OpenRouter, OpenAI, Google, DeepSeek, and more via OpenCode config) or /add-ollama-provider (local open-weight models via Ollama). Both are configurable per agent group, so different agents can run on different backends in the same install.

For one-off experiments, any Claude API-compatible endpoint also works via .env:

ANTHROPIC_BASE_URL=https://your-api-endpoint.com
ANTHROPIC_AUTH_TOKEN=your-token-here

How do I debug issues?

Ask Claude Code. "Why isn't the scheduler running?" "What's in the recent logs?" "Why did this message not get a response?" That's the AI-native approach that underlies NanoClaw.

Why isn't the setup working for me?

If a step fails, nanoclaw.sh hands off to Claude Code to diagnose and resume. If that doesn't resolve it, run claude, then /debug. If Claude identifies an issue likely to affect other users, open a PR against the relevant setup step or skill.

How do I uninstall NanoClaw?

bash nanoclaw.sh --uninstall

Every install is tagged with a per-checkout id, so the uninstaller removes only what belongs to that copy: the background service, containers and image, app data and logs, your agents' files, and this copy's OneCLI vault agents. Shared things — the OneCLI app and your credentials, other NanoClaw copies on the machine — are left alone. It shows exactly what it found and asks for confirmation per group; nothing is deleted until you say yes. Use --dry-run to preview without changing anything, or --yes to skip the prompts. Your .env is backed up before removal. To finish, delete the checkout folder itself.

What changes will be accepted into the codebase?

Only security fixes, bug fixes, and clear improvements will be accepted to the base configuration. That's all.

Everything else (new capabilities, OS compatibility, hardware support, enhancements) should be contributed as skills: channel and provider code on the channels/providers registry branches, everything else as a self-contained skill. See docs/customizing.md and CONTRIBUTING.md.

This keeps the base system minimal and lets every user customize their installation without inheriting features they don't want.

Community

Questions? Ideas? Join the Discord.

Changelog

See CHANGELOG.md for breaking changes, or the full release history on the documentation site.

License

MIT

S
Description
Languages
TypeScript 87.8%
Shell 6.2%
JavaScript 4.7%
CSS 0.8%
Dockerfile 0.2%
Other 0.2%