Chrome leads, three browsers follow

How Claude and I built a bookmark mirror for my Linux desktop in one evening, and what my real bookmarks taught us that the tests couldn’t.

A one-way bookmark mirror Chrome is the master copy at the top. Chromium, Edge and Firefox hang below it like bookmark ribbons, each receiving Chrome's bookmarks. Changes flow down only. Chrome, the master copy (read only) Chromium Bookmarks file (JSON) Edge Bookmarks file (JSON) Firefox places.sqlite (database) A one-way bookmark mirror Chrome is the master copy at the top. Chromium, Edge and Firefox hang below it like bookmark ribbons, each receiving Chrome's bookmarks. Chrome, the master copy read only Chromium JSON file Edge JSON file Firefox database
Changes flow one way, from Chrome down. Each browser is written only while it’s closed, and only when it differs from Chrome.

The problem

I keep four browsers on my Linux desktop: Chrome, which holds my current bookmarks, plus Chromium, Microsoft Edge and Firefox. I use more than one for testing, and as a fallback when a browser update occasionally breaks a site I depend on. Each has its own bookmark sync, but each only syncs with itself. Google syncs Chrome, Mozilla syncs Firefox, Microsoft syncs Edge, and none of them talk to each other.

So the bookmarks drifted. A site saved in Chrome never reached Firefox, and Edge still had an older folder layout than Chrome. Until now, I exported my Chrome bookmarks to a file and imported it into the others by hand. With more than 400 bookmarks in about 80 folders, keeping up that way wasn’t realistic.

What I asked Claude for was simple: keep Chrome, Chromium and Firefox in sync, with Chrome as the primary copy. Edge joined the list later that evening.

2 h 20 min
from first request (7:42 pm) to final release (10:01 pm)
1,241
lines of Python, using only the standard library
8
releases, from 1.0.0 to 1.3.3
430
bookmarks in 81 folders, now identical in four browsers
23
Edge-only bookmarks recovered from a backup
0
packages to install

The approach

Before writing any code, Claude settled the ground rules. They turned out to matter more than any clever feature:

  • One-way mirror. Chrome is read, never written. Every other browser is made to match it exactly.
  • Never write to an open browser. A running browser overwrites its bookmark file when it closes, so the script waits until it’s closed.
  • Back up first, every time. The newest 10 copies of each browser’s bookmarks are kept.
  • Preview before acting. --dry-run shows every change without touching anything.
  • Nothing to install. Plain Python 3, nothing to download. It’s confirmed on Linux. The code also covers macOS and Windows, but those haven’t been tested yet.

Chrome, Chromium and Edge store bookmarks in the same JSON format, so those are straightforward. Firefox is the hard one: its bookmarks live in a SQLite database alongside your browsing history, with internal hash values and reference counts that Firefox expects to stay consistent. The script writes Firefox in a single database transaction, reuses existing history entries, and records deletions so Firefox Sync passes them on to other devices.

What happens on each sync

  1. Read Chrome’s bookmarksIncluding any saved to the Google account, merged without duplicates.
  2. Compare with each browserTitles, links, folders and order. If they already match, nothing is written.
  3. Wait if the browser is openIt’s noted once in the log and updated within a minute of closing.
  4. Back up, and save anything about to disappearBookmarks only that browser had are saved to a file Chrome can import.
  5. Write the new bookmarksAn atomic file swap for Chromium and Edge, one database transaction for Firefox.

The evening, release by release

