Skip to content

paperhurts/gitmarks

Repository files navigation

gitmarks

Serverless cross-browser bookmark sync. Bookmarks live as a JSON file in your own private GitHub repo; browser extensions and a web UI both talk directly to the GitHub Contents API. No server, no backend, no infrastructure to host. You own your data — it's just a file in a repo you control.

Status: Chrome extension is functional end-to-end (save via toolbar button, two-way sync with the native bookmark tree, 5-min poll for remote changes, automatic conflict retry). Firefox MV3 add-on shipping the same source as Chrome via a shared package. Safari / web UI are next in the roadmap. See spec.md for the full design.

Features (Chrome, today)

  • Save the current tab to GitHub via a toolbar button
  • Drag a URL to your Chrome bookmarks bar → it appears in bookmarks.json on GitHub within ~1 second
  • Edit a bookmark's title in Chrome → updates remote within ~1 second
  • Delete a bookmark in Chrome → soft-deleted (tombstoned) remotely; garbage-collected from the JSON after 30 days but retained in git history forever
  • Edit bookmarks.json directly on GitHub → changes pull into Chrome on the next 5-minute poll
  • Concurrent edits from multiple devices reconcile automatically via GitHub's file SHA + optimistic retry-replay
  • 162 automated tests (unit + Playwright e2e against real Chromium)
  • Optional tracking-param stripping (utm_*, fbclid, gclid, etc.) at save time — opt-in via settings

Packages

Package Role
@gitmarks/core Shared TypeScript library: schemas (Zod), GitHub Contents API client with optimistic concurrency, ULID + URL helpers, pure mutation helpers
@gitmarks/extension-shared Cross-browser extension source — popup, options, background, lib/ helpers. Consumed by both browser shells via workspace:*. 96 unit tests live here.
@gitmarks/extension-chrome Chrome MV3 shell. Manifest + Vite/crxjs build + Playwright e2e. Thin entry files import from extension-shared.
@gitmarks/extension-firefox Firefox MV3 shell. Manifest + plain Vite build. Same source as Chrome via extension-shared. Load via about:debugging.

Quick start (Chrome extension)

pnpm install
pnpm --filter @gitmarks/extension-chrome build

Then in Chrome:

  1. chrome://extensions/ → toggle Developer mode on
  2. Load unpacked → select packages/extension-chrome/dist/
  3. Click the toolbar icon → "Set up gitmarks"
  4. Paste a fine-grained PAT (Contents: read/write scope on your bookmarks repo), enter owner/repo/branch, click Save

See packages/extension-chrome/README.md for the full setup walkthrough, the manual smoke test checklist, and architecture notes.

Your data, your PAT

  • The repo must be private. Public repo + the project name = anyone can find your bookmarks. The extension does NOT enforce this — it's on you when you create the repo on github.com.
  • Use a fine-grained PAT scoped to only your bookmarks repo with only Contents: read/write. Never use a classic PAT or one with broader scopes — if your browser profile is ever exfiltrated, that token only unlocks your bookmarks, not your whole GitHub account.
  • The PAT is stored in chrome.storage.local, which is origin-scoped (other extensions / sites can't read it) but readable by anyone with access to your unlocked browser profile. Treat it like a saved password.
  • No telemetry. The extension only talks to api.github.com. That's enforced by the MV3 manifest's host_permissions.

Development

# Everything
pnpm install
pnpm test           # all unit tests across packages
pnpm typecheck
pnpm build

# Just one package
pnpm --filter @gitmarks/core test
pnpm --filter @gitmarks/extension-shared test   # all extension unit tests live here
pnpm --filter @gitmarks/extension-chrome e2e    # Playwright + real Chromium

The repo is a pnpm workspace monorepo. Each package has its own README.md with package-specific docs.

Architecture

[Chrome ext] [Firefox ext] [Safari ext (planned)]    [Web UI (planned)]
       \             |                       /                       /
        \            |                      /                       /
         v           v                     v                       v
                          GitHub REST API (api.github.com)
                                       |
                                       v
                          User's private repo: bookmarks.json + tags.json

The load-bearing invariants:

  • No server, ever. Clients talk to GitHub REST API directly. PAT lives client-side (chrome.storage.local).
  • Optimistic concurrency via GitHub file SHA. On 409, the core client refetches and replays the mutation (up to 3 attempts with exponential backoff).
  • Eventual consistency, ~30s target. Event-driven push for local changes (500ms debounce). 5-minute poll for remote changes via chrome.alarms, with ETag conditional reads so unchanged polls cost nothing against the rate limit.
  • Soft deletes (tombstones) for ~30 days; git history retains everything forever.
  • Suppression registry prevents loop-back: when the extension applies a remote change to chrome.bookmarks, the affected URL is parked in an in-memory registry for ~2 seconds so the resulting local event doesn't echo back to GitHub.

Roadmap

  • @gitmarks/core — schemas, GitHub client, mutations
  • ✅ Chrome MVP — toolbar-button save flow
  • ✅ Chrome native tree integration — listeners, reconcile, poll loop
  • ✅ Tracking-param stripping (opt-in)
  • ✅ Firefox MV3 add-on (#23)
  • ⬜ Web UI v1: list + search + tag management (#24)
  • ⬜ Web UI v2: bulk operations + trash + export (#25)
  • ⬜ Safari (#26)

Files in this repo

  • spec.md — full design spec (source of truth for non-obvious decisions)
  • CONTRIBUTING.md — branch/PR conventions, TDD policy, plan-driven workflow
  • CLAUDE.md — guidance for AI agents working in this repo
  • LICENSE — MIT
  • docs/superpowers/plans/ — implementation plans, one per branch
  • packages/*/README.md — package-specific documentation
  • examples/example-bookmarks-repo/ — sample bookmarks.json + tags.json to seed a fresh repo, used by @gitmarks/core fixture tests
  • .github/workflows/test.yml — CI (typecheck + unit tests + build on every PR)

Contributing

See CONTRIBUTING.md for the branch/PR conventions, conventional-commit scopes, and the plan-driven workflow used for larger features. Every change goes through a PR with green CI — no direct commits to main.

About

Serverless cross-browser bookmark sync. Your bookmarks live as a JSON file in your own private GitHub repo; the extensions talk directly to the GitHub Contents API. No server, no backend.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors