A browser extension that synchronizes open tabs and tab groups across multiple browsers and operating systems without creating circular sync loops.
This project is an experiment with agentic coding. I've never built a web extension and am using this project as a way to learn the contours of developing an extension as well as best practices for working on a greenfield project with coding agents. I hope it's (eventually) useful but note that this is a research project, first and foremost.
- Non-circular tab sync across Chrome and Firefox
- Per-device management with individual sync toggles
- Device identification with browser icons and last sync times
- Tab group support (Chrome) with emoji markers
- Color customization for synced devices
- Configurable tab limits (default: 50, max: 100)
- One-click cleanup for device-specific tabs
- Equal browser support for Chrome and Firefox
- OAuth 2.0 authentication via Google
- Configurable sync intervals (default: 30 seconds)
- Background sync service worker
- Clean, minimal React-based UI
The extension prevents tab explosion by tracking which tabs are synced from other devices:
Chrome:
- Creates tab groups with emoji suffix:
Device Name 📡 - Only syncs tabs from native (non-synced) groups
- Synced groups are excluded from sync operations
- Moved tabs automatically return to their synced groups
Firefox:
- Uses metadata tracking in chrome.storage.local
- Tracks which tabs came from which device
- Excludes synced URLs from being re-synced
- Works with Firefox's limited tab group support
Each synced device is tracked with:
- Unique device ID (UUID)
- Display name (user can rename)
- Browser type with icon (🌐 Chrome, 🦊 Firefox, 🧭 Safari, 📘 Edge)
- Operating system
- Group color (randomly assigned, user-customizable)
- Sync enabled flag
- Registration and last sync timestamps
- Tab limit (0-100)
From the popup, users can:
- Enable/disable sync per device (toggle switch)
- Customize color for each device (8 color options)
- Adjust tab limit per device (0-100)
- Cleanup tabs from specific devices (one-click)
- View last sync time with relative display
- Open tabs from remote devices
- Frontend: TypeScript + React for extension UI
- Backend: Node.js + Express
- Database: MongoDB
- Authentication: OAuth 2.0 with Google
tabbycat/
├── extension/ # Browser extension code
│ ├── src/
│ │ ├── popup/ # Extension popup UI
│ │ ├── options/ # Settings page
│ │ ├── background/ # Background service worker
│ │ └── types/ # TypeScript types
│ ├── public/ # Static assets and manifest
│ └── package.json
├── backend/ # API server
│ ├── src/
│ │ ├── routes/ # API routes
│ │ ├── models/ # MongoDB models
│ │ └── middleware/ # Express middleware
│ └── package.json
└── shared/ # Shared types
The easiest way to get started is using Docker and Docker Compose. See docs/DOCKER.md for detailed instructions.
Quick Start:
# Create .env file with Google OAuth credentials
cat > .env << EOF
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_CALLBACK_URL=http://localhost:3000/auth/callback/google
EOF
# Start services
docker compose up -d
# Build extension
docker compose --profile build up --build extension-build
# View logs
docker compose logs -f- Node.js 18+
- MongoDB
- Google Cloud Console project with OAuth credentials
- Docker and Docker Compose (optional, for containerized deployment)
cd tabbycat
npm install
cd extension && npm install
cd ../backend && npm installStart MongoDB:
mongodOr update MONGODB_URI in backend/.env if using MongoDB Atlas.
- Go to Google Cloud Console
- Create a new project or select existing
- Enable Google+ API
- Create OAuth 2.0 credentials
- Add
http://localhost:3000/auth/callback/googleto authorized redirect URIs - Copy Client ID and Client Secret
Create backend/.env:
PORT=3000
MONGODB_URI=mongodb://localhost:27017/tabsync
JWT_SECRET=your-secret-key-change-this-in-production
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
CLIENT_URL=chrome-extension://*cd backend
npm run devThe API server will run on http://localhost:3000
cd extension
npm run buildOr for development:
npm run dev- Open
chrome://extensions/ - Enable "Developer mode"
- Click "Load unpacked"
- Select
extension/distfolder
- Open
about:debugging#/runtime/this-firefox - Click "Load Temporary Add-on..."
- Select
extension/dist/manifest.json
- Click the extension icon
- Click "Sign in with Google"
- Complete OAuth flow
- Copy the token and store it (will be automated in future)
- Open multiple tabs in your browser
- Click "Sync Now" in the extension popup
- Tabs are synced to the backend
- Device appears in "Synced Devices" list
- Open extension popup
- View list of synced devices with last sync times
- Toggle sync on/off per device
- Click color dots to change device color
- Adjust tab limit (0-100) per device
- Click "Cleanup" to remove all tabs from a device
- Synced devices appear as tab groups:
Chrome on Windows 📡 - Groups are collapsed by default
- Expand to view and open synced tabs
- Tabs moved from synced groups return automatically on next sync
- Synced tabs are tracked via metadata
- Works seamlessly with Firefox's tab group limitations
- All features available except visual tab groups
GET /auth/google- Start Google OAuth flowGET /auth/callback/google- OAuth callbackGET /auth/verify- Verify token
POST /api/sync- Sync tabs from device
GET /api/devices- Get all user devices (up to 100 tabs each)POST /api/devices/register- Register/update device
# Run all tests
pnpm test
# Backend tests only
cd backend && pnpm test
# Extension tests only
cd extension && pnpm test
# Generate coverage report
pnpm test -- --coverageTest Coverage Status:
- Backend: 100% (72/72 tests passing)
- Extension: Configured (tests in progress)
# Start backend with hot reload
docker compose up backend
# Build extension
docker compose --profile build up --build extension-buildRun everything:
npm run dev# Update credentials in docker-compose.yml
# Update GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in .env
# Start all services
docker compose up -d
# Build extension
docker compose --profile build up --build extension-buildSee docs/DOCKER.md for production deployment details including:
- Security checklist
- Backup and restore procedures
- Scaling and resource limits
- Zero-downtime deployments
- Set environment variables in
backend/.env - Build TypeScript:
cd backend && npm run build - Start server:
node dist/server.js
- Build:
cd extension && npm run build - Package
extension/distfolder - Submit to Chrome Web Store and Firefox Add-ons
- Change JWT_SECRET in production
- Use environment variables for secrets
- Enable HTTPS in production
- Validate all user inputs
- Implement rate limiting
- Ensure sync groups have the emoji suffix (📡)
- Check that sync is disabled for the current device
- Clear extension storage and re-authenticate
- Verify device sync is enabled
- Check tab limit setting (may be 0)
- Ensure backend server is running
- Check browser console for errors
- Some tab group features are limited in Firefox
- Synced tabs work but without visual grouping
- All other features work normally
MIT