feat: implement M4 self-hosting, Docker & documentation
- 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>
This commit is contained in:
139
docs/architecture.md
Normal file
139
docs/architecture.md
Normal file
@@ -0,0 +1,139 @@
|
||||
# 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.cookies` API.
|
||||
- **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 |
|
||||
Reference in New Issue
Block a user