Repo: https://github.com/Vermaarp/Syncmark
Open-source, self-hostable bookmark sync across Chromium browsers (Brave, Chrome, Helium, Edge, Opera, Vivaldi). One account, one bookmark tree, any browser.
If you use more than one browser, your bookmarks don't follow you. Chrome Sync is Google-only. Firefox Sync is Firefox-only. Syncmark is a neutral sync server + browser extension that keeps bookmarks consistent across every Chromium-based browser on every device you use.
- Self-hosted: runs on your own machine via Docker, or on Fly.io / Render free tier.
- One extension for every Chromium browser — Manifest V3, same codebase works on Brave, Chrome, Helium, Edge, etc.
- Real-time sync via PocketBase WebSocket subscriptions.
- No tracking, no ads, no telemetry. Your bookmarks live on your server.
| Component | Stack | Role |
|---|---|---|
backend/ |
PocketBase (Go, SQLite) | Auth, storage, realtime broadcast |
extension/ |
Manifest V3, vanilla JS | Reads/writes chrome.bookmarks, syncs to backend |
dashboard/ |
Svelte + DnD (planned) | Web UI for managing bookmarks |
Data model: a single bookmarks collection where each row is a folder or a bookmark, with parent, title, url, idx, isFolder, syncVersion.
| Phase | Scope | Status |
|---|---|---|
| 1 | Backend schema + auth + realtime | Done |
| 2 | Extension two-way sync (initial + live) | Working but buggy (see below) |
| 3 | Web dashboard with drag-and-drop | Not started |
| 4 | Offline queue, E2E encryption, deploy guides, import/export | Not started |
Requires Docker.
git clone https://github.com/Vermaarp/Syncmark.git
cd Syncmark
docker compose up -d
docker exec -it syncmark-pocketbase-1 /pb/pocketbase admin create admin@test.com adminpass123Then:
- Open
http://127.0.0.1:8090/_/, log in asadmin@test.com/adminpass123. - Go to the users collection → + New record → create an email/password, toggle verified ON → Save.
- In Chrome/Brave/Helium/etc., open
chrome://extensions(orbrave://extensions). - Enable Developer Mode.
- Click Load unpacked → select the
extension/folder in this repo. - Click the Syncmark icon in the toolbar.
- Enter:
- Backend URL:
http://127.0.0.1:8090 - Email + Password: the user you created in step 2 above.
- Backend URL:
- Click Login, then Full Sync.
- Repeat on any other Chromium browser with the same credentials.
Bookmarks changed in one browser will propagate to the others within a few seconds.
This is an early prototype. There are real bugs:
- Duplicate folders/bookmarks on re-sync. If you run Full Sync multiple times in sequence or after restoring a wiped database, stale ID mappings in
chrome.storage.localcan cause duplicate folders to be created. Workaround: open the extension's service worker console and runawait chrome.storage.local.clear()before Full Sync. - Race-driven duplication. In rare cases, a bookmark created locally appears 2–3 times after sync. Caused by Server-Sent Event echoes arriving before the client-side ID mapping is persisted.
- Deletes don't always propagate if the mapping hasn't been established yet (e.g., cleaning duplicates before first Full Sync).
- Only the Bookmarks Bar and Other Bookmarks roots sync. The Mobile root is currently ignored.
- No Firefox support (the
browser.bookmarksAPI differences haven't been wired up). - No offline queue. If the server is unreachable, local changes are silently dropped.
- No conflict UI. Last-write-wins by
syncVersion+ timestamp. - No end-to-end encryption. Bookmark content is stored plaintext on the server.
- Index-out-of-bounds errors can appear when restoring a tree if sibling counts are inconsistent.
- Service worker restarts in Manifest V3 can cause the realtime SSE connection to drop silently. An alarm-based reconnect watchdog retries every minute, but live events can be lost in that window.
See the fuller design discussion in DESIGN.md (TODO) for the planned v2 approach: content-addressable IDs, event log instead of state sync, and vector clocks instead of timestamps to eliminate most of the above.
Contributions welcome. Areas where help is most valuable:
- Rewrite the sync loop around content-addressable IDs. Hash
parent + title + url→ deterministic id. Kills the race-condition duplication bug class. - Web dashboard (Svelte + svelte-dnd-action). Tree view of bookmarks, drag-and-drop reordering, CRUD via PocketBase SDK.
- Firefox support. Port the extension to
browser.bookmarks(promise-based API). - Offline queue. Persist pending ops in
chrome.storage.local, drain when online. - E2E encryption. Encrypt
title+urlfields client-side with a passphrase-derived key. - Import/export HTML (Netscape bookmark format, same as browsers).
git clone https://github.com/Vermaarp/Syncmark.git
cd Syncmark
docker compose up -dThe extension is plain JS + manifest.json — no build step. Edit files in extension/ and click the reload icon on chrome://extensions. Backend changes: add a migration in backend/pb_migrations/ and restart the container.
- Fork the repo.
- Create a branch from
main. - If you're fixing a bug listed above, reference it in your PR description.
- Open a PR. Keep it focused — one bug or feature per PR.
Open an issue at https://github.com/Vermaarp/Syncmark/issues with:
- Which browsers you had the extension installed on
- Steps to reproduce
- Anything visible in the extension's service worker console (
chrome://extensions→ Syncmark → service worker link → Console tab) - PB record count (admin UI → bookmarks collection)
cd backend
fly launch --no-deploy
fly volumes create pb_data --size 1 --region iad
fly deployAfter deploy, set the extension's Backend URL to https://<your-app>.fly.dev.
MIT. Do whatever you want with it.