CrowdRoom is a real-time room-based music app built with FastAPI and Vue 3. It lets hosts manage shared rooms, invites, playback, queue interactions, and live updates over WebSockets.
- Real-time room updates over WebSockets
- Room, session, invite, moderation, and membership flows
- Queue management with voting and playback-related state
- Spotify integration for auth, search, and playback control
- Fallback music metadata flow and adapter-based external integrations
LOCALandONLINEauthentication modes- PostgreSQL and SQLite support
- Docker setup for deployment and a separate frontend development workflow
Streaming providers are integrated through adapters (auth, search, playback).
| Platform | Status |
|---|---|
| Spotify | Supported |
| Tidal | Planned |
Spotify is the only provider currently implemented. The adapter layer (backend/adapters/) is built to host additional providers as they land.
esp32-crowdroom is an optional ESP32 firmware that receives a room's QR code and displays it on a physical device, so guests can scan and join the room without sharing a screen.
- FastAPI
- SQLModel
- Alembic
- PostgreSQL / SQLite (native dev only)
- Pydantic v2
- Argon2 password hashing
- Vue 3
- Vue Router
- Vite
- Vitest
- pnpm
# 1. Configure secrets
cp .env.example .env
# Edit .env and set strong values for SECRET_KEY, ENCRYPTION_KEY and DEVICE_AUTH_TOKEN
# 2. Start the stack (tables are created automatically on first boot)
docker compose up --build -d
# 3. (Optional) Apply migrations — only needed when upgrading an
# existing database to a newer schema
docker compose exec app alembic upgrade headThis starts the full stack:
app: FastAPI backend and frontend serving containerdb: PostgreSQL 16 (postgres:16-alpine)
The application container waits for PostgreSQL to become healthy before starting.
Open the app at:
http://localhost:8000on the same machinehttp://<lan-ip>:8000from other devices on the same network
By default the app is published on 0.0.0.0:8000. Both the host bind IP and host port are configurable in .env (the container-side port stays fixed at 8000):
# Restrict exposure to localhost only
BIND_IP=127.0.0.1
# Publish on a different host port
HOST_PORT=9000Persistent Docker volumes:
pgdatafor PostgreSQL dataavatarsfor uploaded avatars
For native development without Docker, point DATABASE_URL to SQLite or a local PostgreSQL instance.
uv sync --dev
alembic upgrade head
uv run uvicorn backend.main:app --reloadcd frontend
pnpm install
pnpm devAPI docs are available at http://localhost:8000/docs.
Settings are loaded from .env using Pydantic.
| Variable | Purpose |
|---|---|
SECRET_KEY |
JWT signing key |
ENCRYPTION_KEY |
Fernet key for encrypted stored tokens |
AUTH_MODE |
Authentication mode: LOCAL or ONLINE |
DATABASE_URL |
PostgreSQL, or SQLite for native dev |
COOKIE_SECURE |
Set True only behind HTTPS |
FRONTEND_URL |
Frontend origin for CORS and redirects |
SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET |
Optional global Spotify credentials |
DEVICE_AUTH_TOKEN |
Shared secret for device/invite flows |
BIND_IP |
Host interface Docker publishes on (default 0.0.0.0) |
HOST_PORT |
Host port mapped to container 8000 (default 8000) |
See .env.example for the complete configuration.
- JWT-based authentication with long-lived access tokens
- Tokens stored in an httpOnly cookie
- Argon2 password hashing
LOCALmode for local host login plus lightweight guest flowsONLINEmode for full registration flows- Token invalidation support through token versioning
- Cooldown and protection around incorrect PIN attempts
The backend follows a layered architecture with clear separation between API, business logic, persistence, and external integrations:
API (backend/api/)
-> Services (backend/services/)
-> Repositories (backend/repositories/)
-> Models (backend/db/models/)
Schemas (backend/schemas/) define request/response contracts
Adapters (backend/adapters/) integrate external providers
Drivers (backend/services/drivers/) isolate database-specific behavior
api/: HTTP routes, auth dependencies, and WebSocket entrypointsservices/: business logic and orchestrationrepositories/: data access and persistencedb/models/: SQLModel entitiesschemas/: validation and serializationadapters/: third-party service integrationsservices/drivers/: backend-specific runtime behavior for PostgreSQL and SQLite
- WebSocket events are used to broadcast room state changes in real time
- Queue and playback flows are handled in dedicated services
- PostgreSQL-specific coordination is isolated behind service/driver logic for operations that require stricter concurrency guarantees
uv run pytest
uv run pytest --cov=backend
uv run pytest -k "test_queue_service"pnpm -C frontend testuv run ruff check
uv run ruff formatbackend/
api/ # FastAPI routers, dependencies, WebSocket endpoints
adapters/ # External service integrations
core/ # Config, security, encryption, shared utilities
db/ # Database setup and SQLModel models
migrations/ # Alembic environment and migration versions
repositories/ # Data access layer
schemas/ # Pydantic schemas
services/ # Business logic
tests/ # Backend tests
frontend/
src/ # Vue SPA source code
public/ # Static public assets
docker/
entrypoint.sh # Container entrypoint
Project-specific conventions and assistant notes live in AGENTS.md and CLAUDE.md.
main: stable or release branchdevelop: integration branchfeature/*,fix/*,chore/*: working branches
This project follows Conventional Commits for all commits and pull request titles.
Common types: feat, fix, chore, docs, refactor, test, style, perf.
Examples:
feat(queue): add vote weight calculationfix(auth): prevent token reuse after invalidationchore(deps): bump fastapi to 0.115
This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
See LICENSE for the full text, or read the license summary at: https://www.gnu.org/licenses/agpl-3.0.html