1# plyr.fm documentation 2 3this directory contains all documentation for the plyr.fm project. 4 5## documentation index 6 7### authentication & security 8- **[authentication.md](./authentication.md)** - secure cookie-based authentication, HttpOnly cookies, XSS protection, environment architecture, migration from localStorage 9 10### frontend 11- **[state-management.md](./frontend/state-management.md)** - global state management with Svelte 5 runes (toast notifications, tracks cache, upload manager, queue management, liked tracks, preferences, localStorage persistence) 12- **[toast-notifications.md](./frontend/toast-notifications.md)** - user feedback system for async operations with smooth transitions and auto-dismiss 13- **[queue.md](./frontend/queue.md)** - music queue management with server sync 14- **[keyboard-shortcuts.md](./frontend/keyboard-shortcuts.md)** - global keyboard shortcuts with context-aware filtering (Q for queue toggle, patterns for adding new shortcuts) 15 16### backend 17- **[configuration.md](./backend/configuration.md)** - backend configuration and environment setup 18- **[liked-tracks.md](./backend/liked-tracks.md)** - ATProto-backed track likes with error handling and consistency guarantees 19- **[streaming-uploads.md](./backend/streaming-uploads.md)** - SSE-based progress tracking for file uploads with fire-and-forget pattern 20- **[transcoder.md](./backend/transcoder.md)** - rust-based HTTP service for audio format conversion (ffmpeg integration, authentication, fly.io deployment) 21 22### deployment 23- **[environments.md](./deployment/environments.md)** - staging vs production environments, automated deployment via GitHub Actions, CORS, secrets management 24- **[database-migrations.md](./deployment/database-migrations.md)** - automated migration workflow via fly.io release commands, alembic usage, safety procedures 25 26### tools 27- **[logfire.md](./tools/logfire.md)** - SQL query patterns for Logfire DataFusion database, finding exceptions, analyzing performance bottlenecks 28- **[neon.md](./tools/neon.md)** - Neon Postgres database management and best practices 29- **[pdsx.md](./tools/pdsx.md)** - ATProto PDS explorer and debugging tools 30 31### local development 32- **[setup.md](./local-development/setup.md)** - complete local development setup guide 33 34## ATProto integration 35 36plyr.fm uses a hybrid storage model: 37- audio files stored in cloudflare R2 (scalable, CDN-backed) 38- metadata stored as ATProto records on user's PDS (decentralized, user-owned) 39- local database indexes for fast queries 40 41key namespaces: 42- `fm.plyr.track` - track metadata (title, artist, album, features, image, audio file reference) 43- `fm.plyr.like` - user likes on tracks (subject references track URI) 44 45## quick start 46 47### current state 48 49plyr.fm is fully functional with: 50- ✅ OAuth 2.1 authentication (ATProto) 51- ✅ secure cookie-based sessions (HttpOnly, XSS protection) 52- ✅ R2 storage for audio files (cloudflare CDN) 53- ✅ track upload with streaming (prevents OOM) 54- ✅ ATProto record creation (fm.plyr.track namespace) 55- ✅ music player with queue management 56- ✅ liked tracks (fm.plyr.like namespace) 57- ✅ artist pages and track discovery 58- ✅ share buttons across track, album, and artist detail pages for quick copy-to-clipboard links 59- ✅ modular audio player with dedicated subcomponents for metadata, transport, progress, and volume controls 60- ✅ image uploads for track artwork 61- ✅ audio transcoding service (rust + ffmpeg) 62- ✅ server-sent events for upload progress 63- ✅ toast notifications 64- ✅ user preferences (accent color, auto-play) 65- ✅ keyboard shortcuts (Q for queue toggle) 66 67### local development 68 69see **[local-development/setup.md](./local-development/setup.md)** for complete setup instructions. 70 71quick start: 72```bash 73# backend 74uv run uvicorn backend.main:app --reload --host 0.0.0.0 --port 8001 75 76# frontend 77cd frontend && bun run dev 78 79# transcoder (optional) 80cd transcoder && just run 81``` 82 83### deployment 84 85see **[deployment/environments.md](./deployment/environments.md)** for details on: 86- staging vs production environments 87- automated deployment via GitHub Actions 88- environment variables and secrets 89 90see **[deployment/database-migrations.md](./deployment/database-migrations.md)** for: 91- migration workflow and safety procedures 92- alembic usage and testing 93 94## architecture decisions 95 96### why R2 instead of PDS blobs? 97 98PDS blobs are designed for smaller files like images. audio files are: 99- larger (5-50MB per track) 100- require streaming 101- benefit from CDN distribution 102 103R2 provides: 104- scalable storage 105- free egress to cloudflare CDN 106- simple HTTP URLs 107- cost-effective (~$0.015/GB/month) 108 109### why fm.plyr namespace? 110 111plyr.fm uses `fm.plyr.*` as the ATProto namespace: 112- `fm.plyr.track` for track metadata 113- `fm.plyr.like` for user likes 114 115this is a domain-specific lexicon that allows: 116- clear ownership and governance 117- faster iteration without formal approval 118- alignment with the plyr.fm brand 119 120### why hybrid storage? 121 122storing metadata on ATProto provides: 123- user data sovereignty (users own their catalog) 124- decentralization (no single point of failure) 125- portability (users can move to another client) 126 127storing audio on R2 provides: 128- performance (fast streaming via CDN) 129- scalability (handles growth) 130- cost efficiency (cheaper than PDS blobs) 131 132### why separate transcoder service? 133 134the transcoder runs as a separate rust service because: 135- ffmpeg operations are CPU-intensive and can block event loop 136- rust provides better performance for media processing 137- isolation prevents transcoding from affecting API latency 138- can scale independently from main backend 139 140## testing 141 142plyr.fm uses pytest for backend testing: 143 144```bash 145# run all tests 146just test 147 148# run specific test file 149just test tests/api/test_track_likes.py 150 151# run with verbose output 152just test -v 153``` 154 155test categories: 156- API endpoints (`tests/api/`) 157- storage backends (`tests/storage/`) 158- ATProto integration (`tests/test_atproto.py`) 159- audio format validation (`tests/test_audio_formats.py`) 160 161see [`tests/CLAUDE.md`](../tests/CLAUDE.md) for testing guidelines. 162 163## troubleshooting 164 165### R2 upload fails 166 167``` 168error: failed to upload to R2 169``` 170 171**check**: 172- R2 credentials in `.env` 173- bucket exists and is accessible 174- account ID is correct 175 176### ATProto record creation fails 177 178``` 179error: failed to create atproto record 180``` 181 182**check**: 183- OAuth session is valid (not expired) 184- user has write permissions 185- PDS is accessible 186- record format is valid 187 188### audio won't play 189 190``` 191404: audio file not found 192``` 193 194**check**: 195- `STORAGE_BACKEND` matches actual storage 196- R2 bucket has public read access 197- file_id matches database record 198 199## monitoring 200 201### key metrics to track 202 2031. **upload success rate** 204 - total uploads attempted 205 - successful R2 uploads 206 - successful record creations 207 2082. **storage costs** 209 - total R2 storage (GB) 210 - monthly operations count 211 - estimated cost 212 2133. **playback metrics** 214 - tracks played 215 - average stream duration 216 - errors/failures 217 218### logging 219 220add structured logging for debugging: 221 222```python 223import structlog 224 225logger = structlog.get_logger() 226 227logger.info( 228 "track_uploaded", 229 track_id=track.id, 230 r2_url=r2_url, 231 atproto_uri=atproto_uri, 232) 233``` 234 235## security considerations 236 237### audio file access 238 239**current**: R2 URLs are public (anyone with URL can access) 240 241**acceptable for MVP** because: 242- music is meant to be shared 243- no sensitive content 244- URL guessing is impractical (content-based hashes) 245 246**future enhancement**: signed URLs with expiration 247 248### record ownership 249 250**enforced by ATProto**: only user with valid OAuth session can create records in their repo 251 252**enforced by backend**: tracks are associated with `artist_did` and only owner can delete 253 254### rate limiting 255 256**recommended**: limit uploads to prevent abuse 257- 10 uploads per hour per user 258- 100MB total per hour per user 259 260## cost estimates 261 262current monthly costs (~$15-20/month): 263- fly.io backend: $5-10/month (shared-cpu-1x, 256MB RAM) 264- fly.io transcoder: $5-10/month (shared-cpu-1x, 256MB RAM) 265- neon postgres: free tier (0.5GB storage, 3GB data transfer) 266- cloudflare R2: ~$0.16/month (6 buckets: audio-dev, audio-stg, audio-prod, images-dev, images-stg, images-prod) 267- cloudflare pages: free (frontend hosting) 268 269R2 storage scaling (audio + images): 270- 1,000 tracks: ~$0.16/month 271- 10,000 tracks: ~$1.58/month 272- 100,000 tracks: ~$15.81/month 273 274## references 275 276### ATProto documentation 277 278- [repository spec](https://atproto.com/specs/repository) 279- [lexicon spec](https://atproto.com/specs/lexicon) 280- [data model](https://atproto.com/specs/data-model) 281- [OAuth 2.1](https://atproto.com/specs/oauth) 282 283### cloudflare documentation 284 285- [R2 overview](https://developers.cloudflare.com/r2/) 286- [R2 pricing](https://developers.cloudflare.com/r2/pricing/) 287- [S3 compatibility](https://developers.cloudflare.com/r2/api/s3/) 288 289### plyr.fm project files 290 291- project instructions: `CLAUDE.md` 292- main readme: `README.md` 293- justfile: `justfile` (task runner) 294- backend: `src/backend/` 295- frontend: `frontend/` 296- transcoder: `transcoder/` 297 298## contributing 299 300when working on plyr.fm: 301 3021. **test empirically first** - run code and prove it works 3032. **reference existing docs** - check docs directory before researching 3043. **keep it simple** - MVP over perfection 3054. **use lowercase** - respect plyr.fm's aesthetic 3065. **no sprawl** - avoid creating multiple versions of files 3076. **document decisions** - update docs as you work 308 309## questions? 310 311if anything is unclear: 312- check the relevant phase document 313- review example projects in sandbox 314- consult ATProto official docs 315- look at your atproto fork implementation