- TypeScript 48.7%
- Rust 27.5%
- Vue 16%
- Python 3.5%
- Shell 1.9%
- Other 2.4%
| .husky | ||
| backend | ||
| deploy | ||
| docs | ||
| frontend | ||
| mobile | ||
| scripts | ||
| .dockerignore | ||
| .gitignore | ||
| DEPLOYMENT_GUIDE.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| package.json | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| README_BUILD_NEW_VERSION.md | ||
| TODO.md | ||
| turbo.json | ||
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.
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.
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.
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 authenticatedGET /api/media/tracks/{track_id}/streamendpoint.
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:
- Tailwind CSS 4 - Utility-first CSS framework
- Reka UI - Accessible, unstyled UI components
- @heroicons/vue - Beautiful hand-crafted SVG icons
- @radix-ui/colors - Professional color system
Developer Experience:
- Vite - Lightning-fast build tool and dev server
- TypeScript - Type-safe development
- Vitest - Fast unit testing framework
- ESLint + Oxlint - Code quality and linting
- Prettier - Code formatting
Data & API:
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
- Clone the repository:
git clone https://github.com/yourusername/tocata.git
cd tocata
- Install dependencies:
pnpm install
- 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 signingASSETS_COVER_DIR- Path to album covers on RPi (e.g.,/opt/tocata/covers)DB_BEETS_ABSOLUTE_PATH- Path to beets database on RPiMUSIC_ABSOLUTE_PATH- Path to music library on RPiALLOWED_CORS_URL- Frontend URL (e.g.,http://raspberrypi.local:8080)MPD_TOCATA_URL- MPD server address (usuallylocalhost: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
Docker Build (Recommended for Cross-Compilation)
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 deploymentdeploy/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 targetpnpm- Node.js package manageraarch64-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 dependenciestocata-pnpm-store- Node.js packagestocata-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.
