Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ZenSync

Sync your Zen Browser configuration across multiple machines using a Git repository as the backbone.

Written in Rust. Linux only for now.

Status: v0.1.0. Declarative, NixOS-style config: you choose exactly what syncs — sessions, cookies and history are off by default.


What problem this solves

Firefox Sync (which Zen uses under the hood) only covers part of the browser state:

  • Bookmarks
  • Passwords
  • History
  • Installed extensions
  • Open tabs

It doesn't cover the customizations that matter most on Zen:

  • Workspaces and containers
  • Arc-style CSS (userChrome.css, userContent.css)
  • Zen mods (zen-themes.json + chrome/zen-themes/)
  • about:config settings (user.js, prefs.js)
  • Custom keyboard shortcuts
  • Toolbar layout (xulstore.json)
  • Essential tabs and pinned tabs

ZenSync covers all of that. You can use it alongside Firefox Sync — or instead of it, if you'd rather sync passwords/bookmarks through Git too (see What gets synced).


What it does today

  • Commands: init, push, pull, config, purge-history
  • Declarative config (config.toml, synced via the repo) + optional per-machine override (config.local.toml) — NixOS-style: same environment everywhere
  • Per-group control: what syncs (sync) and what happens to local data of disabled groups on pull (on_pull = "preserve" | "clean")
  • Orphan detection: files from previous syncs that no longer match your config are flagged and removed (with confirmation)
  • Optional history purge (purge-history) to erase sensitive state from all past commits
  • Automatic detection of the active profile (Flatpak and native install)
  • Automatic backup before every pull
  • Defensive locks: pull aborts if Zen is running; push warns if Zen is running with sessions enabled
  • Absolute path sanitization (/home/user/{{HOME}})

The config file

zensync config --init creates config.toml inside the repo (so every machine shares the same rules). For per-machine overrides, create ~/.local/share/zensync/config.local.toml — it is never synced and wins over the shared file.

[groups.ui]            # themes, mods, prefs, shortcuts, chrome/
sync = true
on_pull = "preserve"

[groups.extensions]    # installed extensions + their settings
sync = true

[groups.containers]    # container definitions (name/color/icon)
sync = true

[groups.passwords]     # logins.json + key4.db
sync = true

[groups.bookmarks]     # bookmarkbackups/ snapshots — NO browsing history
sync = true

[groups.sessions]      # workspaces, open tabs, tab↔container, pins, live folders
sync = false

[groups.history]       # places.sqlite, formhistory
sync = false

[groups.cookies]       # active site logins
sync = false

[groups.storage]       # site data (IndexedDB etc.)
sync = false

[extra]
include = []           # extra paths relative to the profile
exclude = []           # e.g. ["xulstore.json"] to skip window layout
  • sync = false → the group never leaves/enters this machine
  • on_pull = "preserve" → local data of a disabled group is left untouched
  • on_pull = "clean" → local data is deleted on pull (full NixOS-like purity; the automatic backup still covers you)

Want the exact same tabs/workspaces on every machine? Set sessions.sync = true. Sites will open logged-out (cookies stay off) but the structure comes over.

Bookmarks caveat: they sync via Zen's own bookmarkbackups/ snapshots. After a pull, restore once per machine: Ctrl+Shift+O → Import and Backup → Restore → pick the date. Snapshots are generated by Zen daily, so brand-new bookmarks may take up to a day to appear.


What gets synced

Group membership lives in src/files.rs. zensync config shows your effective setup.

What does NOT get synced

The denylist in src/files.rs blocks transient state that either regenerates automatically or corrupts the target if copied:

  • Lock files: .parentlock, parent.lock, lock, any *.lock / *.lck
  • SQLite write-ahead logs: any *-wal, *-shm, *-journal

These are also deliberately excluded (not on the allowlist):

  • cache2/, startupCache/, thumbnails/, shader-cache/ — caches (regenerate)
  • weave/ — Firefox Sync internal state (syncing this causes conflicts)
  • datareporting/, crashes/, minidumps/, logs/ — telemetry / diagnostics
  • gmp-gmpopenh264/, gmp-widevinecdm/ — DRM codecs (installed on demand, ~20 MB)

Important warnings

Passwords in Git

The passwords group is on by default — meaning your passwords go into the Git repo. Even if the repo is private, this means that if your GitHub account or SSH key is compromised, your passwords leak.

If you'd rather leave passwords to Firefox Sync (safer), set groups.passwords.sync = false.

Sensitive state in git history

If you synced before v0.1.0, your repo history contains sessions/cookies. push removes them from the current state (with confirmation), but old commits still have them. To erase them completely:

# requires git-filter-repo: https://github.com/newren/git-filter-repo
zensync purge-history

