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:
- Exports
nd_manifest: Returns a JSON manifest describing the plugin - 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:
- URL Allowlisting: Only URLs listed in
permissions.http.allowedUrlsare accessible - No File System Access: Plugins cannot access the file system
- No Network Listeners: Plugins cannot bind ports or create servers
- Config Isolation: Plugins receive only their own config section
- 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.