Navidrome Plugin System

Navidrome supports WebAssembly (Wasm) plugins for extending functionality. Plugins are loaded from the configured plugins folder and can provide additional metadata agents for fetching artist/album information.

Configuration

Enable plugins in your navidrome.toml:

[Plugins]
Enabled = true
Folder = "/path/to/plugins"   # Default: DataFolder/plugins

# Plugin-specific configuration (passed to plugins via Extism Config)
[PluginConfig.my-plugin]
api_key = "your-api-key"
custom_option = "value"

Plugin Structure

A Navidrome plugin is a WebAssembly (.wasm) file that:

  1. Exports nd_manifest: Returns a JSON manifest describing the plugin
  2. Exports capability functions: Implements the functions for its declared capabilities

Plugin Naming

Plugins are identified by their filename (without .wasm extension), not the manifest name field. This allows:

  • Users to resolve name conflicts by renaming files
  • Multiple instances of the same plugin with different names/configs
  • Simple, predictable naming

Example: my-musicbrainz.wasm → plugin name is my-musicbrainz

Plugin Manifest

Plugins must export an nd_manifest function that returns JSON:

{
  "name": "My Plugin",
  "author": "Author Name",
  "version": "1.0.0",
  "description": "Plugin description",
  "website": "https://example.com",
  "capabilities": ["MetadataAgent"],
  "permissions": {
    "http": {
      "reason": "Fetch metadata from external API",
      "allowedUrls": {
        "https://api.example.com/*": ["GET"]
      }
    }
  }
}

Capabilities

MetadataAgent

Provides artist and album metadata. Implement one or more of these functions:

Function Input Output Description
nd_get_artist_mbid {id, name} {mbid} Get MusicBrainz ID
nd_get_artist_url {id, name, mbid?} {url} Get artist URL
nd_get_artist_biography {id, name, mbid?} {biography} Get artist biography
nd_get_similar_artists {id, name, mbid?, limit} {artists: [{name, mbid?}]} Get similar artists
nd_get_artist_images {id, name, mbid?} {images: [{url, size}]} Get artist images
nd_get_artist_top_songs {id, name, mbid?, count} {songs: [{name, mbid?}]} Get top songs
nd_get_album_info {name, artist, mbid?} {name, mbid, description, url} Get album info
nd_get_album_images {name, artist, mbid?} {images: [{url, size}]} Get album images

Developing Plugins

Plugins can be written in any language that compiles to WebAssembly. We recommend using the Extism PDK for your language.

Go Example

package main

import (
    "encoding/json"
    "github.com/extism/go-pdk"
)

type Manifest struct {
    Name         string   `json:"name"`
    Author       string   `json:"author"`
    Version      string   `json:"version"`
    Capabilities []string `json:"capabilities"`
}

//go:wasmexport nd_manifest
func ndManifest() int32 {
    manifest := Manifest{
        Name:         "My Plugin",
        Author:       "Me",
        Version:      "1.0.0",
        Capabilities: []string{"MetadataAgent"},
    }
    out, _ := json.Marshal(manifest)
    pdk.Output(out)
    return 0
}

type ArtistInput struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

type BiographyOutput struct {
    Biography string `json:"biography"`
}

//go:wasmexport nd_get_artist_biography
func ndGetArtistBiography() int32 {
    var input ArtistInput
    if err := pdk.InputJSON(&input); err != nil {
        pdk.SetError(err)
        return 1
    }
    
    // Fetch biography from your data source...
    output := BiographyOutput{Biography: "Artist biography..."}
    if err := pdk.OutputJSON(output); err != nil {
        pdk.SetError(err)
        return 1
    }
    return 0
}

func main() {}

Build with TinyGo:

tinygo build -o my-plugin.wasm -target wasip1 -buildmode=c-shared ./main.go

Using HTTP

Plugins can make HTTP requests using the Extism PDK. The host controls which URLs are allowed via the permissions.http.allowedUrls manifest field.

//go:wasmexport nd_get_artist_biography
func ndGetArtistBiography() int32 {
    var input ArtistInput
    pdk.InputJSON(&input)
    
    req := pdk.NewHTTPRequest(pdk.MethodGet, 
        "https://api.example.com/artist/" + input.Name)
    resp := req.Send()
    
    // Process response...
    pdk.Output(resp.Body())
    return 0
}

Using Configuration

Plugins can read configuration values passed from navidrome.toml:

apiKey, ok := pdk.GetConfig("api_key")
if !ok {
    pdk.SetErrorString("api_key configuration is required")
    return 1
}

Security

Plugins run in a secure WebAssembly sandbox with these restrictions:

  1. URL Allowlisting: Only URLs listed in permissions.http.allowedUrls are accessible
  2. No File System Access: Plugins cannot access the file system
  3. No Network Listeners: Plugins cannot bind ports or create servers
  4. Config Isolation: Plugins receive only their own config section
  5. Memory Limits: Configurable via Extism

Using Plugins with Agents

To use a plugin as a metadata agent, add it to the Agents configuration:

Agents = "lastfm,spotify,my-plugin"  # my-plugin.wasm must be in the plugins folder

Plugins are tried in the order specified, just like built-in agents.