This rewrites history (force-push). Other machines must re-init afterwards.

SQLite + Zen open

Files like places.sqlite, sessionstore.jsonlz4 etc. change constantly while Zen runs. push warns when Zen is open and the sessions group is enabled — prefer closing Zen first.

Pull conflicts

zensync pull uses git pull --ff-only. If you edited configs on two machines without syncing between them, the pull will fail. For now, resolve manually:

cd ~/.local/share/zensync/repo
git pull --rebase    # or your preferred strategy

Requirements

  • Linux (Fedora, Ubuntu, Arch — any distro; tested on Fedora 43)
  • Rust (any recent stable; tested with 1.95)
  • Git available in PATH
  • Zen Browser installed (native or Flatpak — both autodetected)
  • A GitHub account (or any Git host) with an empty private repository to store your configs
  • An SSH key registered on GitHub (or HTTPS with a PAT)
  • (optional) git-filter-repo — only needed for purge-history

Installation

From crates.io

cargo install zensync

From source

# Install Rust if you don't have it
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
source ~/.cargo/env

# Clone the project
git clone https://github.com/DevPorfirio/ZenSync.git
cd ZenSync

# Build and install the binary to ~/.cargo/bin/zensync
cargo install --path .

# Confirm
zensync --help

Initial setup

1. Create the config repository

Create an empty private repository on GitHub (or GitLab, Codeberg, etc.):

  • Go to github.com/new
  • Name: zen-configs (or whatever you prefer)
  • Visibility: Private
  • Do not check "Add README", "Add .gitignore", or a license — it must be 100% empty

Note the SSH URL, something like: git@github.com:YOUR_USER/zen-configs.git.

2. Make sure SSH works

ssh -T git@github.com
# Expected: "Hi USER! You've successfully authenticated..."

If you get Permission denied, you need to add an SSH key to your GitHub account. Generate one if you don't have any:

ssh-keygen -t ed25519 -C "you@example.com"
cat ~/.ssh/id_ed25519.pub

Paste the output into github.com/settings/ssh/new.

3. Initialize ZenSync

zensync init git@github.com:YOUR_USER/zen-configs.git

This clones the (empty) repo into ~/.local/share/zensync/repo.

4. First push (with Zen closed)

# Close Zen first (important because of the SQLite WAL)
pgrep -af zen

zensync push

Done — your configs are on GitHub.


Usage

# Send local changes to Git
zensync push            # --yes to skip confirmations

# Pull changes from Git and apply (Zen must be closed)
zensync pull

# Show the effective config and where each layer comes from
zensync config          # --init creates the shared config.toml

# Erase disabled groups (sessions, cookies...) from ALL git history
zensync purge-history   # requires git-filter-repo; force-pushes

# Show help
zensync --help

On a second machine

Repeat Installation and Initial setup, but in step 4 use pull instead of push:

zensync init git@github.com:YOUR_USER/zen-configs.git
# close Zen
zensync pull

pull creates a full backup of the current profile at ~/.local/share/zensync/backups/<timestamp>/ before overwriting anything, so you can revert manually if something goes wrong.

Day-to-day flow

  • Changed something on machine A → zensync push
  • Before opening Zen on machine B → zensync pull (with Zen closed) → open Zen

Project structure

ZenSync/
├── Cargo.toml
├── plano.md           # planning notes (Portuguese)
├── README.md          # you are here
└── src/
    ├── main.rs        # entry point, subcommand routing
    ├── cli.rs         # clap definitions (subcommands and flags)
    ├── config.rs      # layered config (shared + local override), groups
    ├── config_cmd.rs  # `config` command
    ├── files.rs       # group-tagged allowlist and denylist
    ├── state.rs       # paths for the state dir (~/.local/share/zensync)
    ├── profile.rs     # detects the active profile via profiles.ini
    ├── lifecycle.rs   # checks whether Zen is running (sysinfo)
    ├── sanitize.rs    # $HOME ↔ {{HOME}} transformation
    ├── git.rs         # shell-out to git
    ├── sync.rs        # copy engine (text/binary/recursive)
    ├── init.rs        # init command
    ├── push.rs        # push command
    ├── pull.rs        # pull command
    └── purge.rs       # purge-history command

Where things live on disk

Path Purpose
~/.local/share/zensync/repo/ Local clone of the configs repo (holds the shared config.toml)
~/.local/share/zensync/config.local.toml Per-machine config override (never synced)
~/.local/share/zensync/backups/<timestamp>/ Automatic backups before every pull
~/.zen/ or ~/.var/app/app.zen_browser.zen/.zen/ Zen profile (we don't touch it directly — we resolve it via profiles.ini)

License

MIT.

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages