Bidirectional sync of Helium browser bookmarks + saved tab groups across Macs, using your own private git repo as the transport. CLI, no extension.
A workaround until imputnet/helium#90 lands native sync.
helium-sync push # done on this device → git push
helium-sync pull # starting on this one → git pull → write to live profile
Two devices, one user, sequential. Last push wins.
brew install aadarwal/tap/helium-sync
helium-sync setupsetup is interactive. It asks where to put your private data repo (default ~/helium-data), walks through adding a git remote (or skips it on the first device), writes ~/.config/helium-sync/config.toml, and bootstraps from your live Helium state.
On a second device, run the same two commands. setup detects the existing remote, branches automatically into the adopt flow, and prompts y/N before overwriting local state.
If you'd rather not use brew (e.g. contributing):
brew install leveldb protobuf go
git clone https://github.com/aadarwal/helium-sync ~/.local/share/helium-sync
cd ~/.local/share/helium-sync
python3.13 -m venv .venv && .venv/bin/pip install -r requirements.txt
ln -sf "$PWD/bin/helium-sync" /opt/homebrew/bin/helium-sync
helium-sync setuphelium-sync setup interactive first-time configuration
helium-sync push snapshot live → git push (Helium can stay running)
helium-sync pull git pull → write to live (Helium must be quit; Cmd-Q first)
helium-sync status diff live vs canonical
helium-sync log recent sync commits
helium-sync gc prune logs/ backups older than 30 days
helium-sync init lower-level: bootstrap on the source-of-truth device
helium-sync adopt lower-level: bootstrap on a new device receiving canonical
Discipline: pull at start of session, push at end. Backups under logs/prePull.<ts>/ if you slip.
Two targets, two formats, one transport.
Bookmarks — Default/Bookmarks is JSON. Read it, zero checksum, write back atomically. That's the whole target.
Saved tab groups — protobuf entries keyed saved_tab_group-dt-<UUID> in a LevelDB at Default/Sync Data/LevelDB/. Read via leveldbutil dump over the SSTable files directly: no LevelDB lock contention, so push works while Helium runs. Write via a small Go binary (bin/leveldb-writer, ~80 lines using syndtr/goleveldb); pull requires Helium quit because LevelDB writes do need the lock.
plyvel won't work — Apple Silicon Homebrew's libleveldb.dylib is built with -fvisibility=hidden (zero exported leveldb symbols, dlopen fails). The Go binary sidesteps the C++ library entirely.
Transport — real git. push does git add state/ && git commit && git push. pull does git pull --rebase. State is plain JSON, so git log is your sync history and git diff is your bookmark diff. No three-way merge: for sequential single-user use, "last push wins" is simpler than reasoning about divergent histories.
History, cookies, passwords, extensions, settings, themes, live open tabs. Privacy-sensitive, per-device-by-design, or wrong shape. Live tabs return per-device via Helium → Settings → On startup → Continue where you left off.
macOS only (arm64 + Intel). One user, two devices, sequential. No Linux. No Windows. No browser extension. No auto-merge. See CONTRIBUTING.md.
Built for personal use, MIT-licensed, shared because imputnet/helium#90 is taking a while. Issues and PRs welcome but slow.