Riverside-style remote podcast recording MVP with local MediaRecorder capture, chunk persistence via IndexedDB, and resumable uploads. Live comms (WebRTC) stay isolated from recording.
frontend/- Next.js 14 (App Router, TS, Tailwind, shadcn-inspired UI), MediaRecorder + IndexedDB + worker upload scaffoldingbackend/- Fastify + TS, JWT auth (host + guest), in-memory session/track store, S3-compatible upload stubsshared/- Types and constants shared across servicesinfra/- Dockerfiles, docker-compose, env examples
pnpm install
pnpm devFrontend: http://localhost:3000 Backend API: http://localhost:4000
pnpm lint
pnpm typecheck
pnpm build
pnpm --filter @podster/backend test
pnpm --filter frontend exec playwright testThe frontend typecheck command bootstraps placeholder App Router type files before tsc runs so clean checkouts do not depend on an existing .next build artifact.
Copy infra/env.example to .env files as needed. Key vars:
NEXT_PUBLIC_API_URL- backend URL for the frontendHOST_JWT_SECRET,GUEST_JWT_SECRET- JWT signing secretsSTORAGE_*- S3/R2-compatible settings for signed URLs
- Multipart uploads require the bucket CORS policy to expose the
ETagresponse header. - Use
backend/scripts/configure-cors.tsto apply a matching bucket CORS rule for the configuredFRONTEND_ORIGIN. STORAGE_FORCE_PATH_STYLEshould usually stay empty unless you are targeting a local S3-compatible endpoint such as MinIO.
- Separation of concerns: WebRTC signaling/client kept in its own module; recording pipeline is local-only.
- Local-first recording: MediaRecorder slices every second, persists to IndexedDB; uploads happen only after stop.
- Resumable uploads: Worker fans out PUTs to signed URLs; chunk metadata stays locally until completion.
- Processing hook: Placeholder FFmpeg service ready to be wired to object-storage events.