- From NanoClaw v1: run
bash migrate-v2.shin the v2 checkout. It copies your state deterministically, then hands off to Claude to finish the judgment calls. - From OpenClaw: run the
/migrate-from-openclawskill in Claude Code. It’s a guided conversation, not a batch script — see Migrating from OpenClaw.
There’s also a
/migrate-nanoclaw skill in the repo. That one is for upgrading a customized fork to a newer upstream — it extracts your customizations into a guide and replays them on a clean base instead of git merge. It’s not a v1 → v2 or OpenClaw path; use it later, once you’re on v2 and tracking upstream.What carries over — and what doesn’t
The migration never modifies your v1 install. It reads from it (the v1 checkout is treated as read-only throughout) and writes into the v2 checkout.| v1 state | What happens in v2 |
|---|---|
.env keys | Merged into v2’s .env. Existing v2 keys are never overwritten. |
| Registered groups | Seeded into the central database (data/v2.db) as agent groups, messaging groups, and the wiring between them. |
| Group folders | Copied into groups/. Each v1 CLAUDE.md becomes CLAUDE.local.md — v2 composes CLAUDE.md at container spawn, so your customizations move to the local file. v1 container_config becomes container.json (or a .v1-container-config.json sidecar if it can’t be parsed). |
| Conversations | Session state and Claude Code transcripts are copied, and the v1 session ID is written as the continuation — your agent resumes the exact same conversation in v2. |
| Scheduled tasks | Active tasks are ported into v2’s task model (task rows in each session’s inbound database). |
| Channel auth | Credentials for the channels you select — both env keys and on-disk auth state like the WhatsApp keystore — are copied, and the channel code is installed. |
| Container skills | Skills under container/skills/ that v2 doesn’t already ship are copied over. |
- Message history. The script reads v1’s
store/messages.dbto discover groups and tasks, but never copies the messages themselves — v2 has no equivalent table, and your agent’s memory of past conversations carries over through the session transcripts instead. - v1 source customizations. v2’s architecture is fundamentally different; forked
src/code isn’t portable. The post-migration skill helps you copy portable pieces (skills, docs) and stashes the rest as reference. - Your owner account and access policy. The script doesn’t seed users or roles — that needs human confirmation, so the
/migrate-from-v1skill handles it after the handoff.
Run the migration
The script is idempotent — re-running it skips what’s already done, so a failed step never forces you to start over.Get a v2 checkout next to v1
Clone v2 as a sibling of your v1 directory — the script auto-detects v1 by scanning sibling directories for a If v1 lives somewhere else, point at it explicitly:
store/messages.db:Run the script in a real terminal
Phase 0: preflight
Installs prerequisites (Node, pnpm, dependencies) via the standard bootstrap, locates your v1 install, and validates its database — it aborts if the
registered_groups table is missing. Then it reports what it found:Phase 1: core state
Five sub-steps, each logged separately under
logs/migrate-steps/: merge .env, seed the v2 database, copy group folders, copy session data, port scheduled tasks. Steps that exit successfully but skip some rows surface their errors inline so partial migrations don’t go unnoticed.Phase 2: channels
An interactive multiselect asks which channels to bring over (Telegram, Discord, Slack, WhatsApp, Teams, Matrix, iMessage, and more). For each selection the script copies the channel’s auth state and runs its installer. To skip the prompt — for unattended runs — set
NANOCLAW_CHANNELS=telegram,discord before running.Phase 3: infrastructure
Installs Docker if missing, sets up the OneCLI credential vault, registers your Anthropic credential (skipped if
.env already has ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN), copies v1 container skills v2 doesn’t have, and builds the agent container image.Service switchover
If your v1 service is running, the script offers to switch: it stops v1, installs and starts the v2 service, and asks you to send a real test message to your bot. Then you choose — keep v2 (v1 is disabled but its service file is kept on disk for rollback) or revert (v2 stops, v1 restarts). Switching is non-destructive either way; rolling back later is one command:The v2 unit name is install-specific (
nanoclaw-v2-<slug> / com.nanoclaw-v2-<slug>) — the script prints it in the summary.Phase 4: handoff
The script writes
logs/setup-migration/handoff.json — per-step results, overall status, and a follow-up list — prints a summary of what was done and what still needs a human, then launches Claude with the /migrate-from-v1 skill automatically (if the claude CLI is installed; otherwise run it yourself).After the handoff
/migrate-from-v1 picks up where the script stopped — the parts that need human judgment. It reads handoff.json first, then works through:
- Get v2 routing real messages. Fixes any failed steps that block message routing, then has you send a test message before going deeper. v1 stays paused, not modified.
- Owner and access. v2 auto-creates a
usersrow for every sender it sees; the skill confirms which one is you and grants theownerrole. Then it asks how open the bot should be — the script left every group onpublicso the switchover test would work, and the skill offers to tighten it tostrict(known users only) orrequest_approval, seeding known senders from your v1 message history if you want. - Clean up
CLAUDE.local.md. The migration copied your entire v1CLAUDE.mdper group, including v1 boilerplate that v2 now provides through composed fragments. The skill diffs each file against the v1 template, strips the stock sections, keeps your customizations and agent identity, and shows you the result before writing. - Container config. Verifies that
container.jsonmount paths still exist on this machine, and resolves any.v1-container-config.jsonsidecars left by unparseable v1 configs. - Fork customizations. If your v1 was a customized fork, the skill walks the commits with you — portable items (container skills, Claude skills, docs) get copied; v1 source changes get stashed to
docs/v1-fork-reference/since they don’t translate.
setup/index.ts --step verify) and restarts the service.
Starting over
migrate-v2-reset.sh wipes the v2 checkout back to clean so you can re-run the migration from scratch:
data/, logs/, the merged .env, group folders copied from v1, and untracked skills and channel code added during migration, then restores the tracked versions of those paths from git. It keeps node_modules/ (expensive to reinstall) and never touches your v1 install.
Migrating from OpenClaw
OpenClaw users skip the script entirely — there’s no deterministic mapping between the two systems, so the migration is a conversation. From the v2 checkout, open Claude Code and run:~/.openclaw, ~/.clawdbot, or a path you give it), summarizes what it found, and then works through each area with you — reading your data, explaining where it belongs in v2, and showing every change before applying it. Credentials are always masked when displayed.
What maps where:
| OpenClaw | NanoClaw v2 |
|---|---|
| An agent | An agent group (its own container, workspace, and memory) |
| A chat or group | A messaging group, wired to an agent group |
IDENTITY.md / SOUL.md | container/CLAUDE.md (shared across agents) or a group’s CLAUDE.local.md, your choice |
USER.md, MEMORY.md, daily memory files | user-context.md, memories.md, and daily-memories/ in the group folder |
| Skills | Copied into container/skills/ — the SKILL.md format is shared, so they’re portable |
| Channel tokens (Telegram, Discord, Slack) | .env — the host process reads them |
| Anthropic and other API keys | The OneCLI vault — injected per request, never in container env |
| Cron jobs | v2 scheduled tasks, created through the agent’s schedule_task tool |
| MCP servers | Per-agent-group container config |
allowFrom / dmPolicy allowlists | unknown_sender_policy per messaging group, plus user roles and group members |
- WhatsApp re-authenticates from scratch. Stale Baileys encryption sessions break decryption, so the skill deliberately doesn’t copy WhatsApp auth state — you scan a QR code again during setup.
- Some features don’t map. Exec approvals are replaced by container isolation; webhooks, human delay, and TTS have no v2 primitive — the skill discusses alternatives case by case.
- Progress is recoverable. The skill keeps a
migration-state.mdfile in the project root as its source of truth, so a lost session picks up where it left off.
/setup to finish the install — select the channels you migrated when setup asks.
Next steps
Connect more channels
Anything you didn’t migrate installs any time with
/add-<name>.The entity model
Agent groups, messaging groups, users, and roles — the v2 model your v1 state was mapped onto.
The ncl CLI
Inspect the migrated groups, sessions, and users from the command line.
Troubleshooting
Migration logs live in
logs/migrate-v2.log and logs/migrate-steps/.