Podcasts hosted on ATProto

docs: Update documentation for OAuth-based architecture and easy fork/deploy

- Update WARP.md to reflect OAuth authentication flow
- Add comprehensive fork & deploy instructions for Railway, Render
- Document React frontend architecture and chunked uploads
- Add troubleshooting section and usage tips
- Highlight AT Protocol benefits for podcasting
- Remove hardcoded credential references

tigwyk 14744916 fefab15e

Changed files
+327 -205
+213 -159
README.md
··· 1 - # AT Protocol Podcast Host 1 + # ATCast - AT Protocol Podcast Host 2 2 3 - A podcast hosting platform that stores media files and RSS feeds using AT Protocol blob storage. Stream your podcasts directly from `at://` URIs! 3 + A podcast hosting platform that stores media files and RSS feeds using AT Protocol blob storage. Each user hosts their podcast on their own Bluesky/AT Protocol account - no centralized storage needed! 4 4 5 - ## Features 5 + ## โœจ Features 6 6 7 - - ๐ŸŽ™๏ธ Upload podcast episodes to AT Protocol blob storage 8 - - ๐Ÿ“ก Generate RSS feeds from AT Protocol-stored content 9 - - ๐ŸŽต Stream media files from `at://` URIs 10 - - ๐Ÿ” Secure authentication with Bluesky/AT Protocol 11 - - ๐Ÿ“ฑ RESTful API for podcast management 12 - - ๐ŸŒ CORS-enabled for web player integration 7 + - ๐ŸŽ™๏ธ **OAuth Authentication** - Users log in with their own Bluesky account 8 + - ๐Ÿ“ค **Large File Support** - Automatic chunking for files over 5MB 9 + - ๐Ÿ“ก **RSS Feed Generation** - Standard RSS 2.0 feeds with AT Protocol extensions 10 + - ๐ŸŽต **Direct Streaming** - Stream episodes from `at://` URIs 11 + - ๐Ÿ“ **Episode Management** - Full CRUD operations for podcast episodes 12 + - ๐ŸŽจ **Modern UI** - React frontend with dark/light mode 13 + - ๐ŸŒ **Decentralized** - Each user's podcast data stored on their own PDS 14 + - ๐Ÿ”“ **Open Access** - Public RSS feeds accessible by DID 13 15 14 - ## Prerequisites 16 + ## ๐Ÿš€ Quick Start: Fork & Deploy 15 17 16 - - Node.js 18+ 17 - - An AT Protocol account (e.g., Bluesky account) 18 - - App password for your AT Protocol account 18 + ### Option 1: Deploy to Railway (Recommended) 19 19 20 - ## Setup 20 + [![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/new) 21 21 22 - ### 1. Clone and Install 22 + 1. **Click the Deploy button** or go to [Railway](https://railway.app) 23 + 2. **Fork this repository** to your GitHub account 24 + 3. **Create a new project** in Railway from your forked repo 25 + 4. **Set environment variables** (see below) 26 + 5. **Deploy!** Railway will automatically build and start your app 23 27 24 - ```bash 25 - cd atproto-podcast 26 - npm install 28 + **Required Environment Variables for Railway:** 29 + ``` 30 + NODE_ENV=production 31 + SESSION_SECRET=your-random-secret-key-here 32 + PUBLIC_URL=https://your-app.up.railway.app 27 33 ``` 28 34 29 - ### 2. Configure Environment 35 + ### Option 2: Deploy to Render 30 36 31 - Copy the example environment file: 37 + 1. **Fork this repository** to your GitHub account 38 + 2. Go to [Render Dashboard](https://dashboard.render.com) 39 + 3. **New** โ†’ **Web Service** 40 + 4. Connect your forked repository 41 + 5. **Configure:** 42 + - **Build Command:** `npm run build` 43 + - **Start Command:** `npm start` 44 + - **Environment Variables:** (see below) 32 45 33 - ```bash 34 - cp .env.example .env 46 + **Required Environment Variables for Render:** 35 47 ``` 36 - 37 - Edit `.env` with your AT Protocol credentials: 38 - 39 - ```env 40 - PORT=3000 41 - ATPROTO_SERVICE=https://bsky.social 42 - ATPROTO_IDENTIFIER=your-handle.bsky.social 43 - ATPROTO_PASSWORD=your-app-password 48 + NODE_ENV=production 49 + SESSION_SECRET=your-random-secret-key-here 50 + PUBLIC_URL=https://your-app.onrender.com 44 51 ``` 45 52 46 - **Note:** Get an app password from your Bluesky account settings, not your main password. 53 + ### Option 3: Deploy to any Node.js host 47 54 48 - ### 3. Run the Application 55 + 1. **Fork and clone** this repository 56 + 2. **Install dependencies:** `npm install` 57 + 3. **Build frontend:** `npm run build` 58 + 4. **Set environment variables** (create `.env` file) 59 + 5. **Start:** `npm start` 49 60 50 - #### Development Mode (React + API) 61 + ## ๐Ÿ” Environment Variables 51 62 52 - Run both the React frontend and backend API concurrently: 53 - ```bash 54 - npm run dev:all 55 - ``` 63 + | Variable | Description | Example | 64 + |----------|-------------|----------| 65 + | `PORT` | Server port | `5000` (default) | 66 + | `NODE_ENV` | Environment | `production` or `development` | 67 + | `SESSION_SECRET` | Secret for session encryption | Generate with `openssl rand -base64 32` | 68 + | `PUBLIC_URL` | Your app's public URL | `https://your-app.railway.app` | 56 69 57 - This starts: 58 - - Backend API on `http://localhost:5000` 59 - - React frontend on `http://localhost:3000` (with proxy to API) 70 + **Important:** 71 + - Generate a strong `SESSION_SECRET` - **never use the example value in production** 72 + - Set `PUBLIC_URL` to your actual deployed URL for OAuth to work correctly 73 + - No AT Protocol credentials needed - users authenticate with their own accounts! 60 74 61 - Or run them separately: 62 - ```bash 63 - # Terminal 1 - Backend API 64 - npm run dev 75 + ## ๐Ÿ“‹ Prerequisites for Local Development 65 76 66 - # Terminal 2 - React Frontend 67 - npm run client 68 - ``` 77 + - Node.js 18+ 78 + - A Bluesky/AT Protocol account (for testing) 69 79 70 - #### Production Mode 80 + ## ๐Ÿ› ๏ธ Local Development Setup 71 81 72 - First, build the React application: 82 + ### 1. Fork and Clone 83 + 73 84 ```bash 74 - npm run build 85 + git clone https://github.com/YOUR-USERNAME/atproto-podcast-host.git 86 + cd atproto-podcast-host 75 87 ``` 76 88 77 - Then start the server: 89 + ### 2. Install Dependencies 90 + 78 91 ```bash 79 - npm start 92 + npm install 80 93 ``` 81 94 82 - The server will serve the built React app and API on `http://localhost:5000` 95 + This will install both backend and frontend dependencies automatically. 83 96 84 - ## API Endpoints 97 + ### 3. Configure Environment 85 98 86 - ### Upload Episode 99 + ```bash 100 + cp .env.example .env 87 101 ``` 88 - POST /api/upload/episode 89 - Content-Type: multipart/form-data 90 102 91 - Fields: 92 - - audio: Audio file (required) 93 - - title: Episode title (required) 94 - - description: Episode description (optional) 95 - - pubDate: Publication date (optional, defaults to now) 96 - ``` 103 + Edit `.env`: 97 104 98 - ### Get RSS Feed 105 + ```env 106 + PORT=5000 107 + NODE_ENV=development 108 + SESSION_SECRET=your-random-secret-for-local-dev 109 + PUBLIC_URL=http://localhost:5000 99 110 ``` 100 - GET /api/feed/rss 101 111 102 - Returns: RSS 2.0 XML feed with podcast episodes 103 - ``` 112 + ### 4. Run the Application 104 113 105 - ### Update Feed Metadata 114 + **Development Mode** (runs both frontend and backend): 115 + ```bash 116 + npm run dev:all 106 117 ``` 107 - POST /api/feed/metadata 108 - Content-Type: application/json 109 118 110 - { 111 - "title": "Your Podcast Name", 112 - "description": "Podcast description", 113 - "link": "https://yoursite.com", 114 - "language": "en" 115 - } 116 - ``` 119 + This starts: 120 + - **Backend API** on `http://localhost:5000` 121 + - **React frontend** on `http://localhost:3000` (proxies API requests to backend) 117 122 118 - ### Get Feed Metadata 119 - ``` 120 - GET /api/feed/metadata 123 + **Or run separately:** 124 + ```bash 125 + # Terminal 1 - Backend only 126 + npm run dev 121 127 122 - Returns: Current feed metadata 128 + # Terminal 2 - Frontend only 129 + npm run client 123 130 ``` 124 131 125 - ### Stream Media 132 + **Production Mode:** 133 + ```bash 134 + npm run build # Build React frontend 135 + npm start # Start production server 126 136 ``` 127 - GET /api/media/stream/:did/:cid 128 137 129 - Parameters: 130 - - did: Decentralized identifier 131 - - cid: Content identifier (from AT Protocol blob) 132 - ``` 138 + The server serves both the React app and API on the same port. 133 139 134 - ### Get All Episodes 135 - ``` 136 - GET /api/media/episodes 140 + ## ๐ŸŽฏ How It Works 137 141 138 - Returns: List of all episodes with streaming URLs 139 - ``` 142 + 1. **User logs in** with their Bluesky handle via OAuth 143 + 2. **Upload episodes** - Files are stored as blobs in the user's PDS 144 + 3. **Episodes stored as records** - Metadata saved in `app.podcast.episode` collection 145 + 4. **RSS feed generated** - Standard RSS 2.0 with AT Protocol extensions 146 + 5. **Anyone can access** - Public RSS feeds available via DID 140 147 141 - ### Get Episode by ID 142 - ``` 143 - GET /api/media/episodes/:id 148 + ## ๐Ÿ“ก Key API Endpoints 149 + 150 + ### Authentication 151 + - `GET /api/auth/login?handle=user.bsky.social` - Start OAuth login 152 + - `GET /api/auth/callback` - OAuth callback (automatic) 153 + - `GET /api/auth/session` - Check current session 154 + - `POST /api/auth/logout` - Log out 155 + 156 + ### Episodes 157 + - `POST /api/upload/episode` - Upload new episode (multipart/form-data) 158 + - `GET /api/media/episodes` - List all episodes 159 + - `GET /api/media/episodes/:id` - Get specific episode 160 + - `PUT /api/media/episodes/:id` - Update episode metadata 161 + - `DELETE /api/media/episodes/:id` - Delete episode 162 + - `GET /api/media/stream/:did/:cid` - Stream episode audio 163 + 164 + ### RSS Feed 165 + - `GET /api/feed/rss` - Get RSS feed (authenticated) 166 + - `POST /api/feed/publish` - Publish feed to AT Protocol 167 + - `GET /api/feed/at-rss` - Get published AT Protocol feed 168 + - `GET /api/feed/at-rss/:did` - Get any user's feed by DID (public) 169 + - `GET /api/feed/uri` - Get AT URI for published feed 144 170 145 - Returns: Single episode details 146 - ``` 171 + ### Feed Settings 172 + - `GET /api/feed/metadata` - Get podcast metadata 173 + - `POST /api/feed/metadata` - Update podcast metadata 174 + 175 + ## ๐Ÿ’ก Usage Tips 147 176 148 - ## Usage Example 177 + ### For Podcast Creators 149 178 150 - ### Upload an Episode 179 + 1. **Log in** with your Bluesky handle at the deployed URL 180 + 2. **Set podcast metadata** in Settings (title, description, etc.) 181 + 3. **Upload episodes** - supports MP3, WAV, and other audio formats 182 + 4. **Publish RSS feed** to AT Protocol for decentralized hosting 183 + 5. **Share your feed** - Give listeners your AT URI or HTTP feed URL 151 184 152 - ```bash 153 - curl -X POST http://localhost:3000/api/upload/episode \ 154 - -F "audio=@/path/to/episode.mp3" \ 155 - -F "title=My First Episode" \ 156 - -F "description=This is my first podcast episode!" 157 - ``` 185 + ### For Podcast Listeners 158 186 159 - ### Get RSS Feed 187 + - **HTTP Feed URL:** `https://your-app.com/api/feed/at-rss/did:plc:creator-did` 188 + - **AT Protocol URI:** `at://did:plc:creator-did/app.podcast.feed/self` 189 + - Compatible with any podcast player that supports RSS feeds 160 190 161 - ```bash 162 - curl http://localhost:3000/api/feed/rss 163 - ``` 191 + ### Accessing Other Users' Feeds 164 192 165 - ### Stream an Episode 193 + Anyone can access a published podcast feed without authentication: 166 194 167 195 ```bash 168 - curl http://localhost:3000/api/media/stream/{did}/{cid} --output episode.mp3 196 + # Get feed by DID 197 + curl https://your-app.com/api/feed/at-rss/did:plc:user-did 198 + 199 + # Stream episode 200 + curl https://your-app.com/api/media/stream/did:plc:user-did/episode-cid 169 201 ``` 170 202 171 203 ## Project Structure ··· 201 233 โ””โ”€โ”€ README.md 202 234 ``` 203 235 204 - ## How It Works 236 + ## ๐Ÿ—๏ธ Technical Architecture 205 237 206 - 1. **Upload**: Audio files are uploaded via multipart form data 207 - 2. **Store**: Files are uploaded to AT Protocol as blobs, receiving a CID (Content Identifier) 208 - 3. **Metadata**: Episode metadata (title, description, CID) is stored locally 209 - 4. **RSS**: RSS feed is dynamically generated from stored metadata 210 - 5. **Stream**: Episodes are streamed directly from AT Protocol blob storage using `at://` URIs 238 + ### AT Protocol Integration 211 239 212 - ## AT Protocol Integration 240 + **Data Storage:** 241 + - `app.podcast.episode` - Episode records with audio blob references 242 + - `app.podcast.settings` - Podcast metadata (title, description, etc.) 243 + - `app.podcast.feed` - Published RSS feed XML 213 244 214 - This project uses AT Protocol's blob storage to host podcast files: 245 + **Blob Storage:** 246 + - Audio files stored as blobs in user's Personal Data Server (PDS) 247 + - Files >5MB automatically chunked for PDS blob limits 248 + - Each blob receives a unique CID (Content Identifier) 249 + - Accessible via `at://` URIs or HTTP streaming endpoint 215 250 216 - - Audio files are stored as blobs in your AT Protocol repository 217 - - Each blob gets a unique CID (Content Identifier) 218 - - Files are accessible via `at://{did}/{cid}` URIs 219 - - The server acts as a bridge between HTTP and AT Protocol 251 + **Authentication:** 252 + - OAuth 2.0 flow with user's AT Protocol PDS 253 + - No centralized credentials - each user authenticates independently 254 + - Session management with file-based store (upgrade to Redis for production scale) 220 255 221 - ## Development 256 + ## ๐Ÿ”ง Technology Stack 222 257 223 - The project uses ES modules and includes: 258 + **Backend:** 259 + - Express.js - Web server 260 + - @atproto/oauth-client-node - OAuth authentication 261 + - @atcute/client - AT Protocol XRPC operations 262 + - @atproto/api - Public AT Protocol API access 263 + - Multer - File upload handling 264 + - RSS - Feed generation 224 265 225 - - Express.js for the web server 226 - - React + Vite for the frontend 227 - - @atproto/api for AT Protocol integration 228 - - Multer for file uploads 229 - - RSS package for feed generation 266 + **Frontend:** 267 + - React 18 - UI framework 268 + - Vite - Build tool and dev server 269 + - React Router - Client-side routing 270 + - Modern CSS with dark/light mode support 271 + 272 + ## ๐ŸŒŸ Why AT Protocol for Podcasting? 273 + 274 + - **Decentralized Ownership** - Your podcast data lives on your PDS, not a platform 275 + - **Portable** - Move your podcast to any AT Protocol host 276 + - **Censorship Resistant** - No central authority can remove your content 277 + - **Built-in Identity** - Your Bluesky handle is your podcast identity 278 + - **Composable** - Other apps can build on your podcast data 279 + - **No Storage Limits** - Limited only by your PDS provider 230 280 231 - ## Deployment 281 + ## ๐Ÿ“š Additional Resources 232 282 233 - ### Quick Deploy to Railway 283 + - [AT Protocol Documentation](https://atproto.com/) 284 + - [Bluesky Developer Portal](https://docs.bsky.app/) 285 + - [OAuth Client Documentation](https://github.com/bluesky-social/atproto/tree/main/packages/oauth) 234 286 235 - 1. Push your code to GitHub 236 - 2. Connect to Railway: https://railway.app 237 - 3. Set environment variables (see [DEPLOYMENT.md](DEPLOYMENT.md)) 238 - 4. Railway will automatically build and deploy 287 + ## ๐Ÿค Contributing 239 288 240 - **One Command Production Build:** 241 - ```bash 242 - npm run build # Builds React app 243 - npm start # Serves production app on port 5000 244 - ``` 289 + Contributions welcome! Please feel free to submit a Pull Request. 245 290 246 - ### Deployment Platforms 291 + 1. Fork the repository 292 + 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 293 + 3. Commit your changes (`git commit -m 'Add amazing feature'`) 294 + 4. Push to the branch (`git push origin feature/amazing-feature`) 295 + 5. Open a Pull Request 247 296 248 - This app works on: 249 - - โœ… Railway (recommended) 250 - - โœ… Render 251 - - โœ… Heroku 252 - - โœ… DigitalOcean App Platform 253 - - โœ… Any Node.js hosting 297 + ## ๐Ÿ› Troubleshooting 254 298 255 - See [DEPLOYMENT.md](DEPLOYMENT.md) for detailed instructions. 299 + **OAuth callback fails:** 300 + - Ensure `PUBLIC_URL` matches your actual deployment URL 301 + - Check that your app is accessible at that URL 302 + - Verify session storage is working (check `sessions/` directory) 256 303 257 - ## RSS Feed on AT Protocol 304 + **Upload fails:** 305 + - Check file size (max 500MB, chunks at 5MB) 306 + - Verify your PDS has available storage 307 + - Check console logs for specific error messages 258 308 259 - Your podcast RSS feed can be published as a decentralized AT Protocol record: 309 + **RSS feed not found:** 310 + - Publish the feed first using "Publish Feed" button in Settings 311 + - Check that episodes exist before publishing 260 312 261 - - Get an `at://` URI for your feed 262 - - Access it from any AT Protocol node 263 - - Maintain full ownership and portability 313 + ## ๐Ÿ“„ License 264 314 265 - See [RSS_AT_PROTOCOL.md](RSS_AT_PROTOCOL.md) for details. 315 + MIT License - see LICENSE file for details 266 316 267 - ## License 317 + ## โญ Show Your Support 268 318 269 - MIT 319 + If you find this project useful, please consider: 320 + - Giving it a star on GitHub 321 + - Sharing it with other podcast creators 322 + - Contributing improvements 323 + - Reporting bugs or suggesting features 270 324 271 - ## Contributing 325 + --- 272 326 273 - Contributions welcome! Please feel free to submit a Pull Request. 327 + Built with โค๏ธ for the AT Protocol ecosystem
+114 -46
WARP.md
··· 4 4 5 5 ## Project Overview 6 6 7 - AT Protocol Podcast Host is a podcast hosting platform that stores media files and RSS feeds using AT Protocol blob storage. Audio files are uploaded as blobs to AT Protocol, generating unique CIDs (Content Identifiers) accessible via `at://` URIs. The server acts as a bridge between HTTP clients and AT Protocol storage. 7 + **ATCast** is a podcast hosting platform that stores media files and RSS feeds using AT Protocol blob storage. It features: 8 + - OAuth-based per-user authentication (no hardcoded credentials) 9 + - React frontend with Vite 10 + - Episode management stored as AT Protocol records 11 + - Chunked blob uploads for large files (>5MB) 12 + - RSS feed generation and publishing to AT Protocol 13 + - Public RSS feed access via `at://` URIs 14 + 15 + Audio files are uploaded as blobs to each user's AT Protocol Personal Data Server (PDS), generating unique CIDs (Content Identifiers) accessible via `at://` URIs. The server acts as a bridge between HTTP clients and AT Protocol storage. 8 16 9 17 ## Common Development Commands 10 18 11 19 ### Running the Server 12 - - **Development mode** (with auto-reload): `npm run dev` 13 - - **Production mode**: `npm start` 14 - - Server runs on `http://localhost:3000` by default 20 + - **Development mode (backend only)**: `npm run dev` (runs on port 5000) 21 + - **Development mode (frontend only)**: `npm run client` (runs on port 3000 with proxy) 22 + - **Development mode (both)**: `npm run dev:all` (runs both concurrently) 23 + - **Production mode**: `npm run build && npm start` 24 + - Backend API runs on `http://localhost:5000` by default 25 + - React dev server runs on `http://localhost:3000` with proxy to backend 15 26 16 27 ### Installation 17 - - Install dependencies: `npm install` 28 + - Install dependencies: `npm install` (also installs client dependencies via postinstall) 18 29 - Copy environment template: `cp .env.example .env` 30 + - Configure `SESSION_SECRET` and `PUBLIC_URL` in `.env` 19 31 20 - ### Testing API Endpoints 21 - - **Upload episode**: 22 - ```powershell 23 - curl -X POST http://localhost:3000/api/upload/episode ` 24 - -F "audio=@/path/to/episode.mp3" ` 25 - -F "title=Episode Title" ` 26 - -F "description=Episode description" 27 - ``` 28 - - **Get RSS feed**: `curl http://localhost:3000/api/feed/rss` 29 - - **Stream episode**: `curl http://localhost:3000/api/media/stream/{did}/{cid} --output episode.mp3` 32 + ### Building for Production 33 + - **Build React app**: `npm run build` (or `npm run client:build`) 34 + - **Railway build**: `npm run railway:build` (installs & builds) 35 + - **Railway start**: `npm run railway:start` 30 36 31 37 ## Architecture 32 38 33 39 ### Core Components 34 40 41 + **OAuth Authentication** (`src/oauth-client.js` & `src/routes/auth.js`): 42 + - Uses `@atproto/oauth-client-node` for per-user OAuth authentication 43 + - Users log in with their own Bluesky/AT Protocol credentials 44 + - Sessions stored in file-based session store (`sessions/` directory) 45 + - OAuth state and session management with in-memory stores (use Redis in production) 46 + - Middleware `requireAuth` protects authenticated routes 47 + - Key endpoints: `/api/auth/login`, `/api/auth/callback`, `/api/auth/session`, `/api/auth/logout` 48 + 35 49 **AT Protocol Integration** (`src/atproto-client.js`): 36 - - Uses `@atproto/api` BskyAgent for authentication and blob operations 37 - - Manages session state with `isAuthenticated` flag 38 - - Key methods: `getAgent()`, `uploadBlob()`, `getBlob()` 39 - - Authenticates once per server lifecycle using credentials from `.env` 50 + - Simple unauthenticated client for public blob fetching via XRPC 51 + - Uses `@atcute/client` for authenticated operations (via OAuth session's fetchHandler) 52 + - Uses `@atproto/api` Agent for public API access 53 + - Key method: `getBlob(did, cid)` for streaming media 40 54 41 55 **Storage Layer** (`src/storage/metadata.js`): 42 - - File-based metadata storage in `podcast-metadata.json` at project root 43 - - Stores episode metadata (title, description, CID, pubDate) and feed configuration 44 - - Episodes sorted by pubDate in descending order 45 - - No database required; metadata persists locally 56 + - **Primary storage**: AT Protocol records in user's PDS 57 + - `app.podcast.episode` collection: Episode metadata + audio blob references 58 + - `app.podcast.settings` collection: Feed metadata (title, description, etc.) 59 + - `app.podcast.feed` collection: Published RSS feed XML 60 + - **Local fallback**: `podcast-metadata.json` for legacy compatibility 61 + - All episode data now stored as AT Protocol records, accessible via `at://` URIs 46 62 47 63 **Route Structure** (`src/routes/`): 48 - - **upload.js**: Handles multipart file uploads via Multer, uploads blobs to AT Protocol, stores metadata, cleans up temp files 49 - - **feed.js**: Generates RSS 2.0 XML feeds dynamically from stored metadata, includes custom `atproto:cid` elements 50 - - **media.js**: Streams blobs from AT Protocol using DID/CID pairs, provides episode listing endpoints 64 + - **auth.js**: OAuth login flow, session management, user authentication middleware 65 + - **upload.js**: Multipart file uploads via Multer, chunked blob uploads (>5MB), creates AT Protocol episode records 66 + - **feed.js**: RSS generation from AT Protocol records, feed publishing, public feed access by DID 67 + - **media.js**: Streams blobs (single or chunked), episode CRUD operations via AT Protocol 68 + 69 + **React Frontend** (`client/`): 70 + - Vite + React application with modern component architecture 71 + - Key pages: Login, Dashboard, Episodes, Settings, Podcast Player 72 + - Theme system with dark/light mode (ThemeContext) 73 + - Components: Header, Sidebar, Upload/Edit modals, Episode cards 74 + - API service layer for backend communication 51 75 52 76 ### Key Architectural Patterns 53 77 54 - 1. **Blob Storage Flow**: Client uploads โ†’ Multer temp storage โ†’ AT Protocol blob upload โ†’ CID generation โ†’ Local metadata storage โ†’ Temp file cleanup 55 - 2. **Streaming Flow**: Client requests DID/CID โ†’ Server fetches from AT Protocol XRPC endpoint โ†’ Proxies binary data to client 56 - 3. **Authentication**: Single agent instance shared across all modules, lazy authentication on first API call 57 - 4. **URL Construction**: RSS feed and API responses construct full URLs using `req.protocol` and `req.get('host')` for proper streaming links 78 + 1. **OAuth Flow**: User enters handle โ†’ Server resolves handle to DID/PDS โ†’ Redirect to user's PDS for auth โ†’ Callback with tokens โ†’ Session stored 79 + 2. **Blob Storage Flow**: Client uploads โ†’ Multer temp storage โ†’ Chunked if >5MB โ†’ Upload to user's PDS โ†’ Create episode record โ†’ CID generation โ†’ Temp file cleanup 80 + 3. **Streaming Flow**: Client requests DID/CID โ†’ Server resolves PDS โ†’ Fetches blob(s) from PDS โ†’ Streams to client (reassembling chunks if needed) 81 + 4. **Authentication**: Per-request authentication via OAuth session's fetchHandler, authenticated operations use user's own credentials 82 + 5. **Record Storage**: Episodes, settings, and feeds stored as AT Protocol records in user's repository 83 + 6. **URL Construction**: RSS feeds and API responses use `req.protocol` and `req.get('host')` for proper URL generation 58 84 59 85 ## Environment Configuration 60 86 61 87 Required environment variables in `.env`: 62 - - `PORT`: Server port (default: 3000) 63 - - `ATPROTO_SERVICE`: AT Protocol service URL (usually `https://bsky.social`) 64 - - `ATPROTO_IDENTIFIER`: Your Bluesky/AT Protocol handle 65 - - `ATPROTO_PASSWORD`: App password (NOT main account password) from Bluesky settings 88 + - `PORT`: Server port (default: 5000) 89 + - `NODE_ENV`: Environment (`development` or `production`) 90 + - `SESSION_SECRET`: Secret key for session encryption (change in production!) 91 + - `PUBLIC_URL`: Public URL for OAuth callbacks (e.g., `http://localhost:5000` or `https://yourdomain.com`) 66 92 67 - The server requires valid AT Protocol credentials to start. Obtain an app password from Bluesky account settings for secure authentication. 93 + **Note**: No hardcoded AT Protocol credentials needed! Users authenticate with their own Bluesky/AT Protocol accounts via OAuth. 68 94 69 95 ## Project Structure 70 96 71 97 ``` 72 - src/ 73 - โ”œโ”€โ”€ index.js # Express server initialization, route registration 74 - โ”œโ”€โ”€ atproto-client.js # AT Protocol authentication and blob operations 75 - โ”œโ”€โ”€ routes/ 76 - โ”‚ โ”œโ”€โ”€ upload.js # Episode upload endpoint 77 - โ”‚ โ”œโ”€โ”€ feed.js # RSS feed generation and metadata management 78 - โ”‚ โ””โ”€โ”€ media.js # Blob streaming and episode retrieval 79 - โ””โ”€โ”€ storage/ 80 - โ””โ”€โ”€ metadata.js # JSON-based metadata persistence 98 + atproto-podcast-host/ 99 + โ”œโ”€โ”€ client/ # React Frontend (Vite) 100 + โ”‚ โ”œโ”€โ”€ src/ 101 + โ”‚ โ”‚ โ”œโ”€โ”€ components/ # React components (Header, Sidebar, modals, etc.) 102 + โ”‚ โ”‚ โ”œโ”€โ”€ pages/ # Page components (Dashboard, Episodes, Settings, etc.) 103 + โ”‚ โ”‚ โ”œโ”€โ”€ contexts/ # React contexts (ThemeContext) 104 + โ”‚ โ”‚ โ”œโ”€โ”€ App.jsx # Main App component with routing 105 + โ”‚ โ”‚ โ””โ”€โ”€ main.jsx # Entry point 106 + โ”‚ โ”œโ”€โ”€ public/ # Static assets 107 + โ”‚ โ”œโ”€โ”€ package.json 108 + โ”‚ โ””โ”€โ”€ vite.config.js # Vite configuration with proxy 109 + โ”œโ”€โ”€ src/ # Backend API (Express) 110 + โ”‚ โ”œโ”€โ”€ index.js # Express server, session config, route registration 111 + โ”‚ โ”œโ”€โ”€ oauth-client.js # OAuth client setup, session management 112 + โ”‚ โ”œโ”€โ”€ atproto-client.js # Unauthenticated AT Protocol client for public blob access 113 + โ”‚ โ”œโ”€โ”€ routes/ 114 + โ”‚ โ”‚ โ”œโ”€โ”€ auth.js # OAuth authentication endpoints 115 + โ”‚ โ”‚ โ”œโ”€โ”€ upload.js # Episode upload with chunking support 116 + โ”‚ โ”‚ โ”œโ”€โ”€ feed.js # RSS generation and publishing 117 + โ”‚ โ”‚ โ””โ”€โ”€ media.js # Media streaming and episode CRUD 118 + โ”‚ โ””โ”€โ”€ storage/ 119 + โ”‚ โ””โ”€โ”€ metadata.js # Local metadata storage (fallback) 120 + โ”œโ”€โ”€ sessions/ # File-based session store 121 + โ”œโ”€โ”€ public/ # Legacy static files (deprecated) 122 + โ”œโ”€โ”€ .env.example # Environment template 123 + โ”œโ”€โ”€ package.json # Root package.json with build scripts 124 + โ”œโ”€โ”€ WARP.md # This file 125 + โ””โ”€โ”€ README.md # User documentation 81 126 ``` 82 127 83 128 ## Important Notes ··· 86 131 - No test framework is currently configured 87 132 - No linter or type checker configured 88 133 - Temporary uploads go to `public/uploads/` (cleaned up after blob upload) 89 - - Metadata file `podcast-metadata.json` is created automatically if missing 134 + - Sessions stored in `sessions/` directory (file-based, consider Redis for production) 90 135 - AT Protocol blobs are immutable; reuploading creates new CIDs 91 - - DID (Decentralized Identifier) comes from authenticated agent session 136 + - Each user's data (episodes, settings, feeds) stored in their own PDS 137 + - Large files (>5MB) are automatically chunked into smaller blobs 138 + - DID (Decentralized Identifier) comes from OAuth session 139 + - Frontend built with React + Vite, served from `client/dist` in production 140 + - CORS configured for production (`PUBLIC_URL`) and development (`http://localhost:3000`) 141 + - Session cookies use `secure: true` in production, require HTTPS 142 + 143 + ## Key Dependencies 144 + 145 + **Backend**: 146 + - `@atproto/api`: AT Protocol SDK for Agent-based operations 147 + - `@atproto/oauth-client-node`: OAuth client for user authentication 148 + - `@atcute/client`: Lightweight AT Protocol client for XRPC operations 149 + - `express`: Web server framework 150 + - `express-session`: Session management 151 + - `session-file-store`: File-based session storage 152 + - `multer`: Multipart file upload handling 153 + - `rss`: RSS feed generation 154 + - `cors`: Cross-origin resource sharing 155 + 156 + **Frontend**: 157 + - `react`: UI framework 158 + - `react-router-dom`: Client-side routing 159 + - `vite`: Build tool and dev server