Skip to main content

arc-to-zen-migration

Use when migrating Arc browser spaces/workspaces into Zen Browser, or bulk-creating Zen workspaces, folders, essentials, or pinned tabs by editing the session store. Covers reading/writing Zen's mozlz4 session file and its folder/group/space schema.

Source facts

Repository
shub-rajput/arc-to-zen-migration
Last source activity
June 9, 2026 at 05:13
Detected SKILL.md language
English
Stars
1
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
arc-to-zen-migration
description
Use when migrating Arc browser spaces/workspaces into Zen Browser, or bulk-creating Zen workspaces, folders, essentials, or pinned tabs by editing the session store. Covers reading/writing Zen's mozlz4 session file and its folder/group/space schema.
# Arc → Zen Migration ## Overview Zen has no Arc importer. But Zen (Firefox-based) keeps its whole tab/folder/workspace state in one compressed file you can script against, so Arc spaces can be rebuilt in bulk — folders become folders, not flat pins. **Read side (Arc):** `~/Library/Application Support/Arc/StorableSidebar.json` — plain JSON holding every space, folder, and pinned tab. No manual pasting needed. **Write side (Zen):** `<profile>/zen-sessions.jsonlz4` — Mozilla-lz4 (`mozLz40\0`) compressed JSON. **Tools:** - `scripts/arc_extract.py` — reads Arc's sidebar, returns each space as a nested tree. - `scripts/zen_session.py` — read/write Zen's mozlz4 session + folder/tab/essential builders. - `scripts/migrate.py` — end-to-end: Arc space → Zen workspace (preserves nested folders). - `scripts/migrate_example.py` — manual fill-in template (when there's no Arc file, e.g. user pastes URLs). ## When to Use - "Migrate my Arc workspaces/spaces to Zen" - "Bulk add folders / pinned tabs / essentials to Zen" - Recreating Arc's folder structure inside a Zen workspace Not for: bookmarks (use Arc export HTML → Zen bookmark import) or passwords (use a password manager / keychain export). ## The Iron Rule **Zen MUST be fully quit (Cmd+Q) before writing.** It rewrites the session on exit and will clobber your changes otherwise. **Always `backup()` first** — a wrong schema corrupts the session; the `.bak` restores instantly. ## Workflow (automatic — Arc installed) 1. **Quit Zen (Cmd+Q)** — non-negotiable (see Iron Rule). 2. **Preview:** `python3 scripts/migrate.py --list` prints Arc spaces ↔ Zen workspaces. 3. **Migrate everything from zero:** `python3 scripts/migrate.py --all --favorites` — creates a Zen workspace per Arc space (with Arc's emoji icon), imports folders + pins, and adds Arc's global Favorites as global essentials. 4. **Or one space:** `python3 scripts/migrate.py "Home" --favorites` (creates the workspace if missing; `--zen "Name"` maps to a differently-named one; `--essentials N` promotes the first N top-level pins to essentials). 5. **Reopen Zen, verify.** Re-runnable — `clear_workspace` wipes the target first. Restore the `.bak` if wrong. **Mapping Arc → Zen:** space `list` → folder (nesting preserved) · space top-level tab → regular pin · global Favorites (`topApps`) → global essentials. ## Workflow (manual — no Arc file, user pastes URLs) Use `migrate_example.py`: fill `ESSENTIALS`/`PINS`/`FOLDERS`, quit Zen, run. Screenshots of the Arc sidebar are enough to fill it. **If Zen's schema drifted** (script misbehaves): make ONE folder with 2 tabs by hand in Zen, quit, `zen_session.py <profile> --dump`, compare the live `folders[]`/`groups[]`/`tabs[]` shapes, update the builders. ## Schema (Zen 1.20.x / Firefox 151) A **folder = two synced records** sharing one `id`, plus member tabs: | Where | Key fields | |---|---| | `folders[]` | `id`, `name`, `workspaceId`, `collapsed`, `parentId:null`, `emptyTabIds:[]`, `userIcon` | | `groups[]` | `id` (same), `name`, `color:"zen-workspace-color"`, `collapsed` | | `tabs[]` (member) | `groupId:<folder id>`, `zenWorkspace:<space uuid>`, `pinned:true`, `zenEssential:false` | - **Essential:** `zenEssential:true`. **Scoped by CONTAINER (`userContextId`), not `zenWorkspace`** — this is the #1 gotcha. With `zen.workspaces.separate-essentials=true` (Zen default), an essential shows only in workspaces whose `containerTabId` matches its `userContextId`; `userContextId:0` = the default container = the first/Home workspace. To make essentials **global** (Arc-style, same in every space), set `zen.workspaces.separate-essentials=false` (via `user.js`) and use `userContextId:0` / `zenWorkspace:null`. For a **per-workspace** essential, set `userContextId` to that workspace's `containerTabId`. - **Plain pin:** `zenEssential:false`, `zenWorkspace:<uuid>`, no `groupId`. Scoped by `zenWorkspace` (works with `userContextId:0`). - **Workspace** lives in `spaces[]`: `{uuid, name, icon(emoji), containerTabId, position, theme}`. `ensure_workspace()` creates one if absent. - **Ordering** = tab array order. `tab.index` is the active history-entry pointer (set to 1), NOT position. - IDs are `<epoch_ms>-<counter>`. Tabs need only a minimal `entries:[{url,title,triggeringPrincipal_base64:"{\"3\":{}}"}]`; Firefox fills the rest on load. - Skip `arc://*` URLs — they don't resolve in Zen. ## Library Quickref ```python from zen_session import ZenSession, find_profiles s = ZenSession(find_profiles()[0]) s.backup() home = s.workspace_by_name("Home") s.clear_folders(workspace=home) # idempotent re-runs s.add_essential("https://discord.com/") # global top icon s.add_tab("https://news.com/", workspace=home) # plain pin fid = s.add_folder("Finance", home) s.add_tab("https://sheet/", workspace=home, folder_id=fid) s.save() # Zen must be quit ``` ## Common Mistakes - **Writing while Zen runs** → changes vanish on quit. Quit first. - **Folder in only one array** → ghost/empty folder. Always write BOTH `folders[]` and `groups[]` (the library does). - **Tab missing `zenWorkspace`** → tab lands in the wrong/no space. Set it (essentials use `null`). - **liblz4 not found** → `brew install lz4` (mac) / `apt install liblz4-1` (linux); fix the path list in `zen_session.py` if needed. - **No backup** → re-run with `backup()`; keep the `.bak` until verified.
View on GitHub