- Dockerfile with multi-stage build (Node 22 Alpine, sodium-native) - docker-compose.yml with health check for easy self-hosting - README with setup guide, API reference, and project overview - Architecture docs (data flow, component breakdown, protocol constants) - Security model docs (threat model, crypto primitives, self-hosting checklist) - GitHub Actions CI pipeline (test, typecheck, Docker smoke test, extension builds) - GitHub Actions release pipeline (GHCR push, extension zip artifacts) - CONTRIBUTING.md with dev setup and code style guidelines Co-Authored-By: Paperclip <noreply@paperclip.ing>
7.0 KiB
CookieBridge Architecture
Overview
CookieBridge is a cross-device cookie sync system built on a zero-knowledge relay architecture. The relay server transports and stores encrypted cookie blobs but cannot decrypt them. All cryptographic operations happen client-side.
Components
Relay Server (src/relay/)
A plain Node.js HTTP + WebSocket server. No frameworks — just http, ws, and sodium-native.
- server.ts — Routes HTTP requests and upgrades WebSocket connections. Single entry point for all API traffic.
- connections.ts — Manages active WebSocket connections. Maps device IDs to sockets for real-time push.
- auth.ts — Two auth methods:
- Token auth: Bearer tokens issued at device registration. Used for HTTP and WebSocket.
- Challenge-response: Server sends random bytes, client signs with Ed25519. Used for WebSocket upgrade.
- store.ts — In-memory encrypted cookie blob storage. Keyed by
(deviceId, domain, cookieName, path). Limit: 10,000 cookies per device. - tokens.ts — Device and agent registries. Tracks device metadata, pairing relationships, and agent access grants.
Cryptography (src/crypto/)
All crypto uses libsodium via sodium-native (server) or libsodium-wrappers-sumo (extension).
| Operation | Algorithm | Purpose |
|---|---|---|
| Encryption | XChaCha20-Poly1305 | Cookie payload encryption (AEAD) |
| Signing | Ed25519 | Message authentication, device identity |
| Key exchange | X25519 | Deriving shared secrets between paired devices |
| Key derivation | BLAKE2b (generic hash) | Deriving encryption keys from ECDH output |
Sync Engine (src/sync/)
- envelope.ts — Wire format for WebSocket messages. Each message is signed and encrypted.
- conflict.ts — Last-writer-wins (LWW) conflict resolution. Uses Lamport clock timestamps. Ties broken by lexicographic device ID comparison.
Browser Extension (extension/)
Single TypeScript codebase compiled per-browser via esbuild.
- service-worker.ts — Extension lifecycle, device registration, pairing orchestration, sync triggers.
- api-client.ts — HTTP client for relay server REST API.
- connection.ts — WebSocket manager with auto-reconnect and exponential backoff.
- crypto.ts — Client-side encryption/decryption using libsodium-wrappers-sumo.
- sync.ts — Processes incoming cookie updates, applies them via
chrome.cookiesAPI. - compat.ts — Cross-browser abstraction layer for Chrome, Firefox, Edge, Safari API differences.
Data Flow
Device Registration
Extension Relay Server
│ │
│ POST /api/devices/register │
│ { deviceId, name, platform, │
│ encPub } │
│──────────────────────────────────▶│
│ │
│ { token, deviceId, ... } │
│◀──────────────────────────────────│
Pairing
Device A Relay Server Device B
│ │ │
│ POST /api/pair │ │
│ { deviceId, x25519Pub,│ │
│ pairingCode } │ │
│──────────────────────▶│ │
│ │ │
│ │ POST /api/pair/accept │
│ │ { deviceId, x25519Pub,│
│ │ pairingCode } │
│ │◀──────────────────────│
│ │ │
│ { peerX25519PubKey } │ { peerX25519PubKey } │
│◀──────────────────────│──────────────────────▶│
│ │ │
│ derive shared secret │ derive shared secret │
│ (X25519 ECDH) │ (X25519 ECDH) │
Cookie Sync (WebSocket)
Device A Relay Server Device B
│ │ │
│ cookie_sync envelope │ │
│ (signed + encrypted) │ │
│──────────────────────▶│ │
│ │ store encrypted blob │
│ │ │
│ │ forward envelope │
│ │──────────────────────▶│
│ │ │
│ │ decrypt│
│ │ apply │
│ ack │ │
│◀──────────────────────│◀──────────────────────│
Cookie Sync (HTTP Polling)
Device Relay Server
│ │
│ POST /api/cookies │
│ { encrypted blob } │
│───────────────────────────────▶│
│ │
│ GET /api/cookies/updates │
│ ?since=<timestamp> │
│───────────────────────────────▶│
│ │
│ [ encrypted blobs ] │
│◀───────────────────────────────│
Storage
All storage is in-memory. The relay server does not persist data to disk. Restarting the server clears all registrations, pairings, and stored cookies. Devices re-register and re-sync automatically on reconnection.
This is intentional for the current version — the server is a transient relay, not a database. Persistent storage may be added in a future milestone.
Protocol Constants
| Constant | Value | Purpose |
|---|---|---|
PROTOCOL_VERSION |
2.0.0 |
Wire protocol version |
MAX_STORED_COOKIES_PER_DEVICE |
10,000 | Per-device cookie limit |
PAIRING_CODE_LENGTH |
6 digits | Pairing code size |
PAIRING_TTL_MS |
5 minutes | Pairing session expiry |
NONCE_BYTES |
24 | XChaCha20 nonce size |
PING_INTERVAL_MS |
30 seconds | WebSocket keepalive |
PONG_TIMEOUT_MS |
10 seconds | Pong deadline |
POLL_INTERVAL_MS |
5 seconds | HTTP polling default |