A lightweight, self-hosted music streaming server built with Rust. Stream your local music library with a modern REST API and JWT authentication.
- πΆ Stream local music - MP3, FLAC, OGG, WAV, M4A, AAC, and more
- π JWT Authentication - Multi-user support with secure token-based auth
- π€ User Management - First user becomes admin, self-registration
- π Search & Filter - Search by title, artist, album, or genre
- π Pagination - Efficient browsing of large libraries
- πΌοΈ Cover Art - Extract and serve embedded album artwork
- π³ Docker Ready - Easy deployment with Docker Compose
- π Structured Logging - JSON logs for production, pretty logs for development
- π‘οΈ Security First - Path traversal protection, CORS configuration, input validation
# Clone the repository
git clone https://github.com/jcv-dev/ferrum.git
cd ferrum
# Create environment file
cp .env.example .env
# Edit .env and set JWT_SECRET to a secure random string
# Start the server
docker-compose up -d
# View logs
docker-compose logs -f# Prerequisites: Rust 1.75+
cargo --version
# Clone and build
git clone https://github.com/jcv-dev/ferrum.git
cd ferrum
cargo build --release
# Create music directory and add your files
mkdir -p music
# Copy your music files to ./music/
# Configure and run
cp .env.example .env
# Edit .env as needed
./target/release/ferrumAll configuration is done via environment variables. See .env.example for all options.
| Variable | Default | Description |
|---|---|---|
HOST |
0.0.0.0 |
Server bind address |
PORT |
8080 |
Server port |
MUSIC_FOLDER |
./music |
Path to your music library |
USERS_FILE |
./data/users.json |
User data storage location |
JWT_SECRET |
(random) | Secret key for signing tokens (set in production!) |
JWT_EXPIRY_DAYS |
7 |
Token validity period |
LOG_LEVEL |
info |
Logging level (trace, debug, info, warn, error) |
LOG_FORMAT |
pretty |
Log format (pretty or json) |
CORS_ORIGINS |
* |
Allowed CORS origins (comma-separated) |
curl -X POST http://localhost:8080/auth/register \
-H "Content-Type: application/json" \
-d '{"username": "myuser", "password": "mypassword123"}'Response:
{
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "myuser",
"is_admin": true,
"created_at": "2024-01-15T10:30:00Z"
},
"token": {
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 604800
}
}Note: The first registered user automatically becomes an admin.
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "myuser", "password": "mypassword123"}'curl http://localhost:8080/auth/me \
-H "Authorization: Bearer <token>"All music endpoints require authentication.
curl "http://localhost:8080/api/music/list?page=1&per_page=20" \
-H "Authorization: Bearer <token>"Query parameters:
q- Search query (title, artist, album)artist- Filter by artistalbum- Filter by albumgenre- Filter by genrepage- Page number (default: 1)per_page- Items per page (default: 50, max: 100)sort- Sort field:title,artist,album,year,durationorder- Sort order:asc,desc
Response:
{
"items": [
{
"id": "a1b2c3d4e5f67890",
"title": "Song Title",
"artist": "Artist Name",
"album": "Album Name",
"duration": 240,
"track_number": 1,
"year": 2023,
"genre": "Rock",
"format": "flac",
"file": "song.flac",
"has_cover": true
}
],
"total": 150,
"page": 1,
"per_page": 20,
"total_pages": 8,
"has_next": true,
"has_prev": false
}curl "http://localhost:8080/api/music/stream/song.mp3" \
-H "Authorization: Bearer <token>" \
--output song.mp3Supports HTTP range requests for seeking.
curl "http://localhost:8080/api/music/cover/song.mp3" \
-H "Authorization: Bearer <token>" \
--output cover.jpgcurl "http://localhost:8080/api/music/artists" \
-H "Authorization: Bearer <token>"curl "http://localhost:8080/api/music/albums" \
-H "Authorization: Bearer <token>"# Liveness check
curl http://localhost:8080/health
# Readiness check (verifies music folder is accessible)
curl http://localhost:8080/readyferrum/
βββ src/
β βββ main.rs # Application entry point
β βββ config.rs # Configuration management
β βββ error.rs # Error types and handling
β βββ models.rs # Data models
β βββ auth/
β β βββ mod.rs
β β βββ jwt.rs # JWT token handling
β β βββ middleware.rs # Auth extractors
β β βββ user_repository.rs # User storage
β βββ api/
β βββ mod.rs
β βββ auth.rs # Auth endpoints
β βββ health.rs # Health endpoints
β βββ music.rs # Music endpoints
βββ Cargo.toml
βββ Dockerfile
βββ docker-compose.yml
βββ .env.example
- MP3 (
.mp3) - FLAC (
.flac) - OGG Vorbis (
.ogg) - WAV (
.wav) - AAC/M4A (
.m4a,.aac) - WMA (
.wma) - Opus (
.opus) - AIFF (
.aiff) - APE (
.ape)
# Run with hot reload (requires cargo-watch)
cargo install cargo-watch
cargo watch -x run
# Run tests
cargo test
# Run clippy lints
cargo clippy -- -D warnings
# Format code
cargo fmt# Build optimized release binary
cargo build --release
# Binary will be at ./target/release/ferrumThe release build includes:
- LTO (Link Time Optimization)
- Single codegen unit for better optimization
- Stripped debug symbols
- Abort on panic (smaller binary)
MIT License - see LICENSE for details.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request