The first version passed every test Claude built against simulated browser profiles. Then it met my real bookmarks. Most of the evening went to what they revealed.

  1. 7:42

    The request

    “Write some code that will keep Chrome, Chromium and Firefox bookmarks in sync, with Chrome being the primary.”

  2. 7:51

    1.0.0First version on GitHub Planned

    Tested first against fake Chrome, Chromium and Firefox profiles: a preview, a real sync, a second run that changed nothing, a browser left open, and a database integrity check.

  3. 7:57

    1.0.1Invisible spaces Real data

    My first preview wanted to delete and re-add the same 14 Firefox bookmarks. Some of my Chrome titles end in stray spaces, and Firefox trims them. Titles are now compared without those spaces.

  4. 7:59

    1.1.0Moves aren’t deletions Real data

    Adding Edge showed 34 additions and 38 deletions, but most were bookmarks filed in a different folder. The report now separates moved and renamed bookmarks from ones that are truly new or truly gone.

  5. 8:02

    1.2.0The rescue Real data

    Syncing Edge removed 24 bookmarks that existed only in Edge. The backup had them all. A new --rescue option pulled the 23 that Chrome didn’t already have into a file I could import back into Chrome. Since then, every sync saves such bookmarks before removing them.

  6. 8:09

    1.3.0Watching both directions Real data

    The background service rewrote Edge but couldn’t say why. It now notices when a browser changes its own bookmarks, and names any folder whose order was restored.

  7. 8:12

    1.3.1Edge calls them favorites Real data

    Edge was still being rewritten at every check, with no differences listed. The cause: Edge names its top folders “Favorites bar” and “Other favorites”, so it never matched Chrome’s “Bookmarks bar”. A one-line change to compare only what’s inside the folders fixed it.

  8. 8:17

    A printable cheat sheet Planned

    A two-page PDF of every command I’d need: service control, logs, previews and recovery.

  9. 8:26

    Reboot test

    After a restart, the service started on its own at login and confirmed that Chromium, Edge and Firefox all matched Chrome.

  10. 8:33

    First real bookmark

    I bookmarked cosmos-institute.org in Chrome. It reached Chromium and Edge on the service’s next check, and Firefox at 8:37, shortly after I closed it.

  11. 9:37

    1.3.2Code review Review

    Reading through the code in VS Code turned up eight edge cases, among them a damaged Chrome bookmark file, file paths containing # or %, sibling folders with the same name, and a backup setting of zero that would have deleted the backup just taken.

  12. 10:01

    1.3.3Checking the fixes Review

    Claude tested each of those eight fixes. All held, but one didn’t match the documentation: saved-bookmark files were trimmed to 10 in total rather than 10 per browser. Fixed and pushed.

The bug worth showing

The Edge problem is a good example of how small these fixes can be once the cause is clear. The comparison included the names of the top-level folders, and each browser chooses its own names:

# 1.3.0: compared everything, including the top folders' own names
return tuple(tree[r].signature() for r in roots)

# 1.3.1: compare only what's inside them
return tuple(tree[r].signature()[2] for r in roots)

Finding it was the hard part. The clue was a log line reporting “Edge: updated” while listing zero differences. If nothing was different, the comparison itself had to be wrong.

Running it day to day

The script runs as a Linux systemd user service. It starts when I log in, checks every 60 seconds, and logs what it does. I don’t think about it anymore: I bookmark in Chrome, and the other browsers have it the next time I open them.

Two practical lessons came out of setup. When I moved my code folder into ~/Development, the service quietly failed, because it still pointed at the old path. The log said so plainly, and one edit to the service file fixed it. And pushing my own changes to GitHub needed a sign-in, which the GitHub command-line tool (gh auth login) handled through the browser.

Who did what

“We” is generous to me. Claude wrote nearly all of it. But the job couldn’t have been done from one side alone.

Claude

  • Designed the approach and safety rules
  • Wrote the code, the README and the cheat sheet
  • Built simulated browser profiles to test every change before release
  • Diagnosed each problem from the output I pasted back
  • Tested my review fixes and caught the one mismatch

Me

  • Set the goal and the scope (Edge in, Safari out)
  • Ran every step on my own machine and real bookmarks
  • Decided which rescued bookmarks to keep
  • Set up the background service
  • Reviewed the code in VS Code for release 1.3.2

What I took away

Try it yourself

The code, README and cheat sheet are on GitHub: ne0busin3ss/Python, Bookmark_Sync folder. Start with a preview. It doesn’t change anything.

git clone https://github.com/ne0busin3ss/Python.git
cd Python/Bookmark_Sync
python3 bookmark_sync.py --list
python3 bookmark_sync.py --dry-run

Tested on Linux only. It runs daily on my Linux desktop, and the background service uses Linux’s systemd. The macOS and Windows support is written but hasn’t been tried on a real machine yet.

It’s a mirror, so anything that exists only in the other browsers will be removed. It’s saved to an importable file first, but read the preview before your first real sync.