Skip to content

Project Overview ​

This document is based on the following source files:

Table of Contents ​

  1. Introduction
  2. Project Structure
  3. Core Features
  4. Technology Stack Overview
  5. System Architecture Overview
  6. Quick Start
  7. Conclusion

1. Introduction ​

Songloft is a self-hosted local music server designed to let users manage and play local music and network audio sources in a unified way on their own hardware. The current version of the project is 2.10.0; the source code is released as open source under the Apache 2.0 license.

Core value:

  • Full control -- Music files and metadata are stored locally by the user, with no dependency on third-party cloud services.
  • Cross-platform coverage -- A single backend paired with a Flutter frontend deploys one codebase to Android, iOS, macOS, Windows, Linux, and Web -- 6 platforms in total.
  • Extensible -- JS plugins run in a QuickJS sandbox, letting users develop their own audio source, lyrics, metadata, and other feature plugins.
  • Lightweight deployment -- The backend compiles into a single binary (with the frontend optionally embedded), SQLite has zero external dependencies, and Docker starts it up in one line.

Section sources


2. Project Structure ​

Songloft uses a multi-repository structure, with each subproject having clearly defined responsibilities:

DirectoryTechnologyDescription
/ (root)Go 1.26 + Chi v5 + SQLiteBackend API service, default port 58091
/mobileGo + gomobileMobile binding entry point for the Go backend (used by gomobile bind, exports Start/Stop/IsRunning/GetPort for Bundle local mode)
/clients/playerFlutter 3.29+ / Dart 3.7+Cross-platform frontend (separate repository songloft-player)
/plugins/toolchainTypeScript + pnpmJS plugin development toolchain: SDK, Builder, scaffolding (separate repository)
/plugins/srcTypeScriptCollection of JS plugin sources (submodules; each plugin distributes releases from its own repository)
/pkg/tagGoAudio metadata read/write library (extends the upstream tag library with MP3/FLAC writing)
/integrations/home-assistantHA add-onHome Assistant add-on (thin layer reusing the Docker image; separate repository, mounted as a submodule)

The backend internal/ directory follows the standard Go layout, preventing external packages from directly referencing internal implementation:

internal/
├── app/            # Application entry: initialization, route registration, config parsing
├── config/         # Startup configuration structure (AppConfig)
├── database/       # Database opening, migration, Repository, UnitOfWork
├── handlers/       # HTTP handler layer
├── httputil/       # HTTP proxy, shared Transport
├── jsplugin/       # JS plugin manager (load/unload/routing/health check/hot reload)
├── jsruntime/      # QuickJS runtime bridge (host capability injection)
├── middleware/     # Authentication, logging, and other middleware
├── models/         # Data model definitions
├── services/       # Business logic layer (Song/Playlist/Cache/Scanner/Source, etc.)
├── tracelycfg/     # Tracely monitoring compile-time injection configuration
└── version/        # Version number compile-time injection

Section sources


3. Core Features ​

3.1 Local Music Management ​

Automatically scans the specified music directory, extracting metadata (title, artist, album, cover art) from audio files, with support for MP3, FLAC, WAV, APE, OGG, Opus, M4A, M4B, WMA, AIF/AIFF, MKA, DSF (DSD) audio formats and MP4, MOV, M4V, MKV, WebM, AVI, TS, MPG, MPEG, FLV, WMV, RM/RMVB, 3GP video containers. Scan configuration (excluded directories, format list) is persisted in the database and can be modified dynamically at runtime.

3.2 Playlist Management ​

Two built-in system playlists, "Favorites" (id=1) and "Radio Favorites" (id=2), are provided. Users can freely create, edit, and delete custom playlists, with support for batch operations.

3.3 JS Plugin System ​

JavaScript plugins run in a QuickJS sandbox, with host capabilities such as http.fetch, storage, and logger provided through the host bridge layer. The plugin system includes:

  • Permission model -- The manifest declares permissions (net/storage/fs:music, etc.), validated at runtime.
  • Health check and hot reload -- File fingerprint monitoring; automatically reloads on change.
  • Common resource injection -- common.css / common.js / fonts are automatically injected into plugin HTML pages, with theme synchronization support.
  • Standalone toolchain -- The npx create-songloft-plugin@latest interactive scaffolding tool quickly creates plugins.

