···11+# CLAUDE.md
22+33+This file provides guidance to Claude Code when working with code in this repository.
44+55+## Project Overview
66+77+**Feedline** (feedline.at) is a local-first, peer-to-peer RSS reader and podcast client with ATProto integration. Think Google Reader meets BitTorrent - your data lives on your devices, syncs peer-to-peer via the Realm protocol, with optional ATProto for discovery and public sharing.
88+99+**Key Technologies:** TypeScript, Solid.js, Vite, Dexie.js (IndexedDB), better-sqlite3, Express, WebRTC (simple-peer), WebSockets, Zod
1010+1111+**Current Status:** Early development - porting realm protocol from prototype (skypod-node) and establishing architecture patterns. Realm sync works; feed/podcast features are in progress.
1212+1313+## Development Commands
1414+1515+### Starting Development
1616+1717+```bash
1818+npm run dev
1919+```
2020+2121+Starts all development services concurrently using wireit:
2222+2323+- Vite dev server (frontend)
2424+- Backend server (WebSocket + API)
2525+- Type-checking in watch mode
2626+2727+### Running Tests
2828+2929+```bash
3030+npm run test # Run all tests once
3131+npm run start:tests # Run tests in watch mode
3232+```
3333+3434+Tests use Jest with ts-jest. Test files are named `*.spec.ts` or `*.spec.tsx`.
3535+3636+To run a single test file:
3737+3838+```bash
3939+npx jest src/path/to/file.spec.ts
4040+```
4141+4242+### Linting and Type-Checking
4343+4444+```bash
4545+npm run lint # Run ESLint
4646+npm run types # Run TypeScript type-checking
4747+npm run build # Build production frontend
4848+```
4949+5050+### Production
5151+5252+```bash
5353+npm run start:prod # Build and run production server
5454+```
5555+5656+### Version Control
5757+5858+This project uses **Jujutsu** (`jj`) for version control, not Git. Common operations:
5959+6060+```bash
6161+jj status # Check working copy status
6262+jj diff # View changes
6363+jj commit -m "message" # Create commit
6464+jj log # View history
6565+```
6666+6767+## Architecture
6868+6969+### High-Level Vision
7070+7171+Feedline is built on three layers:
7272+7373+1. **Lib Layer** (`#lib/*`) - Pure utilities (async primitives, crypto, schema helpers)
7474+2. **Realm Layer** (`#realm/*`) - Generic P2P action sync protocol (frontend-agnostic)
7575+3. **Feedline Layer** (`#feedline/*`) - RSS/podcast application logic
7676+7777+The Realm protocol enables devices to sync "actions" (state changes) in a causally-ordered, offline-first manner. Feedline builds podcast/RSS features on top by defining domain-specific actions like `feed:add`, `feed:patch`, `entry:analyzed`.
7878+7979+### Module Structure
8080+8181+The codebase uses path aliases configured in both `package.json` (imports) and `tsconfig.json` (paths):
8282+8383+- **`#lib/*`** (`src/lib/`) - Reusable utilities that could be extracted to separate packages
8484+ - `async/` - Async primitives (semaphore, blocking queue, aborts, sleep)
8585+ - `crypto/` - JWT, JWK, cipher utilities
8686+ - `schema/` - Zod schema helpers (brands, JSON validation)
8787+ - `errors.ts`, `types.ts`, `utils.ts` - General utilities
8888+8989+- **`#realm/*`** (`src/realm/`) - P2P action sync protocol (frontend-agnostic)
9090+ - `logical-clock.ts` - Hybrid Logical Clock (HLC) for causal ordering
9191+ - `protocol/` - Message schemas and protocol definitions
9292+ - `schema/` - Realm-specific schemas (actions, identities, timestamps)
9393+ - `client/` - Client-side realm connection management
9494+ - `server/` - Server-side signaling and relay (server acts as peer)
9595+9696+- **`#feedline/*`** (`src/feedline/`) - Application layer (RSS/podcast domain)
9797+ - `client/` - Frontend application
9898+ - `server/` - Backend services
9999+ - `worker/` - Background job processing (planned)
100100+ - `scheduler/` - Feed refresh scheduling (planned)
101101+ - `cmd/` - CLI tools
102102+103103+- **`#spec/*`** (`src/spec/`) - Test helpers and integration test utilities
104104+105105+**Always use these path aliases** for imports across module boundaries. Never use relative paths like `../../../realm/`.
106106+107107+### Data Storage
108108+109109+**Client (Browser):**
110110+111111+- **IndexedDB via Dexie.js**
112112+ - Realm actions database (managed by `#realm/*`)
113113+ - Feedline materialized views (managed by `#feedline/*`, separate DB)
114114+ - Private keys storage (IndexedDB provides secure, per-origin isolation)
115115+116116+**Server (Node.js):**
117117+118118+- **better-sqlite3**
119119+ - Realm databases (one per realm, stores actions)
120120+ - Job queue database (planned)
121121+ - Note: Would prefer node:sqlite, but Nix doesn't have good support yet
122122+123123+These are **completely separate** - realm maintains the action log, feedline materializes a queryable schema from those actions.
124124+125125+### Realm Protocol (The Complex Part)
126126+127127+The realm protocol is the most intricate piece of the codebase. It uses a **Hybrid Logical Clock (HLC)** for causal ordering of events across distributed peers.
128128+129129+**Key Concepts:**
130130+131131+1. **Actions** - Immutable state changes (like Redux actions, but distributed)
132132+2. **HLC Timestamps** - Format: `lc:seconds:counter:identid` - provides total ordering even with clock skew
133133+3. **PULL for Catch-up** - New clients pull complete history from one peer
134134+4. **PUSH for Updates** - All clients push their new actions to all connected peers (with deduplication via knowledge vectors)
135135+136136+**When working on realm code:**
137137+138138+- Ordering is critical - never break HLC timestamp comparison logic
139139+- Actions are append-only, never mutate
140140+- Server is just another peer, no special privileges
141141+- WebSocket is for signaling; WebRTC data channels do the heavy lifting
142142+143143+**Important Files:**
144144+145145+- `src/realm/logical-clock.ts` - HLC implementation
146146+- `src/realm/protocol/` - Message schemas (Zod)
147147+- `src/realm/client/realm.ts` - Client-side connection management
148148+- `src/realm/server/driver-realm.ts` - Server-side realm driver
149149+150150+**Tests exist and should be reviewed** - the server peer implementation works but needs code review for correctness.
151151+152152+### Action-Driven Architecture
153153+154154+Everything in Feedline flows through actions:
155155+156156+```typescript
157157+// Example: User adds a feed
158158+{
159159+ type: 'feed:add',
160160+ payload: {
161161+ url: 'https://example.com/feed.rss',
162162+ private: false
163163+ },
164164+ clock: '0:1735689234:0:alice123',
165165+ actor: 'alice123'
166166+}
167167+```
168168+169169+Actions sync across all devices via realm. Each device materializes its own queryable database from the action log.
170170+171171+**Server as Peer:** The server doesn't have special APIs - it just creates actions like any other peer (e.g., `feed:patch` with new episodes after refresh).
172172+173173+## Design Decisions & Open Questions
174174+175175+### Decided
176176+177177+- **Realm protocol is frontend-agnostic** - No Solid/React dependencies in `#realm/*`
178178+- **Action-driven architecture** - All state changes flow through realm actions
179179+- **Server as peer** - No special server APIs, server creates actions like any client
180180+- **Offline-first** - Clients work independently, sync when connected
181181+- **IndexedDB for client** - Needed for secure private key storage
182182+- **Path aliases** - Always use `#lib/*`, `#realm/*`, `#feedline/*`
183183+- **Wireit for task orchestration** - Handles build dependencies and watch mode
184184+- **Jujutsu for VCS** - Using `jj` instead of `git`
185185+- **Solid.js for UI** - Migrated from Preact to Solid for better performance and modern reactive primitives
186186+187187+### Still Figuring Out
188188+189189+- **Job queue architecture** - Designed (Piscina workers, shared job DB) but not implemented yet
190190+- **WebTorrent integration** - Planned for P2P audio distribution, not started
191191+- **ATProto integration** - Will be used for discovery and optional public sharing, design in progress
192192+- **TypeDoc** - Experimenting with API docs generation, unclear if worth it
193193+- **Server/client split in `#feedline/*`** - May organize feedline layer into server/client subdirs, not decided
194194+195195+## Code Style & Patterns
196196+197197+- **Strict TypeScript** - `strict: true`, `noImplicitAny: true`
198198+- **Zod for validation** - All schemas defined with Zod, used for both types and runtime validation
199199+- **Branded types** - Use Zod brands for semantic types (IdentID, RealmID, etc.)
200200+- **ESLint + Prettier** - Auto-formatting and linting enforced
201201+- **Solid Signals** - Using Solid's built-in reactive primitives (createSignal, createEffect, etc.)
202202+203203+## Working with Claude
204204+205205+**What Claude should know:**
206206+207207+Mainly: **You're a Great Rubber Duck** - Claude should ask questions, suggest patterns, and help think through design decisions; rarely should we jump to implementation of anything, and Claude should assume the user would prefer to do implementation themselves.
208208+209209+1. **This is early-stage** - Architecture is being established, patterns are still evolving
210210+2. **Realm protocol is sacred** - Don't break HLC ordering, action immutability, or sync logic
211211+3. **Layer boundaries matter** - Keep `#lib/*` pure, `#realm/*` frontend-agnostic, `#feedline/*` application-specific
212212+4. **Tests are concrete** - Written after structure is stable to "lock in" decisions
213213+214214+**When suggesting code:**
215215+216216+- Respect the layer boundaries
217217+- Use path aliases
218218+- Follow the action-driven pattern
219219+- Ask if you're unsure whether something belongs in lib/realm/feedline
220220+- Point out potential issues with distributed sync or causal ordering
221221+222222+**Current focus areas:**
223223+224224+- Porting realm protocol from prototype (needs review)
225225+- Establishing feedline action schemas
226226+- Designing job queue for background work (not implemented yet)
227227+- Building out Solid.js UI components
228228+229229+## Testing Strategy
230230+231231+Tests are written **after** the structure is stable - think of them as "pouring concrete" to lock in architectural decisions.
232232+233233+**Test organization:**
234234+235235+- Unit tests alongside source: `foo.ts` → `foo.spec.ts`
236236+- Integration tests in `#spec/*`
237237+- Use `@jest/globals` for Jest functions
238238+- Path aliases work in tests (configured in `jest.config.js`)
239239+240240+**What needs testing:**
241241+242242+- Realm protocol correctness (HLC ordering, sync protocol)
243243+- Action materialization (replaying action logs correctly)
244244+- Server peer behavior (relay, storage, deduplication)
245245+246246+## Roadmap
247247+248248+Based on `tmp-analizer/FEEDLINE_ARCHITECTURE.md`, the planned implementation phases are:
249249+250250+### Phase 1: Foundation (In Progress)
251251+252252+- ✅ Basic realm protocol ported (needs review)
253253+- 🔄 Realm client/server architecture
254254+- ⏳ Action sync verification
255255+- ⏳ Feed schemas
256256+257257+### Phase 2: Feed Tracking (Not Started)
258258+259259+- `feed:track` / `feed:untrack` actions
260260+- Server-side scheduler
261261+- RSS fetcher
262262+- `feed:patch` action creator
263263+264264+### Phase 3: Analysis Integration (Planned)
265265+266266+- Analysis worker service (Piscina-based job queue)
267267+- `entry:analyzed` action
268268+- Static file server
269269+- Audio processing
270270+271271+### Phase 4: WebTorrent P2P (Planned)
272272+273273+- RealmTorrentManager
274274+- Separate WebRTC connections for torrents
275275+- Stream source switching
276276+277277+### Phase 5: ATProto Integration (Planned)
278278+279279+- Publisher service (opt-in)
280280+- Magnet discovery
281281+- Jetstream subscriber for aggregation
282282+283283+### Phase 6: Self-Hosted Stack (Planned)
284284+285285+- Container images
286286+- macOS menu bar app
287287+- Linux Flatpak
288288+289289+## Important Files
290290+291291+**Realm Protocol:**
292292+293293+- `src/realm/logical-clock.ts` - HLC implementation
294294+- `src/realm/schema/timestamp.ts` - Timestamp schemas
295295+- `src/realm/client/realm.ts` - Client realm connection
296296+- `src/realm/server/driver-realm.ts` - Server realm driver
297297+298298+**Utilities:**
299299+300300+- `src/lib/async/` - Async primitives (critical for coordination)
301301+- `src/lib/crypto/` - JWT/JWK for authentication
302302+- `src/lib/schema/` - Zod helpers
303303+304304+**Tests:**
305305+306306+- `src/realm/logical-clock.spec.ts` - HLC tests (important!)
307307+- `src/realm/server/` - Server peer tests (need review)
308308+309309+## ATProto Integration Notes
310310+311311+Feedline will use ATProto (feedline.at domain) for:
312312+313313+- **Discovery** - Find popular feeds, episode magnets
314314+- **Sharing** - Optionally publish your subscriptions/magnets
315315+- **Privacy-first** - Everything private by default, ATProto is opt-in
316316+317317+ATProto is **not** used for:
318318+319319+- Primary data storage (all local-first)
320320+- Sync protocol (realm handles that)
321321+- Authentication between your devices (realm uses JWTs)
322322+323323+Think of ATProto as a public discovery layer on top of a fully private P2P foundation.
+62
README.md
···11+# Feedline
22+33+easily self-hosted, local-first RSS reader and podcast client with p2p sync, and optional ATProto based social discovery.
44+55+- subscriptions, read/play state and other actions sync between devices p2p
66+- media can be downloaded with analysis for skipping silence, and shared via public or private torrent swarm
77+- optional atproto integration allows easy on-boarding, and sharing subscriptions/favorites/etc through an open social network
88+99+### development
1010+1111+- node.js 24+
1212+- npm or pnpm
1313+1414+```bash
1515+npm install
1616+npm run dev
1717+# - frontend at `http://localhost:4000` (or configured port)
1818+# - backend server at `http://localhost:4001`, `ws://localhost:4001/stream`
1919+2020+npm test
2121+npm run types
2222+npm run lint -- --fix
2323+2424+# production (heh)
2525+npm run build
2626+npm run start:prod
2727+```
2828+2929+## status: **early**
3030+3131+the core realm protocol is working, but feed/podcast features are still being built.
3232+don't use this yet.
3333+3434+## self-hosting
3535+3636+feedline is designed to be self-hosted:
3737+3838+1. **hosted** - run the web app from https://app.feedline.at, and we use the following:
3939+ - CORS proxy
4040+ - reader-mode proxy
4141+ - audio file downloads for analysis (when not marked private)
4242+2. **home server** - run a local server for your devices (via Docker/Podman)
4343+ - should support `mdns`, client connects, gets PWA, server does syncing
4444+ - "discovery" and other social features would still come from upstream if that's desired
4545+4646+## ATProto integration
4747+4848+feedline integrates with ATProto for:
4949+5050+- easy cross-device **bootstrapping**
5151+ - if both devices are signed in through ATProto OAuth, we don't have to worry about invitation
5252+5353+- **social & discovery**
5454+ - sharing subscriptions means we can track common ones, trending, all that good stuff
5555+ - sharing "shares" or "favorites" or whatever gives nice google-reader vibes
5656+ - share to bluesky / some sort of native commenting or whatever would be easy/useful
5757+5858+ATProto should **never be required** - offline first with a self-hosted server for fetching should always work.
5959+6060+# license
6161+6262+AGPL