audio streaming app plyr.fm

docs: publish docs with Starlight at docs.plyr.fm (#1028)

* docs: publish docs with Starlight at docs.plyr.fm

adds a Starlight (Astro) docs site that symlinks to the existing docs/
directory, keeping it as the single source of truth. includes:

- frontmatter injection across all ~65 markdown files
- docs-site/ scaffold with astro config, sidebar, and justfile
- contributing.md synthesized from setup, README, STATUS, and CLAUDE.md
- root justfile mod for `just docs run/build`
- loq limit bumps for 4 docs files (+4 lines from frontmatter)

Cloudflare Pages project setup is manual (see PR description).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* add GHA workflow for docs deployment via wrangler pages deploy

- path-filtered: triggers on docs/** and docs-site/** changes
- uses cloudflare/wrangler-action for deployment
- documents docs site in deployment/environments.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add landing page and match plyr.fm visual style

- add docs/index.md with splash template hero page
- custom CSS: plyr.fm colors (#0a0a0a bg, #6a9fff accent),
monospace font stack, light mode overrides
- homepage no longer 404s

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove duplicate h1 headings and normalize title casing

Starlight renders the frontmatter title as an h1, so the body # heading
was doubling every page title. stripped all body h1s and normalized
casing to lowercase aesthetic (respecting proper nouns).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: drop "decentralized" from docs tagline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

authored by zzstoatzz.io

Claude Opus 4.6 and committed by
GitHub
0acd8411 bcc79c1e

+584 -72
+34
.github/workflows/deploy-docs.yml
··· 1 + name: deploy docs 2 + 3 + on: 4 + push: 5 + branches: 6 + - main 7 + paths: 8 + - "docs/**" 9 + - "docs-site/**" 10 + - ".github/workflows/deploy-docs.yml" 11 + workflow_dispatch: 12 + 13 + jobs: 14 + deploy: 15 + name: deploy docs 16 + runs-on: ubuntu-latest 17 + concurrency: deploy-docs 18 + permissions: 19 + contents: read 20 + deployments: write 21 + steps: 22 + - uses: actions/checkout@v4 23 + 24 + - uses: oven-sh/setup-bun@v2 25 + 26 + - name: build 27 + run: cd docs-site && bun install && bun run build 28 + 29 + - name: deploy to cloudflare pages 30 + uses: cloudflare/wrangler-action@v3 31 + with: 32 + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} 33 + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} 34 + command: pages deploy docs-site/dist --project-name plyr-fm-docs --branch main
+3
.gitignore
··· 49 49 frontend/.svelte-kit/ 50 50 frontend/build/ 51 51 52 + # Docs site 53 + .astro/ 54 + 52 55 # Rust build artifacts 53 56 **/target/ 54 57
+66
docs-site/astro.config.mjs
··· 1 + import { defineConfig } from "astro/config"; 2 + import starlight from "@astrojs/starlight"; 3 + 4 + export default defineConfig({ 5 + site: "https://docs.plyr.fm", 6 + integrations: [ 7 + starlight({ 8 + title: "plyr.fm docs", 9 + favicon: "/favicon.svg", 10 + social: [ 11 + { 12 + icon: "github", 13 + label: "GitHub", 14 + href: "https://github.com/zzstoatzz/plyr.fm", 15 + }, 16 + ], 17 + sidebar: [ 18 + { 19 + label: "getting started", 20 + items: [ 21 + { slug: "local-development/setup" }, 22 + { slug: "contributing" }, 23 + ], 24 + }, 25 + { 26 + label: "guides", 27 + items: [ 28 + { slug: "authentication" }, 29 + { slug: "security" }, 30 + { slug: "rate-limiting" }, 31 + { slug: "offboarding" }, 32 + { slug: "content-gating-roadmap" }, 33 + ], 34 + }, 35 + { 36 + label: "architecture", 37 + autogenerate: { directory: "architecture" }, 38 + }, 39 + { label: "backend", autogenerate: { directory: "backend" } }, 40 + { label: "frontend", autogenerate: { directory: "frontend" } }, 41 + { 42 + label: "deployment", 43 + autogenerate: { directory: "deployment" }, 44 + }, 45 + { 46 + label: "moderation", 47 + autogenerate: { directory: "moderation" }, 48 + }, 49 + { label: "tools", autogenerate: { directory: "tools" } }, 50 + { label: "testing", autogenerate: { directory: "testing" } }, 51 + { label: "lexicons", autogenerate: { directory: "lexicons" } }, 52 + { label: "runbooks", autogenerate: { directory: "runbooks" } }, 53 + { 54 + label: "legal", 55 + items: [{ slug: "legal/privacy" }, { slug: "legal/terms" }], 56 + }, 57 + ], 58 + customCss: ["./src/styles/custom.css"], 59 + }), 60 + ], 61 + vite: { 62 + resolve: { 63 + preserveSymlinks: true, 64 + }, 65 + }, 66 + });
docs-site/bun.lockb

This is a binary file and will not be displayed.

+17
docs-site/justfile
··· 1 + # docs site commands 2 + 3 + # install dependencies 4 + install: 5 + bun install 6 + 7 + # start dev server 8 + run: install 9 + bun run dev 10 + 11 + # build static site 12 + build: install 13 + bun run build 14 + 15 + # preview built site 16 + preview: install 17 + bun run preview
+17
docs-site/package.json
··· 1 + { 2 + "name": "plyr-fm-docs", 3 + "type": "module", 4 + "version": "0.0.1", 5 + "scripts": { 6 + "dev": "astro dev", 7 + "start": "astro dev", 8 + "build": "astro build", 9 + "preview": "astro preview", 10 + "astro": "astro" 11 + }, 12 + "dependencies": { 13 + "@astrojs/starlight": "^0.34", 14 + "astro": "^5", 15 + "sharp": "^0.33" 16 + } 17 + }
+3
docs-site/public/favicon.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"> 2 + <text x="50%" y="50%" dominant-baseline="central" text-anchor="middle" font-size="28">🎵</text> 3 + </svg>
+7
docs-site/src/content.config.ts
··· 1 + import { defineCollection } from "astro:content"; 2 + import { docsLoader } from "@astrojs/starlight/loaders"; 3 + import { docsSchema } from "@astrojs/starlight/schema"; 4 + 5 + export const collections = { 6 + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), 7 + };
+1
docs-site/src/content/docs
··· 1 + ../../../docs
+43
docs-site/src/styles/custom.css
··· 1 + /* match plyr.fm dark-first aesthetic */ 2 + 3 + /* dark mode (default) */ 4 + :root { 5 + /* accent: plyr.fm periwinkle blue #6a9fff */ 6 + --sl-color-accent-low: hsl(218, 50%, 15%); 7 + --sl-color-accent: #6a9fff; 8 + --sl-color-accent-high: #c0d8ff; 9 + --sl-color-text-accent: #6a9fff; 10 + 11 + /* grays matched to plyr.fm bg/border/text tokens */ 12 + --sl-color-white: #e8e8e8; 13 + --sl-color-gray-1: #b0b0b0; 14 + --sl-color-gray-2: #808080; 15 + --sl-color-gray-3: #666666; 16 + --sl-color-gray-4: #444444; 17 + --sl-color-gray-5: #1a1a1a; 18 + --sl-color-gray-6: #141414; 19 + --sl-color-black: #0a0a0a; 20 + 21 + /* monospace font stack from plyr.fm */ 22 + --sl-font: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Consolas', 23 + monospace; 24 + --sl-font-system-mono: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 25 + 'Consolas', monospace; 26 + } 27 + 28 + /* light mode */ 29 + :root[data-theme='light'] { 30 + --sl-color-accent-low: hsl(218, 60%, 92%); 31 + --sl-color-accent: #4a7ddd; 32 + --sl-color-accent-high: #1a3a6a; 33 + --sl-color-text-accent: #4a7ddd; 34 + 35 + --sl-color-white: #fafafa; 36 + --sl-color-gray-1: #171717; 37 + --sl-color-gray-2: #525252; 38 + --sl-color-gray-3: #737373; 39 + --sl-color-gray-4: #a3a3a3; 40 + --sl-color-gray-5: #e5e5e5; 41 + --sl-color-gray-6: #f5f5f5; 42 + --sl-color-black: #ffffff; 43 + }
+3
docs-site/tsconfig.json
··· 1 + { 2 + "extends": "astro/tsconfigs/strict" 3 + }
+3
docs-site/wrangler.toml
··· 1 + name = "plyr-fm-docs" 2 + compatibility_date = "2025-01-01" 3 + pages_build_output_dir = "dist"
+3 -1
docs/CLAUDE.md
··· 1 - # docs 1 + --- 2 + title: "docs" 3 + --- 2 4 3 5 organized knowledge base - check here before researching. 4 6
+3 -1
docs/README.md
··· 1 - # plyr.fm documentation 1 + --- 2 + title: "plyr.fm documentation" 3 + --- 2 4 3 5 organized knowledge base for plyr.fm development. 4 6
+3 -1
docs/architecture/jams-queue-integration.md
··· 1 - # jams + queue integration 1 + --- 2 + title: "jams + queue integration" 3 + --- 2 4 3 5 ## big picture 4 6
+3 -1
docs/architecture/jams-transport-decision.md
··· 1 - # jams transport layer decision: Redis Streams 1 + --- 2 + title: "jams transport layer decision: Redis Streams" 3 + --- 2 4 3 5 ## status 4 6
+3 -1
docs/architecture/jams.md
··· 1 - # jams — shared listening rooms 1 + --- 2 + title: "jams — shared listening rooms" 3 + --- 2 4 3 5 real-time shared playback rooms. server-side only (no ATProto records in v1). gated behind `jams` feature flag. 4 6
+3 -1
docs/authentication.md
··· 1 - # authentication 1 + --- 2 + title: "authentication" 3 + --- 2 4 3 5 plyr.fm uses secure cookie-based authentication to protect user sessions from XSS attacks. 4 6
+3 -1
docs/backend/atproto-identity.md
··· 1 - # ATProto identity and OAuth 1 + --- 2 + title: "ATProto identity and OAuth" 3 + --- 2 4 3 5 ## what is DID PLC? 4 6
+3 -1
docs/backend/background-tasks.md
··· 1 - # background tasks 1 + --- 2 + title: "background tasks" 3 + --- 2 4 3 5 plyr.fm uses [pydocket](https://github.com/PrefectHQ/pydocket) for durable background task execution, backed by Redis. 4 6
+3 -1
docs/backend/configuration.md
··· 1 - # configuration 1 + --- 2 + title: "configuration" 3 + --- 2 4 3 5 plyr.fm uses nested pydantic settings for configuration management, following a pattern similar to prefect. 4 6
+3 -1
docs/backend/database/connection-pooling.md
··· 1 - # database connection pooling 1 + --- 2 + title: "database connection pooling" 3 + --- 2 4 3 5 configuration and best practices for managing database connections in production. 4 6
+3 -1
docs/backend/database/neon.md
··· 1 - # neon database configuration 1 + --- 2 + title: "neon database configuration" 3 + --- 2 4 3 5 configuration guide for connecting to neon postgres databases. 4 6
+3 -1
docs/backend/feature-flags.md
··· 1 - # feature flags 1 + --- 2 + title: "feature flags" 3 + --- 2 4 3 5 per-user feature flags for controlled rollout of experimental features. 4 6
+3 -1
docs/backend/genre-classification.md
··· 1 - # genre classification 1 + --- 2 + title: "genre classification" 3 + --- 2 4 3 5 ML-based genre classification for tracks using the [effnet-discogs](https://replicate.com/mtg/effnet-discogs) model on Replicate. 4 6
+3 -1
docs/backend/liked-tracks.md
··· 1 - # liked tracks 1 + --- 2 + title: "liked tracks" 3 + --- 2 4 3 5 ## overview 4 6
+3 -1
docs/backend/mood-search.md
··· 1 - # mood search 1 + --- 2 + title: "mood search" 3 + --- 2 4 3 5 semantic search over tracks using CLAP (Contrastive Language-Audio Pretraining) embeddings. users describe a mood in natural language and get matching tracks ranked by audio similarity. 4 6
+3 -1
docs/backend/playlist-recommendations.md
··· 1 - # playlist recommendations 1 + --- 2 + title: "playlist recommendations" 3 + --- 2 4 3 5 recommends tracks for a playlist based on its existing tracks' CLAP embeddings in turbopuffer. shown inline when editing a playlist. 4 6
+3 -1
docs/backend/streaming-uploads.md
··· 1 - # streaming uploads 1 + --- 2 + title: "streaming uploads" 3 + --- 2 4 3 5 **status**: implemented in PR #182 4 6 **date**: 2025-11-03
+3 -1
docs/backend/transcoder.md
··· 1 - # audio transcoder service 1 + --- 2 + title: "audio transcoder service" 3 + --- 2 4 3 5 ## overview 4 6
+3 -1
docs/content-gating-roadmap.md
··· 1 - # Content Gating: Research Notes 1 + --- 2 + title: "content gating: research notes" 3 + --- 2 4 3 5 Reference material for understanding ATProtoFans and preparing for upcoming discussions. 4 6
+155
docs/contributing.md
··· 1 + --- 2 + title: "contributing" 3 + --- 4 + 5 + guide for contributing to plyr.fm. 6 + 7 + ## getting started 8 + 9 + ### prerequisites 10 + 11 + - **python**: 3.11+ (managed via [`uv`](https://docs.astral.sh/uv/)) 12 + - **bun**: for frontend development 13 + - **postgres**: local database (optional — can use neon dev instance) 14 + 15 + ### setup 16 + 17 + ```bash 18 + gh repo clone zzstoatzz/plyr.fm 19 + cd plyr.fm 20 + 21 + # install dependencies 22 + uv sync 23 + cd frontend && bun install && cd .. 24 + 25 + # configure environment 26 + cp .env.example .env 27 + # edit .env with your credentials (see local-development/setup for details) 28 + 29 + # start backend (terminal 1) 30 + just backend run 31 + 32 + # start frontend (terminal 2) 33 + just frontend run 34 + ``` 35 + 36 + visit http://localhost:5173 to see the app. see [local development setup](./local-development/setup.md) for complete environment configuration, database setup, and troubleshooting. 37 + 38 + ## development workflow 39 + 40 + 1. **check [`STATUS.md`](https://github.com/zzstoatzz/plyr.fm/blob/main/STATUS.md)** for active tasks and known issues 41 + 2. **create a GitHub issue** describing the change (we use GitHub Issues, not Linear) 42 + 3. **branch from `main`**, make your changes 43 + 4. **open a PR** for review — never push directly to main 44 + 5. merging to `main` auto-deploys to **staging** (`stg.plyr.fm`). production requires `just release` 45 + 46 + ### running tests 47 + 48 + ```bash 49 + # from repo root (not from backend/) 50 + just backend test 51 + 52 + # specific test file 53 + uv run pytest tests/api/test_tracks.py -v 54 + 55 + # with coverage 56 + uv run pytest --cov=backend 57 + ``` 58 + 59 + always add regression tests when fixing bugs. 60 + 61 + ### linting 62 + 63 + ```bash 64 + just backend lint # python (ruff) 65 + just frontend check # svelte (svelte-check) 66 + ``` 67 + 68 + ### database migrations 69 + 70 + ```bash 71 + # create migration after model changes 72 + just backend migrate "description of change" 73 + 74 + # apply migrations 75 + just backend migrate-up 76 + ``` 77 + 78 + see [database migrations](./deployment/database-migrations.md) for the full workflow. 79 + 80 + ## conventions 81 + 82 + ### code style 83 + 84 + - **type hints required** everywhere — Python and TypeScript 85 + - **async everywhere** — never block the event loop. use `anyio`/`aiofiles` 86 + - **lowercase aesthetic** in naming, docs, and commit messages 87 + - **imports at the top** of files — only defer imports to break circular dependencies 88 + - **keep it simple** — MVP over perfection 89 + 90 + ### python 91 + 92 + - use `uv` for everything (never `pip`) 93 + - use `uv run` to execute scripts and tools 94 + - prefer walrus operator (`:=`) for assign-and-check patterns 95 + - inline pass-through intermediate variables into constructor calls 96 + 97 + ### frontend 98 + 99 + - SvelteKit with **Svelte 5 Runes** — use `$state`, `$derived`, `$effect` 100 + - see [state management](./frontend/state-management.md) for patterns 101 + 102 + ### ATProto 103 + 104 + - NSIDs are environment-aware via settings (`fm.plyr.dev` for dev, `fm.plyr` for prod) — never hardcode them 105 + - session IDs live in HttpOnly cookies — never use `localStorage` for auth 106 + 107 + ## useful commands 108 + 109 + ```bash 110 + # development 111 + just backend run # start backend 112 + just frontend run # start frontend 113 + just backend test # run tests 114 + just backend lint # lint python 115 + just frontend check # lint svelte 116 + 117 + # database 118 + just backend migrate "msg" # create migration 119 + just backend migrate-up # apply migrations 120 + 121 + # infrastructure 122 + just dev-services # start redis (docker) 123 + just dev-services-down # stop redis 124 + just tunnel # expose backend via ngrok 125 + ``` 126 + 127 + ## project structure 128 + 129 + ``` 130 + plyr.fm/ 131 + ├── backend/src/backend/ 132 + │ ├── api/ # public endpoints 133 + │ ├── _internal/ # auth, PDS, uploads 134 + │ ├── models/ # SQLAlchemy schemas 135 + │ ├── storage/ # R2 and filesystem 136 + │ └── utilities/ # config, helpers 137 + ├── frontend/src/ 138 + │ ├── routes/ # pages (+page.svelte, +page.server.ts) 139 + │ └── lib/ # components & state (.svelte.ts) 140 + ├── services/ 141 + │ ├── transcoder/ # audio transcoding (Rust) 142 + │ ├── moderation/ # content moderation (Rust) 143 + │ └── clap/ # ML embeddings (Python, Modal) 144 + ├── scripts/ # admin scripts (uv run scripts/...) 145 + ├── docs/ # architecture & guides 146 + └── STATUS.md # living status document 147 + ``` 148 + 149 + ## further reading 150 + 151 + - [local development setup](./local-development/setup.md) — full environment config and troubleshooting 152 + - [backend configuration](./backend/configuration.md) — settings and environment variables 153 + - [state management](./frontend/state-management.md) — Svelte 5 runes patterns 154 + - [deployment environments](./deployment/environments.md) — staging vs production 155 + - [tools](./tools/) — logfire, neon, pdsx, and other dev tools
+3 -1
docs/deployment/database-migrations.md
··· 1 - # database migrations 1 + --- 2 + title: "database migrations" 3 + --- 2 4 3 5 ## current state (automated ✓) 4 6
+13 -1
docs/deployment/environments.md
··· 1 - # environment separation strategy 1 + --- 2 + title: "environment separation strategy" 3 + --- 2 4 3 5 plyr.fm uses a simple three-tier deployment strategy: development → staging → production. 4 6 ··· 108 110 - production branch: `main` 109 111 - production environment: `PUBLIC_API_URL=https://api-stg.plyr.fm` 110 112 - custom domain: `stg.plyr.fm` 113 + 114 + ### docs 115 + 116 + **plyr-fm-docs**: 117 + - framework: astro (starlight) 118 + - deployed via GHA workflow (`.github/workflows/deploy-docs.yml`) using `wrangler pages deploy` 119 + - triggers on push to `main` when `docs/**` or `docs-site/**` change 120 + - build command: `cd docs-site && bun install && bun run build` 121 + - build output: `docs-site/dist` 122 + - custom domain: `docs.plyr.fm` 111 123 112 124 ### secrets management 113 125
+3 -1
docs/frontend/data-loading.md
··· 1 - # data loading 1 + --- 2 + title: "data loading" 3 + --- 2 4 3 5 ## overview 4 6
+3 -1
docs/frontend/design-tokens.md
··· 1 - # design tokens 1 + --- 2 + title: "design tokens" 3 + --- 2 4 3 5 CSS custom properties defined in `frontend/src/routes/+layout.svelte`. Use these instead of hardcoding values. 4 6
+3 -1
docs/frontend/keyboard-shortcuts.md
··· 1 - # keyboard shortcuts 1 + --- 2 + title: "keyboard shortcuts" 3 + --- 2 4 3 5 global keyboard shortcuts for the plyr.fm frontend. 4 6
+3 -1
docs/frontend/navigation.md
··· 1 - # client-side navigation 1 + --- 2 + title: "client-side navigation" 3 + --- 2 4 3 5 ## preserving player state across navigation 4 6
+3 -1
docs/frontend/portals.md
··· 1 - # portals (rendering modals outside parent DOM) 1 + --- 2 + title: "portals (rendering modals outside parent DOM)" 3 + --- 2 4 3 5 ## the problem 4 6
+3 -1
docs/frontend/queue.md
··· 1 - # queue design 1 + --- 2 + title: "queue design" 3 + --- 2 4 3 5 ## overview 4 6
+3 -1
docs/frontend/redirect-after-login.md
··· 1 - # redirect after login 1 + --- 2 + title: "redirect after login" 3 + --- 2 4 3 5 ## the problem 4 6
+3 -1
docs/frontend/search.md
··· 1 - # unified search 1 + --- 2 + title: "unified search" 3 + --- 2 4 3 5 global search across tracks, artists, albums, and tags with fuzzy matching. 4 6
+3 -1
docs/frontend/state-management.md
··· 1 - # state management 1 + --- 2 + title: "state management" 3 + --- 2 4 3 5 ## svelte 5 runes mode 4 6
+3 -1
docs/frontend/toast-notifications.md
··· 1 - # toast notification system 1 + --- 2 + title: "toast notification system" 3 + --- 2 4 3 5 ## status 4 6
+15
docs/index.md
··· 1 + --- 2 + title: plyr.fm docs 3 + description: documentation for plyr.fm — audio streaming on ATProto 4 + template: splash 5 + hero: 6 + title: plyr.fm docs 7 + tagline: audio streaming on ATProto 8 + actions: 9 + - text: get started 10 + link: /local-development/setup/ 11 + icon: right-arrow 12 + - text: contributing 13 + link: /contributing/ 14 + variant: minimal 15 + ---
+3 -1
docs/legal/meetings/2026-01-02-notes.md
··· 1 - # meeting notes - 2026-01-02 1 + --- 2 + title: "meeting notes - 2026-01-02" 3 + --- 2 4 3 5 ## action items 4 6
+3 -1
docs/legal/meetings/2026-01-03-initial.md
··· 1 - # lawyer meeting agenda 1 + --- 2 + title: "lawyer meeting agenda" 3 + --- 2 4 3 5 ## context 4 6
+3 -1
docs/legal/privacy.md
··· 1 - # privacy policy 1 + --- 2 + title: "privacy policy" 3 + --- 2 4 3 5 > **note:** the source of truth is `frontend/src/routes/privacy/+page.svelte`. this markdown is a plain-text mirror for reference. 4 6
+4 -2
docs/legal/questions.md
··· 1 - plyr.fm legal questions 1 + --- 2 + title: "legal questions" 3 + --- 2 4 3 - # plyr.fm - legal questions 5 + plyr.fm legal questions 4 6 5 7 music streaming platform on AT Protocol. users upload audio. no payments yet. 6 8
+3 -1
docs/legal/terms.md
··· 1 - # terms of service 1 + --- 2 + title: "terms of service" 3 + --- 2 4 3 5 > **note:** the source of truth is `frontend/src/routes/terms/+page.svelte`. this markdown is a plain-text mirror for reference. 4 6
+3 -1
docs/lexicons/overview.md
··· 1 - # plyr.fm Lexicons 1 + --- 2 + title: "plyr.fm lexicons" 3 + --- 2 4 3 5 > **note**: this is living documentation. the lexicon JSON definitions in `/lexicons/` are the source of truth. 4 6
+3 -1
docs/local-development/setup.md
··· 1 - # local development setup 1 + --- 2 + title: "local development setup" 3 + --- 2 4 3 5 ## prerequisites 4 6
+3 -1
docs/moderation/atproto-labeler.md
··· 1 - # ATProto labeler service 1 + --- 2 + title: "ATProto labeler service" 3 + --- 2 4 3 5 technical documentation for the moderation service's ATProto labeling capabilities. 4 6
+3 -1
docs/moderation/copyright-detection.md
··· 1 - # copyright detection 1 + --- 2 + title: "copyright detection" 3 + --- 2 4 3 5 technical documentation for the copyright scanning system. 4 6
+3 -1
docs/moderation/overview.md
··· 1 - # moderation on plyr.fm 1 + --- 2 + title: "moderation on plyr.fm" 3 + --- 2 4 3 5 ## philosophy 4 6
+3 -1
docs/moderation/sensitive-content.md
··· 1 - # sensitive content moderation 1 + --- 2 + title: "sensitive content moderation" 3 + --- 2 4 3 5 ## overview 4 6
+3 -1
docs/offboarding.md
··· 1 - # Offboarding & Data Export 1 + --- 2 + title: "offboarding & data export" 3 + --- 2 4 3 5 Plyr.fm provides tools for users to export their data and manage their presence on the platform. This document outlines the architecture and workflows for these features. 4 6
+3 -1
docs/rate-limiting.md
··· 1 - # Rate Limiting 1 + --- 2 + title: "rate limiting" 3 + --- 2 4 3 5 plyr.fm uses [slowapi](https://github.com/laurentS/slowapi) to implement application-side rate limiting. This protects the backend from abuse, brute-force attacks, and denial-of-service attempts. 4 6
+3 -1
docs/research/2025-12-18-moderation-cleanup.md
··· 1 - # research: moderation cleanup 1 + --- 2 + title: "research: moderation cleanup" 3 + --- 2 4 3 5 **date**: 2025-12-18 4 6 **question**: understand issues #541-544 and how the moderation system works to inform cleanup
+3 -1
docs/research/2025-12-19-beartype.md
··· 1 - # research: beartype runtime type checking 1 + --- 2 + title: "research: beartype runtime type checking" 3 + --- 2 4 3 5 **date**: 2025-12-19 4 6 **question**: investigate beartype for runtime type checking, determine how to integrate into plyr.fm
+3 -1
docs/research/2025-12-19-ios-share-extension-minimal-app.md
··· 1 - # research: minimal iOS app with share extension 1 + --- 2 + title: "research: minimal iOS app with share extension" 3 + --- 2 4 3 5 **date**: 2025-12-19 4 6 **question**: how to build a minimal iOS app with share extension for uploading audio to plyr.fm API, for someone with no iOS experience
+3 -1
docs/research/2025-12-19-web-share-target-ios-pwa.md
··· 1 - # research: Web Share Target API for iOS PWAs 1 + --- 2 + title: "research: web share target API for iOS PWAs" 3 + --- 2 4 3 5 **date**: 2025-12-19 4 6 **question**: can a PWA receive shared audio files from the iOS share sheet (like SoundCloud does)?
+3 -1
docs/research/2025-12-20-atprotofans-paywall-integration.md
··· 1 - # research: atprotofans paywall integration 1 + --- 2 + title: "research: atprotofans paywall integration" 3 + --- 2 4 3 5 **date**: 2025-12-20 4 6 **question**: how should plyr.fm integrate with atprotofans to enable supporter-gated content?
+3 -1
docs/research/2025-12-20-moderation-architecture-overhaul.md
··· 1 - # research: moderation architecture overhaul 1 + --- 2 + title: "research: moderation architecture overhaul" 3 + --- 2 4 3 5 **date**: 2025-12-20 4 6 **question**: how should plyr.fm evolve its moderation architecture based on Roost Osprey and Bluesky Ozone patterns?
+3 -1
docs/research/2025-12-20-supporter-gated-content-architecture.md
··· 1 - # research: supporter-gated content architecture 1 + --- 2 + title: "research: supporter-gated content architecture" 3 + --- 2 4 3 5 **date**: 2025-12-20 4 6 **question**: how do we prevent direct R2 access to paywalled audio content?
+3 -1
docs/research/2026-01-01-atproto-oauth-permission-sets.md
··· 1 - # research: ATProto OAuth permission sets 1 + --- 2 + title: "research: ATProto OAuth permission sets" 3 + --- 2 4 3 5 **date**: 2026-01-01 4 6 **question**: how do ATProto OAuth permission sets work, and how could plyr.fm adopt them?
+3 -1
docs/research/2026-01-03-multi-account-experience.md
··· 1 - # multi-account experience 1 + --- 2 + title: "multi-account experience" 3 + --- 2 4 3 5 **status:** design draft 4 6 **issue:** [#583](https://github.com/zzstoatzz/plyr.fm/issues/583)
+3 -1
docs/research/2026-01-29-atproto-media-service-patterns.md
··· 1 - # ATProto media service patterns 1 + --- 2 + title: "ATProto media service patterns" 3 + --- 2 4 3 5 research date: 2026-01-29 4 6 source: [discourse thread](https://discourse.atprotocol.community/t/media-pds-service/297) (18 posts, nov 2025 - jan 2026)
+3 -1
docs/research/2026-01-29-pds-blob-storage.md
··· 1 - # PDS blob storage research 1 + --- 2 + title: "PDS blob storage research" 3 + --- 2 4 3 5 storing audio on user's PDS instead of (or in addition to) R2. 4 6
+3 -1
docs/research/2026-02-28-redirect-after-login-ux.md
··· 1 - # redirect after login (deep link preservation through auth) 1 + --- 2 + title: "redirect after login (deep link preservation through auth)" 3 + --- 2 4 3 5 **status:** research complete 4 6 **date:** 2026-02-28
+3 -1
docs/runbooks/README.md
··· 1 - # runbooks 1 + --- 2 + title: "runbooks" 3 + --- 2 4 3 5 operational procedures for production incidents. 4 6
+3 -1
docs/runbooks/connection-pool-exhaustion.md
··· 1 - # connection pool exhaustion 1 + --- 2 + title: "connection pool exhaustion" 3 + --- 2 4 3 5 ## symptoms 4 6
+3 -1
docs/security.md
··· 1 - # Security 1 + --- 2 + title: "security" 3 + --- 2 4 3 5 Overview of security mechanisms in plyr.fm. 4 6
+3 -1
docs/testing/README.md
··· 1 - # testing 1 + --- 2 + title: "testing" 3 + --- 2 4 3 5 testing philosophy and infrastructure for plyr.fm. 4 6
+3 -1
docs/testing/integration-tests.md
··· 1 - # integration tests 1 + --- 2 + title: "integration tests" 3 + --- 2 4 3 5 integration tests run against the staging environment (`api-stg.plyr.fm`) using real API tokens. 4 6
+3 -1
docs/tools/logfire.md
··· 1 - # Logfire Querying Guide 1 + --- 2 + title: "logfire querying guide" 3 + --- 2 4 3 5 ## Basic Concepts 4 6
+3 -1
docs/tools/neon.md
··· 1 - # neon mcp guide for plyr.fm 1 + --- 2 + title: "neon mcp guide for plyr.fm" 3 + --- 2 4 3 5 the [neon mcp server](https://github.com/neondatabase/mcp-server-neon) provides tools for interacting with neon postgres databases through the model context protocol. this guide covers read-only operations useful for inspecting and debugging plyr.fm's database. 4 6
+3 -1
docs/tools/pdsx.md
··· 1 - # pdsx guide for plyr.fm 1 + --- 2 + title: "pdsx guide for plyr.fm" 3 + --- 2 4 3 5 [pdsx](https://github.com/zzstoatzz/pdsx) is a CLI tool for ATProto record operations. this guide covers how to use it for inspecting and managing plyr.fm track records. 4 6
+3 -1
docs/tools/plyrfm.md
··· 1 - # plyrfm 1 + --- 2 + title: "plyrfm" 3 + --- 2 4 3 5 python SDK and CLI for plyr.fm - available on [PyPI](https://pypi.org/project/plyrfm/) and [GitHub](https://github.com/zzstoatzz/plyr-python-client). 4 6
+3 -1
docs/tools/status-maintenance.md
··· 1 - # status maintenance workflow 1 + --- 2 + title: "status maintenance workflow" 3 + --- 2 4 3 5 automated workflow that archives old STATUS.md content and generates audio updates. 4 6
+3 -1
docs/tools/tap.md
··· 1 - # tap 1 + --- 2 + title: "tap" 3 + --- 2 4 3 5 bluesky's ATProto sync utility for backfilling and streaming custom lexicons. 4 6
+1
justfile
··· 3 3 mod backend 4 4 mod transcoder 'services/transcoder' 5 5 mod moderation 'services/moderation' 6 + mod docs 'docs-site' 6 7 7 8 8 9 # show available commands
+4 -4
loq.toml
··· 87 87 88 88 [[rules]] 89 89 path = "docs/authentication.md" 90 - max_lines = 627 90 + max_lines = 631 91 91 92 92 [[rules]] 93 93 path = "docs/backend/liked-tracks.md" 94 - max_lines = 503 94 + max_lines = 507 95 95 96 96 [[rules]] 97 97 path = "docs/deployment/database-migrations.md" 98 - max_lines = 629 98 + max_lines = 633 99 99 100 100 [[rules]] 101 101 path = "docs/tools/neon.md" 102 - max_lines = 519 102 + max_lines = 523 103 103 104 104 [[rules]] 105 105 path = "frontend/src/lib/components/AddToMenu.svelte"