# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview **Skypod** is an offline-first, peer-to-peer RSS and podcast Progressive Web App (PWA). The application enables users to manage subscriptions and listening history across multiple devices with direct peer-to-peer synchronization (no central server for data). **Key Technologies:** TypeScript, Preact, Vite, Dexie.js (IndexedDB), Express, WebRTC (simple-peer), WebSockets, Zod ## Development Commands ### Starting Development ```bash npm run dev ``` Starts all development services concurrently using wireit: - Vite dev server at `http://127.0.0.1:4000` (frontend) - Backend server at `http://127.0.0.1:4001` (WebSocket + API) - Live type-checking and linting in watch mode ### Running Tests ```bash npm run test # Run all tests once npm run start:tests # Run tests in watch mode ``` Tests use Jest with ts-jest. Test files are named `*.spec.ts` or `*.spec.tsx`. To run a single test file: ```bash npx jest src/path/to/file.spec.ts ``` ### Linting and Type-Checking ```bash npm run lint # Run ESLint npm run types # Run TypeScript type-checking npm run build # Build production frontend ``` ### Production ```bash npm run start:prod # Build and run production server ``` ### Git Hooks Pre-commit hook runs type-checking and linting automatically. Enable with: ```bash git config core.hooksPath .githooks ``` ## Architecture ### Module Structure The codebase is organized into four main directories with path aliases: - **`#client/*`** (`src/client/`) - Preact frontend application - **`#server/*`** (`src/server/`) - Node.js Express backend - **`#common/*`** (`src/common/`) - Shared code (protocol, crypto, utilities) - **`#skypod/*`** (`src/skypod/`) - Domain-specific schemas and actions These path aliases are configured in both `package.json` (imports) and `tsconfig.json` (paths). Always use these aliases for absolute imports. ### Frontend Client (`src/client/`) Built with Preact and Vite. Key subdirectories: - **`realm/`** - Core P2P connection management - `service-connection.ts` - Main WebSocket connection to signaling server - `service-connection-peer.ts` - WebRTC peer connection management - `service-connection-sync.ts` - Sync protocol coordinator - `context-connection.tsx` - React context for connection state - `context-identity.tsx` - React context for user identity - **`root/`** - Database setup (Dexie/IndexedDB) - **`skypod/`** - Application-specific components and workers - **`components/`** - Reusable UI components ### Backend Server (`src/server/`) Lightweight Express server with two primary functions: 1. **WebSocket Signaling Server** (`routes-socket/`) - Enables WebRTC connection establishment between peers 2. **API Proxy** (`routes-api/`) - Fetches and parses external RSS feeds The server maintains realm state (connected peers) but does NOT store user data - all data lives on clients. ### Common Library (`src/common/`) Shared isomorphic code: - **`protocol/`** - P2P protocol definitions - `logical-clock.ts` - Hybrid Logical Clock (HLC) implementation for causal ordering - `messages.ts` - Zod schemas for all WebSocket/WebRTC messages - `brands.ts` - Branded types for identity IDs - **`crypto/`** - JWT and JWK utilities for authentication - **`async/`** - Utilities (semaphore, blocking queue, sleep, etc.) - **`schema/`** - Common Zod schemas - `socket.ts` - Shared WebSocket message handling - `errors.ts` - Custom error types (ProtocolError) ### Domain Layer (`src/skypod/`) Application-specific schemas and action definitions for the podcast/RSS domain. ## Synchronization Protocol The P2P sync protocol is the core architectural component. It uses a **Hybrid Logical Clock (HLC)** for causal ordering of events and follows a specific pattern: ### PULL for Catch-up When a new client connects or comes online after being offline, it PULLS the complete action history from a single deterministically chosen `syncPartner`. This efficiently brings the client up to date. ### PUSH for Updates All clients PUSH their own new or offline-generated changes to all connected peers. This is NOT a simple broadcast - each client sends a *tailored* set of missing actions to each peer based on a handshake where "knowledge vectors" (last known timestamps) are exchanged. ### Key Components - **Hybrid Logical Clock** (`src/common/protocol/logical-clock.ts`) - Provides total ordering across distributed events even with clock skew - **Message Schemas** (`src/common/protocol/messages.ts`) - Defines all protocol messages using Zod - **Connection Services** (`src/client/realm/service-connection-*.ts`) - Implements the sync protocol When working on sync-related code, understand that ordering is critical - the HLC timestamp format is `lc:seconds:counter:identid` where each component is used for deterministic ordering. ## Build System Uses **wireit** for dependency-aware task orchestration. All build tasks are defined in `package.json` under the `wireit` key. - Tasks automatically track file dependencies and outputs - Service tasks (prefixed with `run:`) run continuously in watch mode - Compound tasks like `start:dev` orchestrate multiple services ## Testing Conventions - Test files use the pattern `*.spec.ts` or `*.spec.tsx` - Tests use `@jest/globals` imports for Jest functions - Use `#common/*`, `#client/*`, `#server/*` aliases in tests (configured in `jest.config.js`) - Tests support ESM and handle Preact/JSX via ts-jest ## Code Style - **ESLint + Prettier** enforce consistent style - **Strict TypeScript** with `strict: true` and `noImplicitAny: true` - **Zod** for runtime validation and schema definitions - **JSX** configured for Preact (`jsxImportSource: "preact"`) ## Important Context for AI Assistance 1. **Path Aliases Are Critical** - Always use `#common/*`, `#client/*`, `#server/*`, `#skypod/*` for imports, never relative paths across module boundaries 2. **Sync Protocol Is Complex** - The HLC-based synchronization is the most intricate part of the codebase. Changes to sync logic require careful consideration of causal ordering and distributed consistency 3. **Client Is Offline-First** - All user data lives in IndexedDB via Dexie. The server is stateless regarding user data (only maintains ephemeral realm/peer state) 4. **WebRTC for P2P** - Peers communicate directly via WebRTC data channels (using simple-peer). The WebSocket connection to the server is only used for signaling and as a fallback broadcast mechanism 5. **Zod Schemas Define Protocol** - All message formats are defined as Zod schemas in `src/common/protocol/`. These schemas are the single source of truth for protocol messages