3.4 Audio Source Orchestration (Source Orchestrator) ​

A three-layer processing chain architecture, Fetcher -> Resolver -> Orchestrator, decouples audio source retrieval, resolution, and scheduling:

  • SourceResolver -- Ranks available audio source plugins by health metrics and selects the optimal plugin to resolve the song URL.
  • SourceFetcher -- Performs the actual HTTP request to fetch the audio stream, with support for URL validity verification.
  • SourceOrchestrator -- Coordinates the above two, handling old-request yielding during rapid song switching (playActivity registry).

3.5 HLS Radio Proxy ​

An optional HLS proxy mode where the server fetches and rewrites the .m3u8 playlist and proxies segments/keys/init segments. Supports the full set of classic HLS and LL-HLS (PART / PRELOAD-HINT / RENDITION-REPORT), with same-origin validation for SSRF protection.

3.6 Audio Cache ​

When playing remote songs, audio files are transparently cached to the server, with an LRU eviction policy (default 1 GB limit) and support for a custom cache directory. Concurrent requests for the same ID are deduplicated inflight and downloaded only once.

3.7 Fingerprint Deduplication ​

Detects duplicate songs based on audio fingerprints, automatically extracting and comparing fingerprints during scanning to avoid the same song being imported multiple times under different filenames.

3.8 Automatic Scanning ​

AutoScanner automatically scans the music directory at a user-configured time interval, automatically syncing added/changed/deleted files to the database. Scheduling state is restored from persisted configuration at startup.

3.9 Multi-Platform Frontend ​

The Flutter frontend covers six platforms: Android, iOS, macOS, Windows, Linux, and Web. Two deployment modes are supported:

  • Embedded mode -- The Flutter Web build artifact is embedded in the Go binary for single-file deployment.
  • Standalone mode -- The frontend is deployed independently and requires manually configuring the API address.

3.10 Bundle Local Mode (v2.9.0+) ​

Embeds the Go backend into the Flutter client, allowing users to use the app without deploying a separate server. Enabled at compile time with --dart-define=HAS_BACKEND=true.

  • Mobile (Android/iOS) -- The Go backend is compiled into a native library (.aar / .xcframework) via gomobile bind, called by Flutter through a MethodChannel (entry point mobile/mobile.go).
  • Desktop (macOS/Windows/Linux) -- The Go backend is compiled into a standalone executable songloft-server, launched by Flutter as a child process.
  • Web -- Bundle mode is not supported (remote server only).
  • Run modes -- RunMode.local (locally embedded backend at 127.0.0.1:<port>, auto-logging in with admin/admin after the health check) and RunMode.remote (remote server) can be switched, persisted to SharedPreferences, and automatically restored at startup.

Section sources


4. Technology Stack Overview ​

4.1 Backend ​

ComponentTechnology ChoicePurpose
LanguageGo 1.26Core language, compiled into a single CGO-free binary
HTTP RouterChi v5Lightweight router with middleware chain support
DatabaseSQLite (modernc.org/sqlite)Pure Go implementation, no CGO required, zero external dependencies
SQL GenerationsqlcCompile-time generation of type-safe code for fixed SQL
Dynamic SQLSquirrelDynamic construction of variable-length WHERE/SET
Database Migrationgoose v3Automatically runs goose.Up at startup
Authenticationgolang-jwt v5JWT dual token (access + refresh)
JS RuntimeQuickJS (modernc.org/quickjs)Pure Go binding, sandboxed execution of JS plugins
API Documentationswaggo/swagGenerates Swagger / OpenAPI from annotations
WebSocketgorilla/websocketReal-time communication (scan progress, etc.)
Compressionandybalholm/brotliBrotli compression middleware
MonitoringTracelyHeartbeat, install/upgrade statistics (optional, compile-time injected)

4.2 Frontend ​

ComponentTechnology ChoicePurpose
FrameworkFlutter 3.29+ / Dart 3.7+Cross-platform UI for 6 platforms
State ManagementRiverpodDeclarative state management
RoutingGoRouterDeclarative routing
Audio Playbackjust_audio + just_audio_media_kitCross-platform audio backend (all native platforms use libmpv/media_kit)

4.3 Plugin Toolchain ​

ComponentTechnology ChoicePurpose
LanguageTypeScriptPlugin development language
Package ManagementpnpmDependency management
Scaffoldingcreate-songloft-pluginInteractive creation of plugin projects

Section sources


5. System Architecture Overview ​

Songloft uses a classic three-tier architecture: the Flutter frontend communicates with the Go backend through an HTTP/REST API, and the backend operates on the SQLite database via the Repository pattern.

Diagram sources

5.1 Startup Flow ​

Application startup goes through the following key steps:

Diagram sources

5.2 Key Design Decisions ​

DecisionChoiceRationale
DatabaseSQLite (pure Go)Zero external dependencies, single-file deployment, modernc.org/sqlite requires no CGO
ORMNot usedFixed SQL generated at compile time with sqlc; dynamic SQL built with squirrel; cross-table writes via RunInTx + UnitOfWork
JS SandboxQuickJS (pure Go)Isolates plugin code, modernc.org/quickjs requires no CGO, distributed together with the binary
Memory ManagementGOMEMLIMIT=2GB + GOGC=50Proactively limits peak memory, trading more frequent GC for a smoother memory curve
Frontend Embeddingembed.FSSPA files embedded in the Go binary for single-file deployment with no additional web server
Cross-device File MovemoveFile wrapperTries os.Rename first, falls back to copy + remove on EXDEV, accommodating Docker volume scenarios

Section sources


6. Quick Start ​

6.1 Requirements ​

  • Go 1.26+ (backend compilation)
  • Flutter 3.29+ / Dart 3.7+ (frontend compilation, only needed when building the full version)
  • SQLite needs no separate installation (pure Go implementation)

6.2 CLI Arguments ​

./songloft [options]

Options:
  -port        Listening port (default: 58091)
  -db          Database file path (default: data/songloft.db)
  -username    Administrator username (default: admin)
  -password    Administrator password (default: admin)
  -base-path   URL base path, for reverse proxy sub-path deployment (e.g. /songloft)
  -version     Display version information
  -help        Display help information

6.3 Environment Variables ​

CLI arguments take precedence, with environment variables as a fallback:

Environment VariableCorresponding ArgumentDefault
ADMIN_USERNAME-usernameadmin
ADMIN_PASSWORD-passwordadmin
LISTEN_PORT-port58091
DB_PATH-dbdata/songloft.db
BASE_PATH-base-path(empty)
GOMEMLIMIT--2GB (default in code)
GOGC--50 (default in code)

6.4 Using Make Commands ​

bash
# Run in dev mode (with Swagger + pprof, account admin/admin)
make run

# Build the dev version (full, with frontend embedded)
make build

# Build the dev version (lite, without frontend embedded)
make build-lite

# Build the production version
make build-prod          # Full (frontend embedded)
make build-prod-lite     # Lite (without frontend)

# Testing
make test                # All tests
make test-short          # Quick tests (skip integration tests)
make check               # fmt + vet + test

# Database-related
make sqlc                # Regenerate sqlc code
make swagger             # Regenerate API documentation

6.5 Docker Deployment ​

bash
# Build and run
make docker-build
docker run -p 58091:58091 -e ADMIN_USERNAME=admin -e ADMIN_PASSWORD=admin \
  -v /path/to/music:/app/music -v /path/to/data:/app/data songloft:latest

6.6 Frontend Development ​

bash
cd songloft-player && flutter run -d chrome                                    # Standalone mode
cd songloft-player && flutter run -d chrome --dart-define=DEPLOY_MODE=embedded # Embedded mode
make build-frontend-web-embedded                                               # Build embedded artifact

6.7 Accessing the Service ​

  • Web UI: http://localhost:58091/
  • API Documentation (dev mode only): http://localhost:58091/swagger/index.html
  • Default account: admin / admin

Section sources


7. Conclusion ​

Songloft is a self-hosted music server with a clear architecture and simple deployment. The backend, implemented in Go, produces a single binary with zero CGO dependencies, covering core features such as local music management, audio source orchestration, HLS proxy, and audio caching. The frontend, implemented in Flutter, covers 6 platforms and can be embedded into the backend binary for single-file distribution. The JS plugin system provides secure extension capabilities based on a QuickJS sandbox, complemented by a standalone plugin toolchain that lowers the development barrier.

For a deeper understanding of the design details of each subsystem, refer to:

Section sources