feat: implement M2 Chrome browser extension
Build the CookieBridge Chrome extension (Manifest V3) with: - Background service worker: cookie monitoring via chrome.cookies.onChanged, WebSocket connection to relay server with auto-reconnect, HTTP polling fallback, device registration and pairing flow - Browser-compatible crypto: libsodium-wrappers-sumo for XChaCha20-Poly1305 encryption, Ed25519 signing, X25519 key exchange (mirrors server's sodium-native API) - Popup UI: device registration, connection status indicator (gray/blue/ green/red), cookie/device/sync stats, one-click current site sync, whitelist quick-add, device pairing with 6-digit code - Options page: server URL config, connection mode (auto/WS/polling), poll interval slider, auto-sync toggle, domain whitelist/blacklist management, paired device list, key export/import, data clearing - Sync engine: LWW conflict resolution with Lamport clocks (same as server), bidirectional cookie sync with all paired peers, echo suppression to prevent sync loops - Badge management: icon color reflects state (gray=not logged in, blue=connected, green=syncing with count, red=error) - Build system: esbuild bundling for Chrome 120+, TypeScript with strict mode, clean type checking Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
83
extension/src/lib/protocol.ts
Normal file
83
extension/src/lib/protocol.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Protocol types — mirrors the server's protocol/spec.ts for use in the extension.
|
||||
*/
|
||||
|
||||
export const PROTOCOL_VERSION = "2.0.0";
|
||||
export const MAX_STORED_COOKIES_PER_DEVICE = 10_000;
|
||||
export const PAIRING_CODE_LENGTH = 6;
|
||||
export const PAIRING_TTL_MS = 5 * 60 * 1000;
|
||||
export const NONCE_BYTES = 24;
|
||||
export const PING_INTERVAL_MS = 30_000;
|
||||
export const PONG_TIMEOUT_MS = 10_000;
|
||||
export const POLL_INTERVAL_MS = 5_000;
|
||||
|
||||
export const MESSAGE_TYPES = {
|
||||
COOKIE_SYNC: "cookie_sync",
|
||||
COOKIE_DELETE: "cookie_delete",
|
||||
ACK: "ack",
|
||||
PING: "ping",
|
||||
PONG: "pong",
|
||||
ERROR: "error",
|
||||
} as const;
|
||||
|
||||
export type MessageType = (typeof MESSAGE_TYPES)[keyof typeof MESSAGE_TYPES];
|
||||
|
||||
export interface Envelope {
|
||||
type: MessageType;
|
||||
from: string;
|
||||
to: string;
|
||||
nonce: string;
|
||||
payload: string;
|
||||
timestamp: string;
|
||||
sig: string;
|
||||
}
|
||||
|
||||
export interface CookieEntry {
|
||||
domain: string;
|
||||
name: string;
|
||||
value: string;
|
||||
path: string;
|
||||
secure: boolean;
|
||||
httpOnly: boolean;
|
||||
sameSite: "strict" | "lax" | "none";
|
||||
expiresAt: string | null;
|
||||
}
|
||||
|
||||
export interface CookieSyncPayload {
|
||||
action: "set" | "delete";
|
||||
cookies: CookieEntry[];
|
||||
lamportTs: number;
|
||||
}
|
||||
|
||||
export interface EncryptedCookieBlob {
|
||||
id: string;
|
||||
deviceId: string;
|
||||
domain: string;
|
||||
cookieName: string;
|
||||
path: string;
|
||||
nonce: string;
|
||||
ciphertext: string;
|
||||
lamportTs: number;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface DeviceRegisterRequest {
|
||||
deviceId: string;
|
||||
name: string;
|
||||
platform: string;
|
||||
encPub: string;
|
||||
}
|
||||
|
||||
export interface DeviceInfo {
|
||||
deviceId: string;
|
||||
name: string;
|
||||
platform: string;
|
||||
encPub: string;
|
||||
token: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface PairingResult {
|
||||
peerDeviceId: string;
|
||||
peerX25519PubKey: string;
|
||||
}
|
||||
Reference in New Issue
Block a user