+213
-159
README.md
+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
+
[](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
+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