A complete Python-based migration tool that converts Arc browser spaces, pinned tabs, and open tabs into Zen browser workspaces with proper tab assignment.
- Python 3.7+
- Arc Browser (with spaces and pinned tabs you want to migrate)
- Zen Browser (installed and run at least once)
- macOS or Windows (current implementation)
- lz4 Python package (for session tab injection):
pip install lz4
- Clone this repository:
git clone https://github.com/rafcabezas/arc2zen.git
cd arc2zen- Install the optional dependency for session tab injection:
pip install lz4Run the complete migration (recommended for first-time users):
# Dry run first to see what will be migrated
python3 migrate_arc_to_zen.py --dry-run
# If everything looks good, run the actual migration
python3 migrate_arc_to_zen.py
# Then inject all tabs into the session file (makes tabs actually appear)
python3 inject_session_tabs.py
# Finally, inject Arc's open (unpinned) tabs (Zen 1.20+ keeps TWO session files)
python3 inject_open_tabs.py --dry-run
python3 inject_open_tabs.pyAfter running the main migration, tabs may only appear as bookmarks or metadata. To make them show up as real browser tabs in Zen's sidebar, run:
# Migrate with custom settings
python3 migrate_arc_to_zen.py --zen-profile "Default" --verboseThis writes directly to zen-sessions.jsonlz4, which is the file Zen actually
reads to render tabs. The script is idempotent — re-running it replaces
previously-injected tabs with a fresh extraction.
For open tabs, zen-sessions.jsonlz4 alone is NOT enough. Zen keeps two
session files in lockstep, matched per-tab by zenSyncId:
zen-sessions.jsonlz4— Zen's workspace tab store (flat list)sessionstore.jsonlz4/sessionstore-backups/recovery.jsonlz4— the regular Firefox session
On startup Zen effectively keeps the intersection: an open tab survives only
if a tab with the same zenSyncId exists in BOTH files. Tabs written to only
one file are silently pruned on the next restart.
inject_open_tabs.py handles this: it appends the same tab object (same
zenSyncId) to both files (plus recovery.jsonlz4), maps Arc spaces to Zen
workspaces by name, infers the container id from the workspace's existing
tabs, dedups against URLs already present in either file, and creates
timestamped backups. Zen must be fully quit while it runs.
python3 inject_open_tabs.py --dry-run # preview what would be injected
python3 inject_open_tabs.py # inject into both session filesArc archives inactive tabs on a timer (12h/24h/7d/30d). restore_auto_archived.py
restores ONLY the tabs Arc closed by itself — archive items in
StorableArchiveItems.json with reason == "auto". Tabs closed manually
(reason == "manual", e.g. Cmd+W) are deliberately ignored. Each tab returns
to its original space (resolved via the archive item's source.space UUID) as
a real open tab, using the same dual-file mechanism as inject_open_tabs.py.
python3 restore_auto_archived.py --dry-run
python3 restore_auto_archived.py# Migrate only a specific Arc space
python3 migrate_arc_to_zen.py --arc-space "Personal"
python3 migrate_arc_to_zen.py --arc-space "Work" --dry-run
# Specify Zen profile
python3 migrate_arc_to_zen.py --zen-profile "Default"
# Also migrate open tabs via sessionstore (legacy approach)
python3 migrate_arc_to_zen.py --open-tabs
# Verbose logging
python3 migrate_arc_to_zen.py --verbose
# See all available options
python3 migrate_arc_to_zen.py --help- Arc Spaces → Zen Workspaces (each Arc space becomes a Zen workspace)
- Space Icons → Workspace Icons (Unicode emojis preserved: 🏠, 🌳, 🎬, ⚖️, etc.)
- Space Colors → Workspace Themes (Arc's subtle color tints accurately reproduced)
- Pinned Tabs → Zen Pinned Tabs (with folder structure preserved)
- Essential Tabs → Essential Pinned Tabs (Arc's top toolbar tabs with large icons)
- Open Tabs → Zen Open Tabs (unpinned tabs restored as real browser tabs)
- Folder Hierarchy → Zen Folder Structure (nested folders maintained)
- Display Order → Zen Sidebar Order (Arc visual ordering preserved)
- Backup Bookmarks → Firefox Bookmarks (additional backup as standard bookmarks)
The tool reads Arc's StorableSidebar.json to extract:
- Space names, structure, and icons (Unicode emojis when available)
- Pinned tabs with URLs and metadata
- Open (unpinned) tabs per space
- Folder hierarchy within each space
- Visual ordering using container childrenIds
The tool helps you map Arc spaces to Zen workspaces:
python3 src/zen_workspace_mapper.pyThis creates a mapping guide showing which Zen workspace UUID corresponds to each Arc space.
For Zen 1.18+, spaces, pinned tabs, and folders are written directly to zen-sessions.jsonlz4 — the modern storage format for Zen's sidebar. For older Zen versions, the tool falls back to the legacy zen_pins and zen_workspaces SQLite tables.
- Read-only Arc access - Your Arc data is never modified
- Automatic backups - Zen database is backed up before any changes
- Dry-run mode - Test migration without making changes
- Validation - Data integrity checks throughout the process
arc2zen/
├── migrate_arc_to_zen.py # Main migration script
├── inject_session_tabs.py # Inject pinned tabs/folders into zen-sessions.jsonlz4
├── inject_open_tabs.py # Inject open tabs into BOTH session files (Zen 1.20+)
├── restore_auto_archived.py # Restore Arc auto-archived tabs (reason=auto)
├── sync_arc_to_zen.py # Optional mirror mode for Zen 1.22 (see Mirror Mode)
├── requirements.txt # Python dependencies (lz4)
├── src/
│ ├── arc_pinned_tab_extractor.py # Extract Arc pinned tabs
│ ├── arc_bookmark_extractor.py # Extract Arc bookmarks
│ ├── arc_profile_discovery.py # Locate Arc profiles
│ ├── zen_sessions_importer.py # Import to zen-sessions.jsonlz4 (Zen 1.18+)
│ ├── zen_sessionstore_manager.py # Read/write mozlz4 sessionstore
│ ├── zen_pinned_tab_importer.py # Legacy: import tabs to zen_pins table
│ ├── zen_workspace_importer.py # Legacy: create zen_workspaces entries
│ ├── zen_space_importer.py # Create Zen containers for spaces
│ ├── zen_bookmark_importer.py # Backup import as Firefox bookmarks
│ ├── zen_workspace_mapper.py # Map Arc spaces to Zen workspaces
│ └── zen_schema_analyzer.py # Analyze Zen database schema
├── .gitignore # Excludes generated files
└── README.md # This file
Before running the full migration, you may want to map your Arc spaces to Zen workspaces:
python3 src/zen_workspace_mapper.pyThis interactive script will:
- Analyze your current Zen workspace structure
- Ask for your Arc space names (or detect them automatically)
- Create a mapping guide at
workspace_uuid_mapping.json - Show you which UUID corresponds to each workspace
You can also run individual components:
# Extract Arc pinned tabs only
python3 src/arc_pinned_tab_extractor.py
# Analyze Zen database schema
python3 src/zen_schema_analyzer.py
# Import pinned tabs to Zen (advanced usage)
python3 src/zen_pinned_tab_importer.py --dry-runA one-command alternative to the steps above, for Zen 1.22 on macOS. What it adds:
- tab favicons from Arc's cache
- one Zen container per Arc profile, with that profile's Favorites as its Essentials. Spaces from your default Arc profile keep the container their matched Zen workspace already uses, or none.
- pinned tabs, folders, Essentials and open tabs in one run (
--no-open-tabsskips open tabs) - safe re-runs: every tab and folder it creates gets a stable id (
arc2zen-<32 hex>, foldersarc2zen-f-<32 hex>), and later runs only replace those. If you changed synced items in Zen since the last sync (moved, renamed, closed or reordered them, or added your own pinned tabs or Essentials in a synced workspace or container), it lists the changes and stops;--overwrite-zen-changesgoes ahead anyway. --extensions: installs the Firefox versions of the Arc extensions it recognizes, from addons.mozilla.org, checked against AMO's checksum. Zen lists them as disabled until you click Enable inabout:addons, and their settings don't carry over.
python3 sync_arc_to_zen.py # dry run: prints the plan, writes nothing
python3 sync_arc_to_zen.py --apply # quit Zen (Cmd+Q) first
python3 sync_arc_to_zen.py --help # all optionsUnlike the other scripts it only writes with --apply. It writes zen-sessions.jsonlz4, containers.json and arc2zen-sync.json (a record of the sync) in the Zen profile, backs each one up next to it first, and prints how to undo the run. With --extensions it also adds <id>.xpi files to the profile's extensions folder (remove those add-ons in about:addons). It never writes places.sqlite, prefs.js or user.js, and refuses --apply while Zen is running.
Good to know:
- An Arc space goes into the Zen workspace named by
--map, else (first sync only) the one with the same name, else a new one. A matched workspace takes the Arc space's icon (its emoji, or its named icon when Zen has one with that name) and, if needed, that Arc profile's container; the plan flags a container change, since the workspace's sites lose that container's logins. Arc's color is only applied to workspaces that don't have one yet. On the first sync Arc's workspaces go first, and your own tabs, folders and Essentials are kept after Arc's (--replace-existingdrops them instead). Workspaces and containers it creates are never removed. - Don't combine it with
migrate_arc_to_zen.pyon the same profile: that script's container cleanup can delete a container named like an Arc space, including one mirror mode created. - Built for Zen 1.22's session format and checked only against it; it warns when the profile was last opened by another Zen version. Zen 1.22 restores open tabs from
zen-sessions.jsonlz4alone whilezen.window-sync.enabledis on (the default; seeZenSessionManager#restoreWindowData), so the dual-file step above isn't needed there. - No history, bookmarks, logins or cookies:
migrate_arc_to_zen.pyhandles history and bookmarks, and you'll sign in to sites again.
Why a separate script: ArcPinnedTabExtractor gives each Arc profile's Favorites to one space (and skips the default profile's unless asked), while mirror mode keeps them per profile and also needs Arc's favicon cache and profile names. Reading Arc directly left the existing pipeline unchanged. Its tests use synthetic data only: python3 -m pytest tests/test_sync.py.
--dry-run- Test migration without making changes--zen-profile NAME- Specify target Zen profile name--arc-space NAME- Migrate only a specific Arc space by name (case-insensitive partial matching). If not specified, all spaces are migrated.--verbose- Enable detailed debug logging--help- Show all available options
The tool creates several files during migration (all excluded from git):
arc_bookmarks_export.json- Extracted Arc pinned tabsarc_pinned_tabs_export.json- Arc pinned tabs with workspace infoworkspace_uuid_mapping.json- Mapping between Arc spaces and Zen workspaces*.backup.*- Database backups
✅ Improved: The migration tool preserves Arc's visual ordering using Arc's internal container structure.
Technical Solution: Arc stores display order in each space's pinned container childrenIds array, which contains items in visual order. The migration tool uses this data structure to maintain ordering fidelity.
Result: Folders and tabs appear in Zen in a similar order to your Arc sidebar.
✅ Implemented: Arc space icons migrate to Zen workspaces as Unicode emojis.
Technical Solution: Extracts Unicode emojis from Arc's customInfo.iconType.emoji_v2 field and preserves them in Zen workspace definitions.
Result: Arc space icons (🏠, 🌳, 🎬, ⚖️, etc.) appear as visual icons in Zen workspaces.
✅ Implemented: Arc space colors migrate as subtle workspace themes with pixel-perfect accuracy.
Technical Solution:
- Extracts RGB values from Arc's
customInfo.windowTheme.primaryColorPalette.midTone - Uses measured Arc color values (e.g., Personal green: #bbf6da, WillowTree gold: #fbe496)
- Applies Arc's exact color transformation algorithm to create matching subtle tints
- Stores as JSON theme data in Zen's workspace theme system (
theme_type,theme_colors)
Result: Zen workspace backgrounds closely match Arc's subtle color aesthetics.
✅ Implemented: Arc's Essential tabs (top toolbar) migrate to appropriate workspaces.
Technical Solution:
- Extracts Essential tabs from Arc's
topAppscontainers per profile - Maps tabs to correct workspaces using profile associations (
directoryBasename) - Imports with
is_essentialflag to distinguish from regular pinned tabs
Result: Arc's Essential tabs appear as pinned tabs in their respective Zen workspaces.
- Arc's custom folder icons/colors are not preserved (Zen uses its own folder styling)
- All folder hierarchy and content relationships are maintained
- Arc-specific features (like Boosts, Easels) don't have Zen equivalents and are not migrated
- Standard web content, bookmarks, and organizational structure migrate completely
- Space icons: Arc space emojis migrate as Unicode icons in Zen
- Space colors: Arc color themes migrate as Zen workspace themes
- Essential tabs: Arc's top toolbar tabs migrate to appropriate workspaces
- Display ordering: Arc sidebar ordering preserved via container childrenIds
- Folder hierarchy: Nested folder structure maintained
- Workspace mapping: Arc space → Zen workspace conversion
"Zen profile not found"
- Make sure Zen browser has been run at least once
- Check that the profile directory exists at
~/Library/Application Support/zen/Profiles/
"No Arc data found"
- Verify Arc browser is installed
- Check that you have spaces with pinned tabs
Workspace mapping issues
- Run
python3 src/zen_workspace_mapper.pyto manually map spaces - Update the generated
workspace_uuid_mapping.jsonfile
If anything goes wrong:
- The tool creates automatic backups of your Zen database
- You can restore from the
.backupfiles - Your Arc data remains unchanged (read-only access)
- Location:
~/Library/Application Support/Arc/(macOS) - Format: Chromium-based with Arc-specific extensions
- Key File:
StorableSidebar.jsoncontains spaces, pinned tabs, and folder hierarchy - Container ordering: Each space has
containerIDs: ['pinned', uuid, 'unpinned', uuid]where the UUID immediately following each marker stores that category'schildrenIdsin exact visual order. The marker order can vary (pinned-first or unpinned-first).
- Location:
~/Library/Application Support/zen/Profiles/[profile]/(macOS) - Format: Firefox-based
- Key Files:
zen-sessions.jsonlz4(Zen 1.18+: spaces, pinned tabs, folders)sessionstore.jsonlz4+sessionstore-backups/recovery.jsonlz4(Firefox session; on Zen 1.20+ open tabs must also exist here with a matchingzenSyncId— see "Dual-File Injection" above)places.sqlite(bookmarks database)containers.json(workspace container definitions)prefs.js(preferences)
- Zen 1.18+:
zen-sessions.jsonlz4— mozlz4-compressed JSON containing spaces, tabs, and folders - Legacy Zen:
zen_pinsandzen_workspacesSQLite tables inplaces.sqlite - Bookmarks: Standard Firefox
moz_bookmarks/moz_placestables (used as backup)
Contributions are welcome! This is an open source tool for the community.
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
Always test with dry-run first:
python3 migrate_arc_to_zen.py --dry-run --verboseMIT License - See LICENSE file for details.
- Arc Browser team for creating an innovative browser
- Zen Browser team for building a privacy-focused alternative
- The open source community for inspiration and tools
- Claude Code for AI-assisted development and debugging