diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..1dd01dc2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,466 @@ +# CLAUDE.md — AI Assistant Guide for bitvid + +This document provides context and guidance for AI assistants working with the bitvid codebase. It explains the project structure, development workflows, conventions, and key architectural decisions. + +--- + +## Project Overview + +**bitvid** is a decentralized video sharing platform built on the Nostr protocol. It operates as a static client — no backend server, no custody of user keys, no server-side signing. All authentication and state management happens client-side or via connected Nostr signers. + +### Key Technologies +- **Nostr**: Decentralized identity and event protocol (NIP-07, NIP-33, NIP-44, NIP-46, NIP-51, NIP-71, etc.) +- **WebTorrent**: P2P video streaming fallback +- **Tailwind CSS**: Utility-first styling with custom design tokens +- **Playwright**: E2E and visual regression testing +- **Node.js 20+**: Development environment + +### License +GPL-3.0-or-later (see `LICENSE`) + +--- + +## Codebase Structure + +``` +bitvid/ +├── js/ # Main application JavaScript +│ ├── app.js # Main orchestrator (bitvidApp) +│ ├── nostr/ # Nostr protocol implementation +│ │ ├── client.js # Core Nostr client with signer adapters +│ │ ├── nip71.js # NIP-71 video note parsing +│ │ ├── sessionActor.js # Anonymous session actors for telemetry +│ │ └── adapters/ # NIP-07, NIP-46, nsec signer adapters +│ ├── services/ # Business logic services +│ │ └── playbackService.js # Video playback orchestration +│ ├── ui/ # UI components and controllers +│ ├── state/ # State management (cache.js) +│ ├── utils/ # Utility functions +│ ├── constants.js # Feature flags and WSS trackers +│ ├── config.js # Re-exports from instance config +│ ├── magnetUtils.js # Magnet URI helpers +│ ├── nostrEventSchemas.js # Event schema definitions (source of truth) +│ └── nostrClientFacade.js # Main Nostr client entry point +├── config/ +│ └── instance-config.js # Deployment configuration +├── css/ +│ ├── tokens.css # Design token definitions +│ ├── tailwind.source.css # Tailwind input file +│ └── tailwind.generated.css # Generated (gitignored) +├── tests/ # Test suites +│ ├── unit/ # Unit tests +│ ├── e2e/ # End-to-end tests +│ ├── visual/ # Visual regression tests +│ ├── nostr/ # Nostr-specific tests +│ └── moderation/ # Moderation feature tests +├── docs/ # Documentation +│ ├── nostr-event-schemas.md # Event schema reference +│ ├── playback-fallback.md # Playback architecture +│ ├── moderation/ # Moderation system docs +│ └── nips/ # NIP reference documentation +├── views/ # HTML view templates +├── components/ # HTML component templates +├── content/ # Markdown content (docs, roadmap) +├── torrent/ # WebTorrent integration +├── scripts/ # Build and utility scripts +└── .github/workflows/ # CI/CD configuration +``` + +--- + +## Essential Files to Know + +| File | Purpose | +|------|---------| +| `AGENTS.md` | AI agent guide with architectural rules and mission | +| `KNOWN_ISSUES.md` | Pre-existing test failures and environmental quirks | +| `CONTRIBUTING.md` | Developer setup and contribution guidelines | +| `js/app.js` | Main application orchestrator (`bitvidApp`) | +| `js/nostr/client.js` | Core Nostr client with signer registry | +| `js/nostrEventSchemas.js` | **Source of truth** for all Nostr event schemas | +| `js/constants.js` | Feature flags and runtime configuration | +| `config/instance-config.js` | Deployment-specific settings | +| `docs/nostr-event-schemas.md` | Event schema documentation | + +--- + +## Development Workflow + +### Initial Setup + +```bash +# Clone and install +git clone https://github.com/PR0M3TH3AN/bitvid.git +cd bitvid +npm ci # Use ci for exact lockfile deps + +# Build (generates Tailwind CSS) +npm run build + +# Start local server +python -m http.server 8000 +# or +npx serve +``` + +### Before Every PR + +```bash +# Required checks +npm run format # Format CSS/HTML/MD +npm run lint # All lint checks +npm run test:unit # Or use shards for faster feedback + +# Recommended for UI changes +npm run test:visual +``` + +### Available Scripts + +| Script | Purpose | +|--------|---------| +| `npm run build` | Full build (Tailwind + dist) | +| `npm run build:css` | Regenerate Tailwind only | +| `npm run format` | Format with Prettier | +| `npm run lint` | Run all linting checks | +| `npm run lint:css` | CSS token validation | +| `npm run lint:inline-styles` | Block inline styles | +| `npm run lint:tokens` | Design token enforcement | +| `npm run test:unit` | Full unit test suite | +| `npm run test:unit:shard1` | Shard 1/3 (faster local dev) | +| `npm run test:unit:shard2` | Shard 2/3 | +| `npm run test:unit:shard3` | Shard 3/3 | +| `npm run test:dm:unit` | Direct message unit tests | +| `npm run test:dm:integration` | DM integration tests | +| `npm run test:e2e` | Headless E2E tests | +| `npm run test:visual` | Visual regression tests | + +--- + +## CI/CD Pipeline + +The CI workflow (`.github/workflows/ci.yml`) runs on every push to `main` and all PRs: + +| Job | Timeout | Description | +|-----|---------|-------------| +| `build` | 45 min | Lint, build, visual regression | +| `unit-tests` | 30 min | Sharded unit tests (3 shards) | +| `dm-unit-tests` | 20 min | Direct message unit tests | +| `dm-integration-tests` | 30 min | DM integration tests | +| `e2e-headless` | 45 min | Playwright E2E tests | + +**Concurrency**: `cancel-in-progress: true` — new commits cancel running jobs. + +--- + +## Code Conventions + +### Logging + +**Always use the logger utility**, never call `console.*` directly: + +```javascript +import { logger } from './utils/logger.js'; + +logger.user.error('User-facing error'); // Production visible +logger.dev.log('Debug info'); // Dev mode only (IS_DEV_MODE) +``` + +### Styling Rules + +1. **Token-first**: Use design tokens from `css/tokens.css`, never raw hex colors +2. **No inline styles**: `style=` attributes and `element.style` are blocked by lint +3. **Semantic tokens**: Use `bg`, `text`, `accent`, `border`, `danger`, etc. +4. **Theme scopes**: Toggle themes via `data-theme` attribute, not code + +### Nostr Event Schemas + +All Nostr events must be defined in `js/nostrEventSchemas.js`: + +```javascript +import { buildVideoPostEvent, NOTE_TYPES } from './nostrEventSchemas.js'; + +const event = buildVideoPostEvent({ + pubkey: hexPubkey, + created_at: Math.floor(Date.now() / 1000), + dTagValue: 'video-id', + content: { + version: 3, + title: 'My Video', + videoRootId: 'video-id', + url: 'https://example.com/video.mp4' + } +}); +``` + +### Magnet Handling + +**Critical rules** — magnets are fragile: + +```javascript +import { safeDecodeMagnet, normalizeAndAugmentMagnet } from './magnetUtils.js'; + +// Always decode first +const raw = safeDecodeMagnet(userInput); + +// Then normalize with hints +const enriched = normalizeAndAugmentMagnet(raw, { wsHint, xsHint }); + +// NEVER use new URL() on magnets — it corrupts hashes! +``` + +- WSS trackers only (from `js/constants.js`) +- Persist raw magnet strings, decode only at playback +- Include HTTPS `ws=`/`xs=` hints when available + +### Architecture Pattern + +**Separation of concerns**: + +- `bitvidApp` (js/app.js) — Orchestrator, manages app-wide state +- UI Controllers — Handle specific UI components (e.g., `ProfileModalController`) +- Services — Business logic (e.g., `playbackService.js`) +- State — Central truth in `js/state/cache.js` + +**Data flow**: +1. User action triggers controller event handler +2. Controller invokes callback provided by `bitvidApp` +3. `bitvidApp` executes logic, updates central state +4. `bitvidApp` notifies controller to re-render + +--- + +## Feature Flags + +Feature flags in `js/constants.js` gate experimental behavior: + +| Flag | Default | Purpose | +|------|---------|---------| +| `URL_FIRST_ENABLED` | Depends on config | Try URL before magnet | +| `FEATURE_WATCH_HISTORY_V2` | true | Watch history system | +| `FEATURE_PUBLISH_NIP71` | false | NIP-71 video publishing | +| `FEATURE_SEARCH_FILTERS` | Dev mode only | Advanced search | +| `FEATURE_TRUST_SEEDS` | true | Baseline trust seeds | + +Toggle at runtime: +```javascript +import { setUrlFirstEnabled } from './constants.js'; +setUrlFirstEnabled(false); // Disable URL-first playback +``` + +--- + +## Nostr Integration + +### Key NIPs Used + +| NIP | Purpose | +|-----|---------| +| NIP-07 | Browser extension signing | +| NIP-33 | Parameterized replaceable events (videos) | +| NIP-04 | Legacy DM encryption | +| NIP-44 | Modern encryption | +| NIP-46 | Remote signing | +| NIP-51 | Lists (mute, admin) | +| NIP-56 | Moderation reports | +| NIP-71 | Video events | + +### Signer Adapters + +```javascript +import { nostrClient } from './nostrClientFacade.js'; +import { setActiveSigner } from './nostr/client.js'; + +// Register a signer +setActiveSigner({ + pubkey, + signEvent: (event) => signer.sign(event), + nip04Encrypt: (target, text) => signer.nip04.encrypt(target, text), + nip04Decrypt: (actor, cipher) => signer.nip04.decrypt(actor, cipher), + // Optional: nip44Encrypt, nip44Decrypt +}); +``` + +### NIP-33 Addressing (Important!) + +Video events are addressed by `kind + pubkey + d-tag`: + +```javascript +// CORRECT: Use the d-tag for addressing +const dTag = event.tags.find(t => t[0] === 'd')?.[1]; + +// WRONG: Don't use logical IDs like videoRootId for relay lookups +``` + +--- + +## Testing Guidelines + +### Before Starting Work + +1. Read `KNOWN_ISSUES.md` for pre-existing failures +2. Run relevant test shard to verify baseline + +### Running Tests + +```bash +# Fast feedback during development +npm run test:unit:shard1 + +# Full suite before PR +npm run test:unit + +# Visual changes +npm run test:visual + +# DM features +npm run test:dm:unit && npm run test:dm:integration + +# E2E journeys +npm run test:e2e +``` + +### Test Artifacts + +Failed visual tests store artifacts in `artifacts/test-results/`: +- Screenshots +- Videos +- Traces + +View with: `./scripts/show-artifacts.sh` + +--- + +## Common Tasks + +### Adding a New Nostr Event Type + +1. Define schema in `js/nostrEventSchemas.js` +2. Add builder function +3. Document in `docs/nostr-event-schemas.md` +4. Add tests + +### Creating UI Components + +1. Create controller in `js/ui/` +2. Wire callbacks through `bitvidApp` +3. Read state via provided getters, never directly +4. Use design tokens for styling + +### Modifying Playback Behavior + +1. Check `js/services/playbackService.js` for stream handling +2. Update `js/constants.js` feature flags if needed +3. Test with both URL and magnet sources +4. Document rollback steps in PR + +--- + +## Troubleshooting + +### Build Issues + +```bash +# Browserslist warning +npx update-browserslist-db@latest + +# Tailwind not regenerating +rm css/tailwind.generated.css && npm run build:css +``` + +### Test Hangs + +- Unit tests may hang after completion — use `Ctrl+C` +- Use shards for faster local runs +- Set `UNIT_TEST_TIMEOUT_MS=120000` for slow tests + +### Lint Failures + +- **Inline styles**: Move to CSS classes or tokens +- **Hex colors**: Use design tokens +- **Design tokens**: Use `theme()` or existing utilities + +--- + +## Release Channels + +- **Main**: Production track — preserve UX, atomic commits, feature flags default off +- **Unstable**: Experimentation — gate risky features behind flags + +### Emergency Response + +If a change breaks playback or magnet handling: +1. Revert immediately +2. Note rollback in PR +3. Document in AGENTS.md + +--- + +## Key Documentation + +| Document | What It Covers | +|----------|---------------| +| `AGENTS.md` | AI agent rules, mission, architectural decisions | +| `CONTRIBUTING.md` | Setup, PR process, DCO | +| `KNOWN_ISSUES.md` | Pre-existing test failures | +| `docs/nostr-event-schemas.md` | Event definitions | +| `docs/playback-fallback.md` | URL-first strategy | +| `docs/moderation/README.md` | Moderation system | +| `docs/logging.md` | Logger usage | + +--- + +## Quick Reference + +### Import Patterns + +```javascript +// Nostr client +import { nostrClient } from './nostrClientFacade.js'; + +// View events (analytics) +import { recordVideoView } from './nostrViewEventsFacade.js'; + +// Watch history +import { updateWatchHistoryListWithDefaultClient } from './nostrWatchHistoryFacade.js'; + +// Event building +import { buildVideoPostEvent } from './nostrEventSchemas.js'; + +// Feature flags +import { URL_FIRST_ENABLED, setUrlFirstEnabled } from './constants.js'; + +// Logger +import { logger } from './utils/logger.js'; +``` + +### Content Schema v3 + +```javascript +{ + version: 3, + title: string, // Required + url?: string, // HTTPS video URL + magnet?: string, // WebTorrent magnet + thumbnail?: string, + description?: string, + mode: 'live' | 'dev', + isPrivate: boolean, + deleted: boolean, + videoRootId: string // Stable identifier +} +``` + +Validation: Must have `title` + at least one of `url` or `magnet`. + +--- + +## Final Notes + +1. **Read AGENTS.md first** — it contains mission-critical architectural decisions +2. **Check KNOWN_ISSUES.md** — avoid investigating pre-existing failures +3. **Use sharded tests** — faster feedback during development +4. **Never commit generated CSS** — `css/tailwind.generated.css` is gitignored +5. **Token-first styling** — no raw colors, no inline styles +6. **Keep magnets raw** — decode only at playback time +7. **Document rollback steps** — especially for playback changes