Files
bitvid/CLAUDE.md
T

14 KiB

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

# 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

# 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
node scripts/agent/validate-events.mjs Validate Nostr event schemas

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:

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:

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:

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:

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

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:

// 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

# 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

# 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

// 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

{
  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