--- url: /advanced/configuration.md description: Configuration options for Oktomusic --- [prisma_connection_url]: https://www.prisma.io/docs/orm/reference/connection-urls#postgresql # Configuration Reference Configuration for the app is provided with environment variables. ## App | Name | Description | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `NODE_ENV` | Application environment. One of `development`, `production`, or `test`. Default: `development`. | | `DATABASE_URL` | Prisma PostgreSQL [connection URL][prisma_connection_url]. Required. | | `SESSION_SECRET` | Secret string used for session encryption. Required. Keep secret in production. | | `APP_LIBRARY_PATH` | Path to the music library folder. Must exist and be a directory; the path is resolved at startup. | | `APP_INTERMEDIATE_PATH` | Path to store intermediate files (transcoding, etc) folder. Must exist and be a directory; the path is resolved at startup. | | `FFMPEG_PATH` | Optional path to the `ffmpeg` binary. | | `FFPROBE_PATH` | Optional path to the `ffprobe` binary. | | `METAFLAC_PATH` | Optional path to the `metaflac` binary. | ## HTTP | Name | Description | | ------------- | --------------------------------------------------------------------------------------------- | | `PORT` | Port number for the HTTP server to listen on. Default: `3000`. | | `TRUST_PROXY` | Enable reverse proxy support (can be `true`, `false`, or specific proxies). Default: `false`. | ## Valkey Valkey is used for session and queue storage. | Name | Description | | ----------------- | ---------------------------------------------------- | | `VALKEY_HOST` | Hostname of the Valkey server. Default: `localhost`. | | `VALKEY_PORT` | Port number for Valkey. Default: `6379`. | | `VALKEY_PASSWORD` | Optional password for Valkey (nullable). | ## OpenID Connect The backend authenticates users with an [OpenID Connect](https://openid.net/developers/how-connect-works) provider. The following variables configure the OIDC client used by the NestJS backend. | Name | Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `OIDC_ISSUER` | Base URL of the OIDC issuer (discovery endpoint base). Must be a valid URL (e.g. `https://auth.example.com/realms/main`). | | `OIDC_CLIENT_ID` | Client ID registered with the OIDC provider. Identifies the backend as a relying party. | | `OIDC_CLIENT_SECRET` | Client secret for token exchange. Keep this secret in production (use CI secrets or a vault). | | `OIDC_REDIRECT_URI` | Redirect URI that the provider will redirect back to after authentication. Must be a valid URL and match provider configuration. | | `OIDC_LOGOUT_REDIRECT_URI` | Optional post-logout redirect URI (frontchannel logout). If set, users are redirected here after logging out at the provider. | | `OIDC_SCOPES` | Scopes requested during authentication. Default: `openid profile offline_access`. `offline_access` enables refresh token support. | | `OIDC_RESPONSE_TYPE` | OIDC response type used for the authorization request. Typically `code` for the authorization code flow. Default: `code`. | | `OIDC_AUTO_DISCOVERY` | Whether to automatically fetch the provider's discovery document. Defaults to `true`. Set to `false` to configure endpoints manually. | | `OIDC_JWKS_CACHE_TTL` | Optional JWKS cache TTL (seconds) used when validating tokens to avoid frequent requests to the provider. Default: `3600`. | | `OIDC_ROLES_PATH` | JSON path used to extract user roles from the access token response. Supports `` placeholder. Default: `resource_access..roles`. | --- --- url: /guide/faq.md description: Frequently Asked Questions about Oktomusic --- # FAQ ## Why is only Chromium supported and not Firefox? There are multiple reasons but the main one is the terrible lack of modern app features in Firefox. Here are all many APIs the project uses or will use that are not supported by Firefox: * [Progressive Web Apps (PWA)](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) * [Background Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Background_Fetch_API) * [Document Picture-in-Picture API](https://developer.mozilla.org/en-US/docs/Web/API/Document_Picture-in-Picture_API) * [AudioSession API](https://www.w3.org/TR/audio-session) Some of the missing features are explicitly rejected by the Firefox team, like [PWA support](https://bugzilla.mozilla.org/show_bug.cgi?id=1682593) (later "replaced" by a half-baked kind of [taskbar bookmark](https://www.firefox.com/en-US/firefox/143.0/releasenotes) without actual PWA features). The monopoly of Chromium is sad (competition is always a good thing), but until Firefox, which was once one of the most inovative browser, stop shooting itself in the foot with stupid decisions and consequently [drop market share each year](https://gs.statcounter.com/browser-market-share), it just makes everything harder for the developer for a smaller and smaller number of users. --- --- url: /guide/installation.md description: Steps to install Oktomusic using Docker Compose --- [pg_upgrade_docker]: https://github.com/docker-library/postgres/issues/37 # Installation Guide The supported way to install Oktomusic is by using the provided Docker images. Here we will describe the steps to get Oktomusic up and running using Docker Compose. > \[!NOTE] > Oktomusic require the following external services to run: > > * [PostgreSQL](https://www.postgresql.org) 18 or higher (database) > * [Valkey](https://valkey.io) 9 or higher (session and queue storage) > * An [OpenID Connect](https://openid.net/developers/how-connect-works) provider like [Keycloak](https://www.keycloak.org) or [Authentik](https://goauthentik.io) (user authentication) > > Older versions of PostgreSQL and Valkey may work but are not officially tested. ## Prerequisites * [Docker Engine](https://docs.docker.com/engine) installed on your server system. * A OpenID Connect provider instance up and running. * [Keycloak documentation](https://www.keycloak.org/guides) * [Authentik documentation](https://docs.goauthentik.io/install-config/install/docker-compose) > \[!TIP] > If your target system isn't Linux based, you can also use [Docker Desktop](https://docs.docker.com/desktop) which runs on Windows and macOS. > > For production deployments, it's recommended to use a Linux based system and Docker Engine which have better performance than Docker Desktop. ## Docker Compose setup > \[!IMPORTANT] > This example assumes you already have an OpenID Connect provider (Keycloak, Authentik, …) running and configured. > > You must create an OIDC client and set the `OIDC_*` variables accordingly. > > See the [OpenID Connect configuration guide](./openid) for more information. `.env`: ```ini # PostgreSQL (Prisma connection URL) DATABASE_URL=postgresql://oktomusic:oktomusic@postgres:5432/oktomusic # Session encryption secret (generate a long random string) SESSION_SECRET=change-me-to-a-long-random-secret # Storage paths inside the container (must exist) # The library path is considered read-only by the application. # The intermediate path is used for temporary files (transcoding, lower resolution cover arts, etc). APP_LIBRARY_PATH=/srv/music APP_INTERMEDIATE_PATH=/srv/intermediate # Valkey (session + queue storage) VALKEY_HOST=valkey VALKEY_PORT=6379 VALKEY_PASSWORD=change-me # OpenID Connect # Example (Keycloak): https://auth.example.com/realms/my-realm-id OIDC_ISSUER=https://auth.example.com/realms/my-realm-id OIDC_CLIENT_ID=oktomusic OIDC_CLIENT_SECRET=change-me OIDC_ROLES_PATH=resource_access..roles # These must match your OIDC provider client settings. # If you expose Oktomusic on https://music.example.com, keep the paths and change the domain. OIDC_REDIRECT_URI=https://music.example.com/api/auth/callback OIDC_LOGOUT_REDIRECT_URI=https://music.example.com/ # --- Optional --- # PORT=3000 # TRUST_PROXY=false ``` `compose.yml`: ```yaml --- name: Oktomusic services: postgres: image: postgres:18-alpine environment: POSTGRES_USER: oktomusic POSTGRES_PASSWORD: oktomusic POSTGRES_DB: oktomusic ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U oktomusic"] interval: 5s timeout: 5s retries: 5 valkey: image: valkey/valkey:9-alpine environment: VALKEY_PASSWORD: ${VALKEY_PASSWORD} command: - valkey-server - "--port" - "6379" - "--requirepass" - "${VALKEY_PASSWORD}" - "--dir" - "/data" - "--appendonly" - "yes" - "--appendfilename" - "appendonly.aof" ports: - "6379:6379" volumes: - valkey_data:/data healthcheck: test: ["CMD-SHELL", "valkey-cli -a $$VALKEY_PASSWORD ping | grep PONG"] interval: 5s timeout: 5s retries: 10 start_period: 5s app: image: ghcr.io/oktomusic/oktomusic:latest env_file: - ./.env ports: - "3100:3000" volumes: # Replace the left side paths with directories on your host. # The source library path is set to read-only. - /path/to/your/music:/srv/music:ro - /path/to/your/intermediate:/srv/intermediate depends_on: postgres: condition: service_healthy valkey: condition: service_healthy volumes: postgres_data: valkey_data: ``` Start the stack: ```bash docker compose up -d ``` Then open `http://localhost:3100`. > \[!IMPORTANT] > The app purposely do not support TLS termination directly, which is more or less required by OpenID Connect providers. > > To get TLS support, you must run Oktomusic behind a reverse proxy like [Traefik](https://traefik.io), [Caddy](https://caddyserver.com) or [Nginx](https://nginx.org). > > It should be pretty straightforward to set up TLS termination with any of these proxies. > > You need to enable reverse proxy support in Oktomusic by setting the `TRUST_PROXY` environment variable to `true`. ### Behind a reverse proxy TODO --- --- url: /guide/llms.md description: Get LLMs assistance to setup Oktomusic --- # LLMs This project follows the [llms.txt](https://llmstxt.org) proposed standard to provide this documentation in a LLM readable format. The following files are availlable: * [`llms.txt`](/llms.txt) * [`llms-full.txt`](/llms-full.txt) Additionally, the documentation is availlable from the [Context7](https://context7.com/oktomusic/oktomusic) [MCP](https://modelcontextprotocol.io) server. Refer to your agent documentation for more informations on how to get the best from these resources. --- --- url: /guide/music-collection.md description: How to organize your music collection for use with Oktomusic --- # Music collection ## File organization The library is the single read-only source of truth for your music collection. It must use a tree structure with a folder per album, containing only [FLAC](https://xiph.org/flac) files. Each folder is validated separately, with all files having consistent metadata and the exact same album related metadata. Additionally, the cover of the album must be present as a file in the folder, named either `cover.png`, `cover.avif`, `cover.jpg` or `cover.jpeg`, taken by this order of preference. These will be converted to lossy [AVIF](https://en.wikipedia.org/wiki/AVIF) images of various sizes, so the ideal would be to use a lossless [PNG](https://en.wikipedia.org/wiki/PNG) or [AVIF](https://en.wikipedia.org/wiki/AVIF) image as source. The app supports lyrics stored as separate files in the album folder. Supported formats are [TTML](https://en.wikipedia.org/wiki/Timed_Text_Markup_Language) `.ttml` and both [LRC](https://en.wikipedia.org/wiki/LRC_\(file_format\)) and [Enhanced LRC](https://en.wikipedia.org/wiki/LRC_\(file_format\)#A2_extension_\(Enhanced_LRC_format\)) `.lrc`. To be picked up, the lyrics file must have the exact same base name as the corresponding track file, only with the relevant extension. If multiple matching lyric files exist, `.ttml` is preferred over `.lrc`. ## Individual file metadata FLAC files metadata is composed of [Vorbis comments](https://xiph.org/vorbis/doc/v-comment.html), and extracted by the server using [`metaflac`](https://github.com/xiph/flac). > \[!NOTE] > Sadly, the Vorbis standard doesn't clearly define tag names and formats, so we have to define our own rules. > > We try to stick as close as possible to [Vorbis recommendations](https://xiph.org/vorbis/doc/v-comment.html#fieldnames), as well as names used by [MusicBrainz Picard](https://picard-docs.musicbrainz.org/en/variables/tags_basic.html). | Tag Name | Required | Format | Unique | Multiple Allowed | | ------------- | -------- | ---------------------------------- | ------ | ---------------- | | `TITLE` | ✅ | String | ❌ | ❌ | | `ARTIST` | ✅ | String | ❌ | ✅ | | `ALBUM` | ✅ | String | ✅ | ❌ | | `TRACKNUMBER` | ✅ | Integer (1-based) | ❌ | ❌ | | `TOTALTRACKS` | ✅ | Integer (1-based) | ❌ | ❌ | | `DISCNUMBER` | ✅ | Integer (1-based) | ❌ | ❌ | | `TOTALDISCS` | ✅ | Integer (1-based) | ❌ | ❌ | | `ISRC` | ❌ | [ISRC code](https://isrc.ifpi.org) | ❌ | ❌ | > \[!IMPORTANT] > > * **Unique**: All files in the same album folder must have the exact same value for this tag > * **Multiple Allowed**: The tag may appear multiple times in the same file > * Splitting a tag value by separator (ex: `;`) is NOT supported and will never be > * `TOTALTRACKS` is the total number of tracks in the track's disc `DISCNUMBER` > * `TRACKNUMBER` is the number of the track in its disc `DISCNUMBER` > * `TOTALTRACKS`, `TOTALDISCS` are validated for consistency across all files in the album folder > * `DISCNUMBER` + `TRACKNUMBER` pairs are validated for uniqueness across all files in the album folder > * At the moment, two different artists with the same name cannot be distinguished. The indexing process might support an additional key like `MUSICBRAINZ_ARTISTID` in the future to solve this. ## Indexing process To allow moving/renaming files as well as modifying them (ex: replacing them with higher quality version) without breaking things like listening stats and playlists, we have to start with a stable data model that separate files from tracks. The first step of the indexing process is to read each album folder, extract metadata for each file, and validate it individually. Then, the album metadata is extracted from the files and validated for consistency. We then have to both check for duplicates inside the file collection, match the extracted albums to database and determine which ones will need to be created. An album in database is almost considered immutable and usually can't be updated by a later indexing job. Multiple albums may share the same name, so we have to check multiple criterias to uniquely identify an album: * Album name * Album artist name(s) * Track count per disc * ISRC or track names if no ISRC is present > \[!NOTE] > While ISRC codes aren't mandatory (as it prevent using "private", non published albums) > we use it instead of track names when possible to prevent rare cases of albums sharing the name/artist/track count and track names. > > One example would be albums released both in a explicit and non-explicit version, like *I'm Good (Blue)* by *David Guetta*, with the exact same metadata and track names except for the ISRC codes of the tracks. > > If an ISRC code is present for a track, it's name *can* be updated by the indexing process since we already have a unique way to identify the track. Finally music files are linked to their corresponding track in database, and any required transcoding/cover derived files are generated. > \[!NOTE] > Multiple albums can share individual tracks with the same ISRC code. > > Since we start from a flat files collection, we won't de-duplicate tracks files across albums > (a source file is always linked to a single track in a single album, not a ISRC). > > This may cause some data duplication (including on the client when downloading tracks). --- --- url: /guide/openid.md description: OpenID Connect configuration for Oktomusic --- # OpenID Connect configuration > \[!IMPORTANT] > Oktomusic delegates all user authentication to an external **OpenID Connect Provider**. > > The main reasons are: > > * Simplicity in the application architecture > * Delegate accounts security to specialized software > * Give full control for user management to the administrator > * Leverage existing battle-tested identity management solutions > > OpenID is a widely adopted standard with many availlable providers: > > * [Keycloak](https://www.keycloak.org) (exemple in this guide) > * [Authentik](https://goauthentik.io) > \[!NOTE] > For an introduction to OpenID Connect, please refer to the official documentation: [How OpenID Connect works](https://openid.net/developers/how-connect-works). > > * Oktomusic is a **OpenID Connect RP** (Relying Party) > * It delegates all user authentication to an external **OpenID Provider** (OP) > * It uses **Authorization Code Flow** with **PKCE** and server-side sessions > * It relies on **OpenID Connect Core** and **OpenID Connect Discovery** protocols ## Keycloak When using [Keycloak](https://www.keycloak.org) as **OpenID Provider**, you can follow these steps to create a client for Oktomusic. ### Login to Admin Console Keycloak use **realms** to isolate different environments. The default created realm is `master`, regardless if you create a new one or not, the realm name will be needed to configure Oktomusic later. ![Keycloak > Manage Realms](/assets/keycloak_admin_realms.B8jk_x4b.png) ### Create a new OpenID Client Now you can go to `Clients > Create client` to create a new OpenID Client. * Keep `Client type` to `OpenID Connect` * Set `Client ID` to a realm-unique name that will be used in URLs, such as `oktomusic` (or any other name you prefer) * Set other options as you prefer, then click `Next` ![Keycloak > Create Client > General Settings](/assets/keycloak_create_client_general.yOaATdVy.png) * Switch `Client Authentication` to `ON` * Switch `Authorization` to `ON` * Set `PKCE Method` to `S256` * Click `Next` ![Keycloak > Create Client > Capability Config](/assets/keycloak_create_client_capability.C0kVZnxe.png) * Set `Root URL` to your Oktomusic base URL, e.g. `https://oktomusic.example.com` * Set `Home URL` to `/` (relative to the base URL) * Add `/api/auth/callback` to `Valid Redirect URIs` * Add `/` to `Valid Post Logout Redirect URIs` * Click `Save` ![Keycloak > Create Client > Login Settings](/assets/keycloak_create_client_login.BazVsVfc.png) ### Create Roles The app relies on two specific roles to manage access control. `admin` and `user`. There are multiple ways to provide roles to clients, here we will create them as **Client Roles** to assign manually. You can create mappers to automatically assign client roles based on user attributes or groups later if needed. * Go in your client page, then click on the `Roles` tab * Click on `Add Role` and add a role named `user` * Go back and add another role named `admin` * This time in the role settings go to the `Associated Roles` tab * Assign the `user` role as an associated client role of `admin` * In the role list the `admin` role should be displayed as composite now ![Keycloak > Client Details > Roles](/assets/keycloak_create_role_assign.CZyarPsL.png) ### Configure Oktomusic ```ini # URL of the Keycloak realm # Constructed from your Keycloak domain and realm name OIDC_ISSUER=https:///realms/ # Client ID created previously OIDC_CLIENT_ID=oktomusic-dev # Client Secret generated by Keycloak # Retreive it from the 'Credentials' tab of your client OIDC_CLIENT_SECRET=change-me # Where Oktomusic should read roles from inside the *access token* (JWT). # # For Keycloak, if you created client roles # (as in this guide), they are typically exposed at: # resource_access..roles # The placeholder is replaced with OIDC_CLIENT_ID automatically. OIDC_ROLES_PATH=resource_access..roles ```