Skip to content

Latest commit

 

History

35 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Syncphony

One shared music queue for a group of friends, across everyone's streaming services.

Each friend links their own service (self-hosted Navidrome, Spotify, and more later). Everyone searches and adds songs. Syncphony plays them in fair turns from whichever service each song came from, through one player: usually a phone running the web app, connected to a Bluetooth speaker.

Status: early. Phases 0–2 are in place: you can host a hangout with Navidrome and a phone on a Bluetooth speaker. See the roadmap below.

How it fits together

 friends' phones ──REST/WebSocket──▶  Syncphony server (Go)  ◀── provider interface ──▶  Navidrome
 (search, queue)                      queue · fairness · auth                         ──▶  Spotify
                                      credential vault · audio proxy                  ──▶  (future providers)
 player phone ──<audio> stream──────▶                    │
 └─ Bluetooth speaker                                    SQLite
  • Providers are plugins behind one Go interface. Each declares whether it streams audio through the server (Navidrome) or is driven as a remote player. Adding a service means adding a package, not touching the queue or the API.
  • Service credentials never leave the server. They're encrypted at rest, and the server proxies audio to the player.

Stack

Server Go (stdlib net/http), SQLite, WebSockets
API api/openapi.yaml is the source of truth. Go server types (oapi-codegen) and TS client types (openapi-typescript) are generated from it.
Web React 19, TypeScript, Vite, TanStack Router + Query, Tailwind v4, shadcn/ui, Motion
Ship One Docker image: the Go binary with the built web app embedded

Repo layout

api/openapi.yaml        HTTP API contract
server/                 Go module
  cmd/syncphony/        entrypoint, and the `syncphony vault` commands
  internal/config/      SYNCPHONY_* env config
  internal/httpapi/     API handlers and the room WebSocket (+ api.gen.go, generated)
  internal/auth/        accounts: invites, passkeys, passwords, sessions
  internal/vault/       envelope encryption for linked-service credentials
  internal/links/       linking service accounts, link health
  internal/realtime/    event bus and presence
  internal/rooms/       room snapshots and change announcements
  internal/store/       SQLite: goose migrations, sqlc queries (+ *.gen.go, generated)
  internal/provider/    provider interface, canonical types, registry
    fake/               in-memory provider for tests and UI development
    providertest/       conformance suite every provider runs
  internal/transcode/   ffmpeg fallback for formats the player can't decode
  internal/webui/       embedded web app (production builds)
web/                    React app
  src/routes/           file-based routes (TanStack Router)
  src/api/              typed API client (+ schema.gen.ts, generated)
  src/components/ui/    shadcn/ui components
docs/adr/               architecture decision records
deploy/
  Dockerfile            production image
  compose.yml           example deployment behind Caddy (HTTPS)
  dev/                  local Navidrome + synthetic sample library

Development

Requirements: Go 1.27+, Node 24+ with pnpm (corepack enable), Docker, and ffmpeg (optional; used to generate the sample library, falls back to Docker).

make setup   # install dependencies
make dev     # Navidrome + Go server (live reload) + Vite dev server

Other tasks: make gen (after editing api/openapi.yaml, or the SQL in server/internal/store), make test, make lint, make build (single binary at server/bin/syncphony), make docker. Run make help for the full list.

Configuration is via SYNCPHONY_* environment variables. See .env.example.

Deployment

SYNCPHONY_BASE_URL=https://syncphony.example.com docker compose -f deploy/compose.yml up -d

This serves on port 8080, ready for your existing reverse proxy. If you don't have one, add --profile caddy and Caddy handles HTTPS for the host in SYNCPHONY_BASE_URL.

First run: there are no accounts yet, so the server logs a one-time setup link (docker compose logs syncphony). Whoever opens it becomes the admin. The link lasts 24 hours, and restarting while there are still no accounts prints a new one. After that, Syncphony is invite-only: admins create invite links for friends. Everyone can sign in with a passkey, a password, or both.

Credential vault: linked-service credentials are encrypted at rest with a master key. If you don't set one, the server generates vault.key in the data directory on first run. That's convenient, but a backup of the data directory then holds both the key and the credentials it protects. For better protection, set SYNCPHONY_VAULT_KEY (or SYNCPHONY_VAULT_KEY_FILE, e.g. a Docker secret) and keep the key somewhere else. To rotate the key, run syncphony vault for the steps (docker compose exec syncphony syncphony vault rotate). Losing the key means everyone links their services again; nothing else is lost.

Images are published to ghcr.io/madeofpendletonwool/syncphony: :main tracks the main branch, and v* tags publish :X.Y.Z and :latest. Pick one with SYNCPHONY_TAG (e.g. SYNCPHONY_TAG=main).

Playing at a hangout

  1. Pair a phone with the Bluetooth speaker and open Syncphony on it. On iPhone, add it to the Home Screen first (Share → Add to Home Screen).
  2. On the Room screen, tap Play on this phone. That phone is now the speaker: it plays the queue, and its lock screen and the speaker's buttons control the room.
  3. Everyone else opens Syncphony on their own phone, searches, and adds songs to their lane.

What works where (and what doesn't yet) is in docs/player-mode.md.

Roadmap

  1. Foundation: repo, tooling, CI, image ✅
  2. Core platform: data model, invite-only accounts, credential vault, provider interface, realtime ✅
  3. Navidrome MVP: Navidrome provider, fair queue, playback engine, web app and phone player ✅
  4. Spotify provider
  5. Party features: vote-skip, fairness policies, history, cross-service track matching
  6. Native app and beyond: Capacitor app, more providers, multiple rooms

License

AGPL-3.0. If you run a modified Syncphony as a service for others, you must share your changes.

About

A music player that merges APIs from popular streaming services into one location for multiple location queueing

Resources

Stars

3 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages