Mpd server api and web client
  • TypeScript 48.7%
  • Rust 27.5%
  • Vue 16%
  • Python 3.5%
  • Shell 1.9%
  • Other 2.4%
Find a file
2026-10-30 19:25:08 +01:00
.husky fix: resolve Rust warnings and add comprehensive pre-commit linters 2026-02-03 17:35:51 +01:00
backend [FEAT] Added Radio section to mobile 2026-10-30 17:22:22 +01:00
deploy [FEAT] Added Radio section to mobile 2026-10-30 17:22:22 +01:00
docs [FEAT] Updated readme imagens. Fixed error navigating from player to detail becouse no load album image 2026-03-29 20:44:37 +02:00
frontend [FEAT] Added Radio section to mobile 2026-10-30 17:22:22 +01:00
mobile [FEAT] become the app to responsibe to adapt in tablets 2026-10-30 19:25:08 +01:00
scripts [FEAT] Added Radio section to mobile 2026-10-30 17:22:22 +01:00
.dockerignore feat: add Docker-based cross-compilation environment for Raspberry Pi 2026-02-07 12:45:17 +01:00
.gitignore chore(repo): stop tracking tocata sqlite db 2026-03-26 14:08:15 +01:00
DEPLOYMENT_GUIDE.md [FEAT] Added 2FA auth 2026-09-27 11:09:41 +02:00
docker-compose.yml chore(repo): stop tracking tocata sqlite db 2026-03-26 14:08:15 +01:00
Dockerfile feat(deploy): add release service management scripts 2026-03-26 13:55:54 +01:00
LICENSE [FEAT] Added 2FA auth 2026-09-27 11:09:41 +02:00
package.json [FEAT] become the app to responsibe to adapt in tablets 2026-10-30 19:25:08 +01:00
pnpm-workspace.yaml [FEAT] Added new mobile app 2026-10-21 19:51:04 +02:00
README.md [FEAT] Added Radio section to mobile 2026-10-30 17:22:22 +01:00
README_BUILD_NEW_VERSION.md [CHORE] Added docs 2026-10-03 11:44:25 +02:00
TODO.md [FEAT] Added Radio section to mobile 2026-10-30 17:22:22 +01:00
turbo.json [FEAT] Fixed back navigation 2026-07-05 17:47:01 +02:00

TOCATA

AI-Assisted Development

Tocata is developed with extensive support from AI agents and chat-based AI models for discussion, architecture, implementation, testing, documentation, and review. This is stated explicitly as part of the project's transparency commitments; the direction of the project and the responsibility for accepting changes remain human.

Desktop Album List

📺 Video Demo

Overview

Tocata is a modern, self-hosted web interface for browsing and playing a music collection. On the local network it controls MPD (Music Player Daemon); from the public site it streams the original audio securely to the browser. It combines the library database generated by beets with its own SQLite database to provide a rich, album-centric browsing experience.

The application transforms your music library into an interactive visual collection, allowing you to explore and manage your albums with powerful filtering capabilities by genre, release year, artist, title, and more. Unlike traditional MPD clients, Tocata emphasizes the album as the fundamental unit, creating a more natural and engaging way to interact with your music collection.

Features

Responsive Design

Tocata offers a fully responsive interface that adapts seamlessly across devices - from desktop to mobile.

Mobile Album List Mobile Search

Advanced Filtering

Navigate your music library with sophisticated filtering options. Search and filter by artist, album, genre, year, and more to quickly find exactly what you're looking for.

Album Detail View

Get comprehensive information about each album with a dedicated detail view that displays track listings, metadata, and playback controls.

Desktop Album Detail Mobile Album Detail

Floating Player Controls

Access playback controls directly from the album list view with a floating player that shows current track information and progress.

Local Playback and Remote Streaming

Tocata automatically selects the playback engine from the hostname:

  • On the local deployment, playback is delegated to MPD and Tocata acts as its remote control.
  • On tocata.dkt-mlk.es, tracks are streamed directly to the browser through the authenticated GET /api/media/tracks/{track_id}/stream endpoint.

Remote playback currently serves original FLAC files without transcoding. HTTP byte ranges allow the browser to seek without loading an entire track into memory. The browser player maintains the album queue, advances between tracks, integrates with the Media Session controls exposed by compatible operating systems, and restores the active queue and playback position from the local browser session.

Streaming is same-origin and uses the regular HttpOnly access cookie. The backend resolves each track through the beets database, canonicalizes its path, and rejects files outside MUSIC_ABSOLUTE_PATH; physical filesystem paths are never returned to the client. Both local and remote sessions require username, password, and a six-digit TOTP code.

Vim-Style Navigation

Power users can navigate the album grid using Vim-inspired keyboard shortcuts, making it fast and efficient to browse and select albums without touching the mouse.

Technical Stack

Tocata is built as a modern monorepo managed by pnpm and Turbo, enabling efficient development workflows and optimized build processes.

Backend (Rust)

The backend is a high-performance REST API built with Rust, designed for speed, safety, and reliability.

Core Technologies:

  • Axum - Modern async web framework with excellent type safety
  • Tokio - Asynchronous runtime powering concurrent request handling
  • SQLx - Compile-time verified SQL queries with SQLite
  • mpd_client - Direct integration with Music Player Daemon

Security & Authentication: The backend implements a robust JWT-based authentication system with:

  • Secure HTTP-only cookies - Tokens stored in HTTP-only cookies to prevent XSS attacks
  • Access & Refresh token pattern - Short-lived access tokens (45 minutes) with long-lived refresh tokens (30 days)
  • Token rotation - Automatic token refresh for seamless user experience
  • HTTPS/TLS support - Built-in TLS using rustls for secure communication

Additional Features:

  • CORS middleware - Configured for cross-origin requests
  • Structured logging - Using tracing for observability
  • Type-safe API - TypeScript bindings auto-generated from Rust types using ts-rs
  • Authenticated media streaming - Original FLAC delivery with HTTP range support
  • Dual listeners - Local HTTPS and optional HTTP behind a trusted WireGuard/Traefik boundary

Frontend (Vue 3)

A modern, reactive single-page application built with the Vue 3 Composition API.

Core Technologies:

  • Vue 3 - Progressive JavaScript framework with Composition API
  • Vue Router - Client-side routing
  • Pinia - Intuitive state management
  • TanStack Query - Powerful async state management for server data

UI & Styling:

Developer Experience:

Data & API:

  • Axios - HTTP client with interceptors for auth
  • Zod - TypeScript-first schema validation

Project Structure

tocata/
├── backend/           # Rust/Axum API server
│   ├── src/
│   │   ├── auth/     # Password hashing and TOTP authentication
│   │   ├── cli/      # Local administration commands
│   │   ├── routes/   # API endpoints
│   │   ├── db/       # Database layer (SQLite)
│   │   └── utils/    # Utilities & helpers
│   └── Cargo.toml
├── frontend/          # Vue 3 SPA
│   ├── src/
│   │   ├── features/ # Feature-based modules
│   │   ├── shared/   # Shared components & utilities
│   │   └── router.ts
│   └── package.json
├── docs/
│   └── assets/       # Documentation images
├── scripts/          # Build, deployment, update, and migration scripts
└── turbo.json        # Turbo monorepo config

Getting Started

Prerequisites

  • Node.js 20.19+ or 22.12+
  • Rust 1.70+ (2024 edition)
  • pnpm 10.28+
  • MPD server running and accessible
  • beets music library tool (optional, for database generation)

Installation

  1. Clone the repository:
git clone https://github.com/yourusername/tocata.git
cd tocata
  1. Install dependencies:
pnpm install
  1. Set up environment variables:
# Backend (.env in backend/)
JWT_SECRET=your-secret-key
DATABASE_URL=sqlite:./db/tocata.db
MPD_HOST=localhost
MPD_PORT=6600

# Frontend (.env in frontend/)
VITE_APP_API_HOST=https://localhost:3000

Development

Run both frontend and backend in development mode:

pnpm dev

Or run them separately:

# Backend only
cd backend && pnpm dev

# Frontend only
cd frontend && pnpm dev

Local user administration

Users are created and updated only from the Raspberry Pi or another trusted local shell. Passwords are stored as salted Argon2id PHC hashes and every user is provisioned with a mandatory TOTP secret:

bin/tocata-service user configure manuel

Use --database /path/to/tocata.db to select an explicit existing database. The command never creates a missing database. It asks for the password without echo, shows a terminal QR and an otpauth:// fallback URI, verifies a six-digit code, and only then commits the password hash, TOTP secret and accepted timestep together. The QR is compatible with Proton Pass and standard TOTP authenticators.

Every login requires username, password and the current six-digit TOTP code. The confirmation code entered by user configure is recorded as consumed, so wait for the next authenticator code before the first login. The same mandatory flow is used over local HTTPS and remote streaming; TOTP is verified entirely on the Raspberry Pi and does not depend on Authelia, Internet connectivity or any external identity service. There is no web registration or password recovery UI.

For an existing installation, stop Tocata and run the explicit schema cutover before reprovisioning users:

scripts/migrate-auth-users-v2.sh /path/to/tocata.db
bin/tocata-service user configure manuel --database /path/to/tocata.db

The migration preserves the old plaintext table as users_legacy. Remove it manually only after all required users have been reprovisioned and tested.

Database migrations and release updates

Stop Tocata and make a backup of tocata.db before applying migrations to an existing installation. Release archives include both migration scripts under scripts/:

# Add the tag, genre, and search-index structures. This migration is idempotent.
scripts/migrate.sh /path/to/tocata.db

# Replace the legacy plaintext users table with Argon2id + TOTP credentials.
scripts/migrate-auth-users-v2.sh /path/to/tocata.db

The authentication migration deliberately preserves the previous table as users_legacy and does not copy plaintext passwords into the new schema. Reconfigure every required account with tocata-service user configure, test the accounts, and only then remove the legacy table manually.

For normal release upgrades, update.py validates the archive, creates timestamped backups, and updates the backend and frontend by default:

# Preview an update without changing the installation.
scripts/update.py /tmp/tocata-1.0.0-aarch64.tar.gz --dry-run

# Update the backend and frontend.
scripts/update.py /tmp/tocata-1.0.0-aarch64.tar.gz

# Update and rebuild the album search index.
scripts/update.py /tmp/tocata-1.0.0-aarch64.tar.gz --sync-search-index

# Only synchronize the search index.
scripts/update.py --sync-search-index

Use scripts/update.py --help for component-specific modes such as --bin-only, --frontend-only, --scripts, or --full. See scripts/UPDATE_GUIDE.md for backup, rollback, and path-selection details.

Managing Internet radios

Radio stations are stored in tocata.db; their thumbnails are copied into Tocata's managed data/radio-thumbnails/ directory. Add and inspect stations with the dedicated script:

scripts/radio.py add \
  --name "FluxFM Clubsandwich" \
  --url "https://channels.fluxfm.de/clubsandwich/externalembedflxhp/stream.mp3" \
  --thumbnail "/path/to/clubsandwich.png"

scripts/radio.py list

The script reads DB_TOCATA_RELATIVE_PATH and RADIO_THUMBNAILS_DIR from the release .env. Without an explicit thumbnail directory, it uses radio-thumbnails/ next to tocata.db. Use the global --db-path and --thumbnails-dir options before the add or list command to override those locations.

Configuration for Raspberry Pi

Before building for Raspberry Pi, create the environment configuration file:

# Copy template
cp deploy/.env.pi.example deploy/.env.pi

# Edit with your actual Raspberry Pi values
nano deploy/.env.pi

Important: The .env.pi file contains secrets and environment-specific paths. It should never be committed to the repository (already excluded in .gitignore).

Required values to configure:

  • JWT_SECRET - Strong random secret for JWT signing
  • ASSETS_COVER_DIR - Path to album covers on RPi (e.g., /opt/tocata/covers)
  • DB_BEETS_ABSOLUTE_PATH - Path to beets database on RPi
  • MUSIC_ABSOLUTE_PATH - Path to music library on RPi
  • ALLOWED_CORS_URL - Frontend URL (e.g., http://raspberrypi.local:8080)
  • MPD_TOCATA_URL - MPD server address (usually localhost:6600)

Building for Production

The root workspace exposes the current build and verification commands:

pnpm build          # Build all workspace packages through Turbo
pnpm test           # Run all workspace tests through Turbo
pnpm build:release  # Build and package a native release
pnpm build:pi       # Cross-compile and package an ARM64 release
pnpm docker:build   # Build the cross-compilation container
pnpm docker:build-pi
pnpm docker:test
pnpm docker:shell

Native release assembly requires deploy/.env.native; Raspberry Pi assembly requires deploy/.env.pi. Both commands create an assembled tree in deploy/out/ and a versioned archive in deploy/releases/.

Release-producing commands (build:release, build:pi, and docker:build-pi) run a Git and version preflight before compiling. The working tree must be clean and the root package.json version must be newer than the latest archive or release tag for that target. After packaging, a local tag named tocata-{target}-v{version} is created; push that tag so other checkouts retain the release history. The regular pnpm build command intentionally skips this guard for development.

For a new release, choose the appropriate SemVer increment, commit it with the source changes, and then build. For example:

pnpm version patch --no-git-tag-version
git add package.json
git commit -m "chore: release 1.0.1"
pnpm docker:build-pi
git push origin tocata-aarch64-v1.0.1

Build a native release:

pnpm build:release

Build for Raspberry Pi (ARM64):

pnpm build:pi

For building the Raspberry Pi version on macOS (or any platform without aarch64-linux-gnu toolchain), use the Docker-based build environment:

Prerequisites

  • Docker Desktop with arm64 support (native on Apple Silicon)

First-Time Setup

Build the Docker image (takes ~10-15 minutes on first run):

pnpm docker:build

Building for Raspberry Pi Zero 2W

Use the automated build script:

pnpm docker:build-pi

To rebuild the Docker image:

bash scripts/docker-build-pi.sh --rebuild

The build process generates:

  • deploy/out/ - Assembled files ready for deployment
  • deploy/releases/tocata-{version}-aarch64.tar.gz - Compressed release package

Transfer to your Raspberry Pi:

scp deploy/releases/tocata-*.tar.gz pi@raspberrypi:/tmp/

# On Raspberry Pi
ssh pi@raspberrypi
cd /tmp
tar -xzf tocata-*.tar.gz -C /opt/tocata/

Running Tests

Execute all tests (frontend + backend) inside the Docker container:

pnpm docker:test

Or run tests individually:

# Frontend tests
docker compose run --rm tocata-builder pnpm --filter frontend test

# Backend tests
docker compose run --rm tocata-builder cargo test --manifest-path backend/Cargo.toml

Interactive Development

Open a shell inside the container for manual operations:

pnpm docker:shell

Inside the container you have access to:

  • cargo - Rust toolchain with aarch64-unknown-linux-gnu target
  • pnpm - Node.js package manager
  • aarch64-linux-gnu-gcc - Cross-compilation toolchain

Verifying Build Output

Check that the binary was correctly cross-compiled:

file deploy/out/bin/tocata-service
# Should output: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV)...

# Check the release package
ls -lh deploy/releases/
# Should show: tocata-1.0.0-aarch64.tar.gz

Cache Management

Docker volumes persist build artifacts between runs for faster builds:

  • tocata-cargo-cache - Rust dependencies
  • tocata-pnpm-store - Node.js packages
  • tocata-target-cache - Compiled Rust artifacts

To clean all caches:

docker compose down -v

License

Tocata is licensed under the GNU Affero General Public License v3.0 only (AGPL-3.0-only).


Built with ❤️ for music lovers who appreciate both great sound and great code.