plugins

package
v0.61.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 12, 2026 License: GPL-3.0 Imports: 57 Imported by: 0

README

Navidrome Plugin System

Navidrome supports WebAssembly (Wasm) plugins for extending functionality. Plugins run in a secure sandbox and can provide metadata agents, scrobblers, and other integrations through host services like scheduling, caching, WebSockets, and Subsonic API access.

The plugin system is built on Extism, a cross-language framework for building WebAssembly plugins. This means you can write plugins in any language that Extism supports (Go, Rust, Python, TypeScript, and more) using their Plugin Development Kits (PDKs).

Essential Extism Resources:

Table of Contents


Quick Start

1. Create a minimal plugin

Create main.go:

package main

import "github.com/extism/go-pdk"

func main() {}

// Implement your capability functions here

Create manifest.json:

{
    "name": "My Plugin",
    "author": "Your Name",
    "version": "1.0.0"
}
2. Build with TinyGo and package as .ndp
# Compile to WebAssembly
tinygo build -o plugin.wasm -target wasip1 -buildmode=c-shared .

# Package as .ndp (zip archive)
zip -j my-plugin.ndp manifest.json plugin.wasm
3. Install

Copy my-plugin.ndp to your Navidrome plugins folder and enable plugins in your config:

[Plugins]
Enabled = true
Folder = "/path/to/plugins"

Plugin Basics

What is a Plugin?

A Navidrome plugin is an .ndp package file (zip archive) containing:

  1. manifest.json – Plugin metadata (name, author, version, permissions)
  2. plugin.wasm – Compiled WebAssembly module with capability functions
Plugin Package Structure
my-plugin.ndp (zip archive)
├── manifest.json    # Required: Plugin metadata
└── plugin.wasm      # Required: Compiled WebAssembly module
Plugin Naming

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

  • my-plugin.ndp → plugin ID is my-plugin
  • The manifest name is the display name shown in the UI

This allows users to have multiple instances of the same plugin with different configs by renaming the files.

The Manifest

Every plugin must include a manifest.json file. Example:

{
  "name": "My Plugin",
  "author": "Author Name",
  "version": "1.0.0",
  "description": "What this plugin does",
  "website": "https://example.com",
  "permissions": {
    "http": {
      "reason": "Fetch metadata from external API",
      "requiredHosts": ["api.example.com", "*.musicbrainz.org"]
    }
  }
}

Required fields: name, author, version

Experimental Features

Plugins can opt-in to experimental WebAssembly features that may change or be removed in future versions. Currently supported:

  • threads – Enables WebAssembly threads support (for plugins compiled with multi-threading)
{
  "name": "Threaded Plugin",
  "author": "Author Name",
  "version": "1.0.0",
  "experimental": {
    "threads": {
      "reason": "Required for concurrent audio processing"
    }
  }
}

Note: Experimental features may have compatibility or performance implications. Use only when necessary.


Capabilities

Capabilities define what your plugin can do. They're automatically detected based on which functions you export.

MetadataAgent

Provides artist and album metadata. Export 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

Example:

type ArtistInput struct {
    ID   string `json:"id"`
    Name string `json:"name"`
    MBID string `json:"mbid,omitempty"`
}

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..."}
    pdk.OutputJSON(output)
    return 0
}

To use the plugin as a metadata agent, add it to your config:

Agents = "lastfm,spotify,my-plugin"
Scrobbler

Integrates with external scrobbling services. Export one or more of these functions:

Function Input Output Description
nd_scrobbler_is_authorized {username} bool Check if user is authorized
nd_scrobbler_now_playing See below (none) Send now playing
nd_scrobbler_scrobble See below (none) Submit a scrobble

Important: Scrobbler plugins require the users permission in their manifest. Scrobble events are only sent for users assigned to the plugin through Navidrome's configuration. The nd_scrobbler_is_authorized function is called after the server-side user check passes.

Manifest permission:

{
  "permissions": {
    "users": {
      "reason": "Receive scrobble events for users assigned to this plugin"
    }
  }
}

NowPlaying/Scrobble Input:

{
  "username": "john",
  "track": {
    "id": "track-id",
    "title": "Song Title",
    "album": "Album Name",
    "artist": "Artist Name",
    "albumArtist": "Album Artist",
    "duration": 180.5,
    "trackNumber": 1,
    "discNumber": 1,
    "mbzRecordingId": "...",
    "mbzAlbumId": "...",
    "mbzArtistId": "..."
  },
  "timestamp": 1703270400
}

Error Handling:

On success, return 0. On failure, use pdk.SetError() with one of these error types:

  • scrobbler(not_authorized) – User needs to re-authorize
  • scrobbler(retry_later) – Temporary failure, Navidrome will retry
  • scrobbler(unrecoverable) – Permanent failure, scrobble discarded
import "github.com/navidrome/navidrome/plugins/pdk/go/scrobbler"

// Return error using predefined constants
return scrobbler.ScrobblerErrorNotAuthorized
return scrobbler.ScrobblerErrorRetryLater
return scrobbler.ScrobblerErrorUnrecoverable
Lifecycle

Optional initialization callback. Export this function to run code when your plugin loads:

Function Input Output Description
nd_on_init {} {error?} Called once after plugin loads

Useful for initializing connections, scheduling recurring tasks, etc.


Host Services

Host services let your plugin call back into Navidrome for advanced functionality. Each service requires declaring the permission in your manifest.

HTTP Requests

Make HTTP requests using the Extism PDK's built-in HTTP support. See your Extism PDK documentation for more details on making requests.

Manifest permission:

{
  "permissions": {
    "http": {
      "reason": "Fetch metadata from external API",
      "requiredHosts": ["api.example.com", "*.musicbrainz.org"]
    }
  }
}

Usage:

req := pdk.NewHTTPRequest(pdk.MethodGet, "https://api.example.com/data")
req.SetHeader("Authorization", "Bearer " + apiKey)
resp := req.Send()

if resp.Status() == 200 {
    data := resp.Body()
    // Process response...
}
Scheduler

Schedule one-time or recurring tasks. Your plugin must export nd_scheduler_callback to receive events.

Manifest permission:

{
  "permissions": {
    "scheduler": {
      "reason": "Schedule periodic metadata refresh"
    }
  }
}

Host functions:

Function Parameters Description
scheduler_scheduleonetime delaySeconds, payload, scheduleId? Schedule one-time callback
scheduler_schedulerecurring cronExpression, payload, scheduleId? Schedule recurring callback
scheduler_cancelschedule scheduleId Cancel a scheduled task

Callback function:

type SchedulerCallbackInput struct {
    ScheduleID  string `json:"scheduleId"`
    Payload     string `json:"payload"`
    IsRecurring bool   `json:"isRecurring"`
}

//go:wasmexport nd_scheduler_callback
func ndSchedulerCallback() int32 {
    var input SchedulerCallbackInput
    pdk.InputJSON(&input)

    // Handle the scheduled task based on payload
    pdk.Log(pdk.LogInfo, "Task fired: " + input.ScheduleID)
    return 0
}

Scheduling tasks (using generated SDK):

Add the generated SDK to your go.mod:

require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

Then import and use:

import "github.com/navidrome/navidrome/plugins/pdk/go/host"

// Schedule one-time task in 60 seconds
scheduleID, err := host.SchedulerScheduleOneTime(60, "my-payload", "")

// Schedule recurring task with cron expression (every hour)
scheduleID, err := host.SchedulerScheduleRecurring("0 * * * *", "hourly-task", "")

// Cancel a task
err := host.SchedulerCancelSchedule(scheduleID)
Cache

Store and retrieve data in an in-memory TTL-based cache. Each plugin has its own isolated namespace.

Manifest permission:

{
  "permissions": {
    "cache": {
      "reason": "Cache API responses to reduce external requests"
    }
  }
}

Host functions:

Function Parameters Description
cache_setstring key, value, ttl_seconds Store a string
cache_getstring key Get a string
cache_setint key, value, ttl_seconds Store an integer
cache_getint key Get an integer
cache_setfloat key, value, ttl_seconds Store a float
cache_getfloat key Get a float
cache_setbytes key, value, ttl_seconds Store bytes
cache_getbytes key Get bytes
cache_has key Check if key exists
cache_remove key Delete a cached value

TTL: Pass 0 for the default (24 hours), or specify seconds.

Usage (with generated SDK):

Import the Go SDK (see Scheduler for go.mod setup):

import "github.com/navidrome/navidrome/plugins/pdk/go/host"

// Cache a value for 1 hour
host.CacheSetString("api-response", responseData, 3600)

// Retrieve (check Exists before using Value)
result, err := host.CacheGetString("api-response")
if result.Exists {
    data := result.Value
}

Note: Cache is in-memory only and cleared on server restart.

KVStore

Persistent key-value storage that survives server restarts. Each plugin has its own isolated SQLite database.

Manifest permission:

{
  "permissions": {
    "kvstore": {
      "reason": "Store OAuth tokens and plugin state",
      "maxSize": "1MB"
    }
  }
}

Permission options:

  • maxSize: Maximum storage size (e.g., "1MB", "500KB"). Default: 1MB

Host functions:

Function Parameters Description
kvstore_set key, value Store a byte value
kvstore_get key Retrieve a byte value
kvstore_delete key Delete a value
kvstore_has key Check if key exists
kvstore_list prefix List keys matching prefix
kvstore_getstorageused - Get current storage usage (bytes)

Key constraints:

  • Maximum key length: 256 bytes
  • Keys must be valid UTF-8 strings

Usage (with generated SDK):

Import the Go SDK (see Scheduler for go.mod setup):

import "github.com/navidrome/navidrome/plugins/pdk/go/host"

// Store a value (as raw bytes)
token := []byte(`{"access_token": "xyz", "refresh_token": "abc"}`)
_, err := host.KVStoreSet("oauth:spotify", token)

// Retrieve a value
result, err := host.KVStoreGet("oauth:spotify")
if result.Exists {
    var tokenData map[string]string
    json.Unmarshal(result.Value, &tokenData)
}

// List all keys with prefix
keysResult, err := host.KVStoreList("user:")
for _, key := range keysResult.Keys {
    // Process each key
}

// Check storage usage
usageResult, err := host.KVStoreGetStorageUsed()
fmt.Printf("Using %d bytes\n", usageResult.Bytes)

// Delete a value
host.KVStoreDelete("oauth:spotify")

Note: Unlike Cache, KVStore data persists across server restarts. Storage is located at ${DataFolder}/plugins/${pluginID}/kvstore.db.

WebSocket

Establish persistent WebSocket connections to external services.

Manifest permission:

{
  "permissions": {
    "websocket": {
      "reason": "Real-time connection to service",
      "requiredHosts": ["gateway.example.com", "*.discord.gg"]
    }
  }
}

Host functions:

Function Parameters Description
websocket_connect url, headers?, connectionId? Open a connection
websocket_sendtext connectionId, message Send text message
websocket_sendbinary connectionId, data Send binary data
websocket_close connectionId, code?, reason? Close connection

Callback functions (export these to receive events):

Function Input Description
nd_websocket_on_text_message {connectionId, message} Text message received
nd_websocket_on_binary_message {connectionId, data} Binary message received (base64)
nd_websocket_on_error {connectionId, error} Connection error
nd_websocket_on_close {connectionId, code, reason} Connection closed
Library

Access music library metadata and optionally read files from library directories.

Manifest permission:

{
  "permissions": {
    "library": {
      "reason": "Access library metadata for analysis",
      "filesystem": false
    }
  }
}
  • filesystem – Set to true to enable read-only access to library directories (default: false)

Host functions:

Function Parameters Returns
library_getlibrary id Library metadata
library_getalllibraries (none) Array of library metadata

Library metadata:

{
  "id": 1,
  "name": "My Music",
  "path": "/music/collection",
  "mountPoint": "/libraries/1",
  "lastScanAt": 1703270400,
  "totalSongs": 5000,
  "totalAlbums": 500,
  "totalArtists": 200,
  "totalSize": 50000000000,
  "totalDuration": 1500000.5
}

Note: The path and mountPoint fields are only included when filesystem: true is set in the permission.

Filesystem access:

When filesystem: true, your plugin can read files from library directories via WASI filesystem APIs. Each library is mounted at /libraries/<id>:

import "os"

// Read a file from library 1
content, err := os.ReadFile("/libraries/1/Artist/Album/track.mp3")

// List directory contents
entries, err := os.ReadDir("/libraries/1/Artist")

Security: Filesystem access is read-only and restricted to configured library paths only. Plugins cannot access other parts of the host filesystem.

Usage (with generated SDK):

Import the Go SDK (see Scheduler for go.mod setup). The Library struct is provided by the SDK:

import "github.com/navidrome/navidrome/plugins/pdk/go/host"

// Get a specific library
resp, err := host.LibraryGetLibrary(1)
if err != nil {
    // Handle error
}
library := resp.Result

// Get all libraries
resp, err := host.LibraryGetAllLibraries()
for _, lib := range resp.Result {
    // lib is of type host.Library
    fmt.Printf("Library: %s (%d songs)\n", lib.Name, lib.TotalSongs)
}
Artwork

Generate public URLs for Navidrome artwork (albums, artists, tracks, playlists).

Manifest permission:

{
  "permissions": {
    "artwork": {
      "reason": "Get artwork URLs for display"
    }
  }
}

Host functions:

Function Parameters Returns
artwork_getartisturl id, size Artwork URL
artwork_getalbumurl id, size Artwork URL
artwork_gettrackurl id, size Artwork URL
artwork_getplaylisturl id, size Artwork URL
SubsonicAPI

Call Navidrome's Subsonic API internally (no network round-trip).

Manifest permission:

{
  "permissions": {
    "subsonicapi": {
      "reason": "Access library data"
    },
    "users": {
      "reason": "Access user information for SubsonicAPI authorization"
    }
  }
}

Important: The subsonicapi permission requires the users permission. User access is controlled through the plugin's database configuration, not the manifest. Configure which users can use the plugin through the Navidrome UI or API.

Host function:

Function Parameters Returns
subsonicapi_call uri JSON response

Usage:

// The URI must include the 'u' parameter with the username
response, err := SubsonicAPICall("getAlbumList2?type=random&size=10&u=username")
Config

Access plugin configuration values programmatically. Unlike pdk.GetConfig() which only retrieves individual values, this service can list all available configuration keys—useful for discovering dynamic configuration (e.g., user-to-token mappings).

Note: This service is always available and does not require a manifest permission.

Host functions:

Function Parameters Returns
config_get key value, exists
config_getint key value, exists
config_keys prefix Array of matching key names

Usage (with generated SDK):

import "github.com/navidrome/navidrome/plugins/pdk/go/host"

// Get a string configuration value
value, exists := host.ConfigGet("api_key")
if exists {
    // Use the value
}

// Get an integer configuration value
count, exists := host.ConfigGetInt("max_retries")

// List all keys with a prefix (useful for user-specific config)
keys := host.ConfigKeys("user:")
for _, key := range keys {
    // key might be "user:john", "user:jane", etc.
}

// List all configuration keys
allKeys := host.ConfigKeys("")
Users

Access user information for the users that the plugin has been granted access to. This is useful for plugins that need to associate data with specific users or display user information.

Manifest permission:

{
  "permissions": {
    "users": {
      "reason": "Display user information in status updates"
    }
  }
}

Important: Before enabling a plugin that requires the users permission, an administrator must configure which users the plugin can access. This can be done in two ways:

  1. Allow all users – Enable the "Allow all users" toggle in the plugin settings
  2. Select specific users – Choose individual users from the user list

If neither option is configured, the plugin cannot be enabled.

Host functions:

Function Parameters Returns
users_getusers Array of User objects

User object fields:

Field Type Description
userName string The user's unique username
name string The user's display name
isAdmin boolean Whether the user is an admin

Security: Sensitive fields like passwords, email addresses, and internal IDs are never exposed to plugins.

Usage (with generated SDK):

import "github.com/navidrome/navidrome/plugins/pdk/go/host"

// Get all users the plugin has access to
users, err := host.UsersGetUsers()
if err != nil {
    pdk.Log(pdk.LogError, "Failed to get users: " + err.Error())
    return
}

for _, user := range users {
    pdk.Log(pdk.LogInfo, "User: " + user.UserName + " (" + user.Name + ")")
    if user.IsAdmin {
        pdk.Log(pdk.LogInfo, "  - Administrator")
    }
}

Rust example:

use nd_pdk_host::users::get_users;

let users = get_users()?;
for user in users {
    println!("User: {} ({})", user.user_name, user.name);
}

Python example:

from host.nd_host_users import users_get_users

users = users_get_users()
for user in users:
    print(f"User: {user['userName']} ({user['name']})")

Configuration

Server Configuration

Enable plugins in navidrome.toml:

[Plugins]
Enabled = true
Folder = "/path/to/plugins"   # Default: DataFolder/plugins
AutoReload = true             # Auto-reload on file changes (dev mode)
LogLevel = "debug"            # Plugin-specific log level
CacheSize = "200MB"           # Compilation cache size limit
Plugin Configuration

Plugin configuration is managed through the Navidrome web UI. Navigate to the Plugins page, select a plugin, and edit its configuration as key-value pairs.

Access configuration values in your plugin:

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

Building Plugins

Supported Languages

Plugins can be written in any language that Extism supports. Each language has its own PDK (Plugin Development Kit) that provides the APIs for I/O, logging, configuration, and HTTP requests. See the Extism PDK documentation for details.

We recommend:

  • Go – Best experience with TinyGo and the Go PDK
  • Rust – Excellent performance with the Rust PDK
  • Python – Experimental support via extism-py
  • TypeScript – Experimental support via extism-js
# Install TinyGo: https://tinygo.org/getting-started/install/

# Build WebAssembly module
tinygo build -o plugin.wasm -target wasip1 -buildmode=c-shared .

# Package as .ndp
zip -j my-plugin.ndp manifest.json plugin.wasm
Using Go PDK Packages

Navidrome provides type-safe Go packages for each capability in plugins/pdk/go/. Instead of manually exporting functions with //go:wasmexport, use the Register() pattern:

package main

import (
    "github.com/navidrome/navidrome/plugins/pdk/go/metadata"
)

type myPlugin struct{}

func (p *myPlugin) GetArtistBiography(input metadata.ArtistRequest) (*metadata.ArtistBiographyResponse, error) {
    return &metadata.ArtistBiographyResponse{Biography: "Biography text..."}, nil
}

func init() {
    metadata.Register(&myPlugin{})
}

func main() {}

Add to your go.mod:

require github.com/navidrome/navidrome v0.0.0
replace github.com/navidrome/navidrome => ../../..

Available capability packages:

Package Import Path Description
metadata plugins/pdk/go/metadata Artist/album metadata providers
scrobbler plugins/pdk/go/scrobbler Scrobbling services
lifecycle plugins/pdk/go/lifecycle Plugin initialization
scheduler plugins/pdk/go/scheduler Scheduled task callbacks
websocket plugins/pdk/go/websocket WebSocket event handlers
host plugins/pdk/go/host Host service SDK (HTTP, cache, etc.)

See the example plugins in examples/ for complete usage patterns.

Rust
# Build WebAssembly module
cargo build --release --target wasm32-wasip1

# Package as .ndp
zip -j my-plugin.ndp manifest.json target/wasm32-wasip1/release/plugin.wasm
Using Rust PDK

The Rust PDK provides generated type-safe wrappers for both capabilities and host services:

# Cargo.toml
[dependencies]
nd-pdk = { path = "../../pdk/rust/nd-pdk" }
extism-pdk = "1.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Implementing capabilities with traits and macros:

use nd_pdk::scrobbler::{Scrobbler, IsAuthorizedRequest, Error};
use nd_pdk::register_scrobbler;

#[derive(Default)]
struct MyPlugin;

impl Scrobbler for MyPlugin {
    fn is_authorized(&self, req: IsAuthorizedRequest) -> Result<bool, Error> {
        Ok(true)
    }
    fn now_playing(&self, req: NowPlayingRequest) -> Result<(), Error> { Ok(()) }
    fn scrobble(&self, req: ScrobbleRequest) -> Result<(), Error> { Ok(()) }
}

register_scrobbler!(MyPlugin);  // Generates all WASM exports

Using host services:

use nd_pdk::host::{cache, scheduler, library};

// Cache a value for 1 hour
cache::set_string("my_key", "my_value", 3600)?;

// Schedule a recurring task
scheduler::schedule_recurring("@every 5m", "payload", "task_id")?;

// Access library metadata
let libs = library::get_all_libraries()?;

See pdk/rust/README.md for detailed documentation and examples.

Python (with extism-py)
# Build WebAssembly module (requires extism-py installed)
extism-py plugin.wasm -o plugin.wasm *.py

# Package as .ndp
zip -j my-plugin.ndp manifest.json plugin.wasm
Using XTP CLI (Scaffolding)

Bootstrap a new plugin from a schema:

# Install XTP CLI: https://docs.xtp.dylibso.com/docs/cli

# Create a metadata agent plugin
xtp plugin init \
  --schema-file plugins/capabilities/metadata_agent.yaml \
  --template go \
  --path ./my-agent \
  --name my-agent

# Build and package
cd my-agent && xtp plugin build
zip -j my-agent.ndp manifest.json dist/plugin.wasm

See capabilities/README.md for available schemas and scaffolding examples.

Using Host Service SDKs

Generated SDKs for calling host services are in plugins/pdk/go/, plugins/pdk/python/ and plugins/pdk/rust.

For Go plugins: Import the SDK as a Go module:

import "github.com/navidrome/navidrome/plugins/pdk/go/host"

Add to your go.mod:

require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

See pdk/go/README.md for detailed documentation.

For Python plugins: Copy functions from nd_host_*.py into your __init__.py (see comments in those files for extism-py limitations).

Recommendations:

  • Go: Best overall experience with excellent stdlib support and familiar syntax for most developers. Recommended if you're already in the Go ecosystem.
  • Rust: Best for performance-critical plugins or when leveraging Rust's ecosystem. Produces smallest binaries with excellent type safety.
  • Python: Best for rapid prototyping or simple plugins. Note that extism-py has limitations compared to compiled languages.

Examples

See examples/ for complete working plugins:

Plugin Language Capabilities Host Services Description
minimal Go MetadataAgent Basic structure example
wikimedia Go MetadataAgent HTTP Wikidata/Wikipedia integration
coverartarchive-py Python MetadataAgent HTTP Cover Art Archive
webhook-rs Rust Scrobbler HTTP HTTP webhooks
nowplaying-py Python Lifecycle Scheduler, SubsonicAPI Periodic now-playing logger
library-inspector Rust Lifecycle Library, Scheduler Periodic library stats logging
crypto-ticker Go Lifecycle WebSocket, Scheduler Real-time crypto prices demo
discord-rich-presence-rs Rust Scrobbler HTTP, WebSocket, Cache, Scheduler, Artwork Discord integration (Rust)

Security

Plugins run in a secure WebAssembly sandbox provided by Extism and the Wazero runtime:

  1. Host Allowlisting – Only explicitly allowed hosts are accessible via HTTP/WebSocket
  2. Limited File System – Plugins can only access library directories when explicitly granted the library.filesystem permission, and access is read-only
  3. No Network Listeners – Plugins cannot bind ports
  4. Config Isolation – Plugins only receive their own config section
  5. Memory Limits – Controlled by the WebAssembly runtime
  6. User-Scoped Authorization – Plugins with subsonicapi or scrobbler capabilities can only access/receive events for users assigned to them through Navidrome's configuration. The users permission is required for these features.
  7. Users Permission – Plugins requesting user access must be explicitly configured with allowed users; sensitive data (passwords, emails) is never exposed

Runtime Management

Auto-Reload

With AutoReload = true, Navidrome watches the plugins folder and automatically detects when .ndp files are added, modified, or removed. When a plugin file changes, the plugin is disabled and its metadata is re-read from the archive.

If the AutoReload setting is disabled, Navidrome needs to be restarted to pick up plugin changes.

Enabling/Disabling Plugins

Plugins can be enabled/disabled via the Navidrome UI. The plugin state is persisted in the database.

Important Notes
  • In-flight requests – When reloading, existing requests complete before the new version takes over
  • Config changes – Changes to the plugin configuration in the UI are applied immediately
  • Cache persistence – The in-memory cache is cleared when a plugin is unloaded

Documentation

Index

Constants

View Source
const (
	FuncWebSocketOnTextMessage   = "nd_websocket_on_text_message"
	FuncWebSocketOnBinaryMessage = "nd_websocket_on_binary_message"
	FuncWebSocketOnError         = "nd_websocket_on_error"
	FuncWebSocketOnClose         = "nd_websocket_on_close"
)

WebSocket callback function names

View Source
const (
	FuncGetArtistMBID           = "nd_get_artist_mbid"
	FuncGetArtistURL            = "nd_get_artist_url"
	FuncGetArtistBiography      = "nd_get_artist_biography"
	FuncGetSimilarArtists       = "nd_get_similar_artists"
	FuncGetArtistImages         = "nd_get_artist_images"
	FuncGetArtistTopSongs       = "nd_get_artist_top_songs"
	FuncGetAlbumInfo            = "nd_get_album_info"
	FuncGetAlbumImages          = "nd_get_album_images"
	FuncGetSimilarSongsByTrack  = "nd_get_similar_songs_by_track"
	FuncGetSimilarSongsByAlbum  = "nd_get_similar_songs_by_album"
	FuncGetSimilarSongsByArtist = "nd_get_similar_songs_by_artist"
)

Export function names (snake_case as per design)

View Source
const (
	FuncScrobblerIsAuthorized = "nd_scrobbler_is_authorized"
	FuncScrobblerNowPlaying   = "nd_scrobbler_now_playing"
	FuncScrobblerScrobble     = "nd_scrobbler_scrobble"
)

Scrobbler function names (snake_case as per design)

View Source
const (
	FuncLyricsGetLyrics = "nd_lyrics_get_lyrics"
)
View Source
const FuncOnInit = "nd_on_init"
View Source
const FuncSchedulerCallback = "nd_scheduler_callback"
View Source
const FuncTaskWorkerCallback = "nd_task_execute"
View Source
const (
	// PackageExtension is the file extension for Navidrome plugin packages.
	PackageExtension = ".ndp"
)

Variables

This section is empty.

Functions

func ValidateConfig added in v0.60.0

func ValidateConfig(manifest *Manifest, configJSON string) error

ValidateConfig validates a config JSON string against a plugin's config schema. If the manifest has no config schema, it returns an error indicating the plugin has no configurable options. Returns nil if validation passes, ConfigValidationErrors if validation fails.

func ValidateWithCapabilities added in v0.60.0

func ValidateWithCapabilities(m *Manifest, capabilities []Capability) error

ValidateWithCapabilities validates the manifest against detected capabilities. This must be called after WASM capability detection since Scrobbler capability is detected from exported functions, not manifest declarations.

Types

type ArtworkPermission added in v0.60.0

type ArtworkPermission struct {
	// Explanation for why artwork access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

Artwork service permissions for generating artwork URLs

type CachePermission added in v0.60.0

type CachePermission struct {
	// Explanation for why cache access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

Cache service permissions for storing and retrieving data

type Capability added in v0.60.0

type Capability string

Capability represents a plugin capability type. Capabilities are detected by checking which functions a plugin exports.

const CapabilityLifecycle Capability = "Lifecycle"

CapabilityLifecycle indicates the plugin has lifecycle callback functions. Detected when the plugin exports the nd_on_init function.

const CapabilityLyrics Capability = "Lyrics"
const CapabilityMetadataAgent Capability = "MetadataAgent"

CapabilityMetadataAgent indicates the plugin can provide artist/album metadata. Detected when the plugin exports at least one of the metadata agent functions.

const CapabilityScheduler Capability = "Scheduler"

CapabilityScheduler indicates the plugin can receive scheduled event callbacks. Detected when the plugin exports the scheduler callback function.

const CapabilityScrobbler Capability = "Scrobbler"

CapabilityScrobbler indicates the plugin can receive scrobble events. Detected when the plugin exports at least one of the scrobbler functions.

const CapabilityTaskWorker Capability = "TaskWorker"

CapabilityTaskWorker indicates the plugin can receive task execution callbacks.

const CapabilityWebSocket Capability = "WebSocket"

CapabilityWebSocket indicates the plugin can receive WebSocket callbacks. Detected when the plugin exports any of the WebSocket callback functions.

type ConfigDefinition added in v0.60.0

type ConfigDefinition struct {
	// JSON Schema (draft-07) defining the plugin's configuration options
	Schema map[string]interface{} `json:"schema" yaml:"schema" mapstructure:"schema"`

	// Optional JSONForms UI Schema for customizing form layout
	UiSchema map[string]interface{} `json:"uiSchema,omitempty" yaml:"uiSchema,omitempty" mapstructure:"uiSchema,omitempty"`
}

Configuration schema for the plugin using JSON Schema (draft-07) and optional JSONForms UI Schema

func (*ConfigDefinition) UnmarshalJSON added in v0.60.0

func (j *ConfigDefinition) UnmarshalJSON(value []byte) error

UnmarshalJSON implements json.Unmarshaler.

type ConfigValidationError added in v0.60.0

type ConfigValidationError struct {
	Field   string `json:"field"`
	Message string `json:"message"`
}

ConfigValidationError represents a validation error with field path and message.

type ConfigValidationErrors added in v0.60.0

type ConfigValidationErrors struct {
	Errors []ConfigValidationError `json:"errors"`
}

ConfigValidationErrors is a collection of validation errors.

func (*ConfigValidationErrors) Error added in v0.60.0

func (e *ConfigValidationErrors) Error() string

type Experimental added in v0.60.0

type Experimental struct {
	// Threads corresponds to the JSON schema field "threads".
	Threads *ThreadsFeature `json:"threads,omitempty" yaml:"threads,omitempty" mapstructure:"threads,omitempty"`
}

Experimental features that may change or be removed in future versions

type HTTPPermission added in v0.60.0

type HTTPPermission struct {
	// Explanation for why HTTP access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`

	// List of required host patterns for HTTP requests (e.g., 'api.example.com',
	// '*.musicbrainz.org')
	RequiredHosts []string `json:"requiredHosts,omitempty" yaml:"requiredHosts,omitempty" mapstructure:"requiredHosts,omitempty"`
}

HTTP access permissions for a plugin

type KVStorePermission added in v0.60.0

type KVStorePermission struct {
	// Maximum storage size (e.g., '1MB', '500KB'). Default: 1MB
	MaxSize *string `json:"maxSize,omitempty" yaml:"maxSize,omitempty" mapstructure:"maxSize,omitempty"`

	// Explanation for why key-value store access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

Key-value store permissions for persistent plugin storage

type LibraryPermission added in v0.60.0

type LibraryPermission struct {
	// Whether the plugin requires read-only filesystem access to library directories
	Filesystem bool `json:"filesystem,omitempty" yaml:"filesystem,omitempty" mapstructure:"filesystem,omitempty"`

	// Explanation for why library access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

Library service permissions for accessing library metadata and optionally filesystem

func (*LibraryPermission) UnmarshalJSON added in v0.60.0

func (j *LibraryPermission) UnmarshalJSON(value []byte) error

UnmarshalJSON implements json.Unmarshaler.

type LyricsPlugin added in v0.61.0

type LyricsPlugin struct {
	// contains filtered or unexported fields
}

LyricsPlugin adapts a WASM plugin with the Lyrics capability.

func (*LyricsPlugin) GetLyrics added in v0.61.0

func (l *LyricsPlugin) GetLyrics(ctx context.Context, mf *model.MediaFile) (model.LyricList, error)

GetLyrics calls the plugin to fetch lyrics, then parses the raw text responses using model.ToLyrics.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager manages loading and lifecycle of WebAssembly plugins. It implements both agents.PluginLoader and scrobbler.PluginLoader interfaces.

func GetManager

func GetManager(ds model.DataStore, broker events.Broker, m PluginMetricsRecorder) *Manager

GetManager returns a singleton instance of the plugin manager. The manager is not started automatically; call Start() to begin loading plugins.

func (*Manager) DisablePlugin added in v0.60.0

func (m *Manager) DisablePlugin(ctx context.Context, id string) error

DisablePlugin disables a plugin by unloading it and updating the DB. Returns an error if the plugin is not found in DB.

func (*Manager) EnablePlugin added in v0.60.0

func (m *Manager) EnablePlugin(ctx context.Context, id string) error

EnablePlugin enables a plugin by loading it and updating the DB. Returns an error if the plugin is not found in DB or fails to load.

func (*Manager) GetPluginInfo added in v0.60.0

func (m *Manager) GetPluginInfo() map[string]PluginInfo

GetPluginInfo returns information about all loaded plugins.

func (*Manager) LoadLyricsProvider added in v0.61.0

func (m *Manager) LoadLyricsProvider(name string) (lyrics.Lyrics, bool)

LoadLyricsProvider loads and returns a lyrics provider plugin by name.

func (*Manager) LoadMediaAgent

func (m *Manager) LoadMediaAgent(name string) (agents.Interface, bool)

LoadMediaAgent loads and returns a media agent plugin by name. Returns false if the plugin is not found or doesn't have the MetadataAgent capability.

func (*Manager) LoadScrobbler

func (m *Manager) LoadScrobbler(name string) (scrobbler.Scrobbler, bool)

LoadScrobbler loads and returns a scrobbler plugin by name. Returns false if the plugin is not found or doesn't have the Scrobbler capability.

func (*Manager) PluginNames

func (m *Manager) PluginNames(capability string) []string

PluginNames returns the names of all plugins that implement a particular capability. This is used by both agents and scrobbler systems to discover available plugins. Capabilities are auto-detected from the plugin's exported functions.

func (*Manager) RescanPlugins added in v0.60.0

func (m *Manager) RescanPlugins(ctx context.Context) error

RescanPlugins triggers a manual rescan of the plugins folder. This synchronizes the database with the filesystem, discovering new plugins, updating changed ones, and removing deleted ones.

func (*Manager) SetSubsonicRouter

func (m *Manager) SetSubsonicRouter(router SubsonicRouter)

SetSubsonicRouter sets the Subsonic router for SubsonicAPI host functions. This should be called after the subsonic router is created but before plugins that require SubsonicAPI access are loaded.

func (*Manager) Start added in v0.60.0

func (m *Manager) Start(ctx context.Context) error

Start initializes the plugin manager and loads plugins from the configured folder. It should be called once during application startup when plugins are enabled. The startup flow is: 1. Sync plugins folder with DB (discover new, update changed, remove deleted) 2. Load only enabled plugins from DB

func (*Manager) Stop added in v0.60.0

func (m *Manager) Stop() error

Stop shuts down the plugin manager and releases all resources.

func (*Manager) UnloadDisabledPlugins added in v0.60.0

func (m *Manager) UnloadDisabledPlugins(ctx context.Context)

UnloadDisabledPlugins checks for plugins that are disabled in the database but still loaded in memory, and unloads them. This is called after user or library deletion to clean up plugins that were auto-disabled due to permission loss.

func (*Manager) UpdatePluginConfig added in v0.60.0

func (m *Manager) UpdatePluginConfig(ctx context.Context, id, configJSON string) error

UpdatePluginConfig updates the configuration for a plugin. If the plugin is enabled, it will be reloaded with the new config.

func (*Manager) UpdatePluginLibraries added in v0.60.0

func (m *Manager) UpdatePluginLibraries(ctx context.Context, id, librariesJSON string, allLibraries, allowWriteAccess bool) error

UpdatePluginLibraries updates the libraries permission settings for a plugin. If the plugin is enabled, it will be reloaded with the new settings. If the plugin requires library permission and no libraries are configured (and allLibraries is false), the plugin will be automatically disabled.

func (*Manager) UpdatePluginUsers added in v0.60.0

func (m *Manager) UpdatePluginUsers(ctx context.Context, id, usersJSON string, allUsers bool) error

UpdatePluginUsers updates the users permission settings for a plugin. If the plugin is enabled, it will be reloaded with the new settings. If the plugin requires users permission and no users are configured (and allUsers is false), the plugin will be automatically disabled.

func (*Manager) ValidatePluginConfig added in v0.60.0

func (m *Manager) ValidatePluginConfig(ctx context.Context, id, configJSON string) error

ValidatePluginConfig validates a config JSON string against the plugin's config schema. If the plugin has no config schema defined, it returns an error. Returns nil if validation passes, or an error describing the validation failure.

type Manifest added in v0.60.0

type Manifest struct {
	// The author of the plugin
	Author string `json:"author" yaml:"author" mapstructure:"author"`

	// Config corresponds to the JSON schema field "config".
	Config *ConfigDefinition `json:"config,omitempty" yaml:"config,omitempty" mapstructure:"config,omitempty"`

	// A brief description of what the plugin does
	Description *string `json:"description,omitempty" yaml:"description,omitempty" mapstructure:"description,omitempty"`

	// Experimental corresponds to the JSON schema field "experimental".
	Experimental *Experimental `json:"experimental,omitempty" yaml:"experimental,omitempty" mapstructure:"experimental,omitempty"`

	// The display name of the plugin
	Name string `json:"name" yaml:"name" mapstructure:"name"`

	// Permissions corresponds to the JSON schema field "permissions".
	Permissions *Permissions `json:"permissions,omitempty" yaml:"permissions,omitempty" mapstructure:"permissions,omitempty"`

	// The version of the plugin (semver recommended)
	Version string `json:"version" yaml:"version" mapstructure:"version"`

	// URL to the plugin's website or repository
	Website *string `json:"website,omitempty" yaml:"website,omitempty" mapstructure:"website,omitempty"`
}

Plugin manifest for Navidrome plugins

func ParseManifest added in v0.60.0

func ParseManifest(data []byte) (*Manifest, error)

ParseManifest unmarshals manifest JSON and performs cross-field validation. This is the single entry point for manifest parsing after reading from a file.

func (*Manifest) HasConfigSchema added in v0.60.0

func (m *Manifest) HasConfigSchema() bool

HasConfigSchema returns true if the manifest defines a config schema.

func (*Manifest) HasExperimentalThreads added in v0.60.0

func (m *Manifest) HasExperimentalThreads() bool

HasExperimentalThreads returns true if the manifest requests experimental threads support.

func (*Manifest) HasLibraryFilesystemPermission added in v0.61.2

func (m *Manifest) HasLibraryFilesystemPermission() bool

HasLibraryFilesystemPermission checks if the manifest grants filesystem permission for libraries.

func (*Manifest) UnmarshalJSON added in v0.60.0

func (j *Manifest) UnmarshalJSON(value []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (*Manifest) Validate added in v0.60.0

func (m *Manifest) Validate() error

Validate performs cross-field validation that cannot be expressed in JSON Schema. This validates rules like "SubsonicAPI permission requires users permission".

type MetadataAgent added in v0.60.0

type MetadataAgent struct {
	// contains filtered or unexported fields
}

MetadataAgent is an adapter that wraps an Extism plugin and implements the agents interfaces for metadata retrieval.

func (*MetadataAgent) AgentName added in v0.60.0

func (a *MetadataAgent) AgentName() string

AgentName returns the plugin name

func (*MetadataAgent) GetAlbumImages added in v0.60.0

func (a *MetadataAgent) GetAlbumImages(ctx context.Context, name, artist, mbid string) ([]agents.ExternalImage, error)

GetAlbumImages retrieves images for an album

func (*MetadataAgent) GetAlbumInfo added in v0.60.0

func (a *MetadataAgent) GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*agents.AlbumInfo, error)

GetAlbumInfo retrieves album information

func (*MetadataAgent) GetArtistBiography added in v0.60.0

func (a *MetadataAgent) GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error)

GetArtistBiography retrieves the biography for an artist

func (*MetadataAgent) GetArtistImages added in v0.60.0

func (a *MetadataAgent) GetArtistImages(ctx context.Context, id, name, mbid string) ([]agents.ExternalImage, error)

GetArtistImages retrieves images for an artist

func (*MetadataAgent) GetArtistMBID added in v0.60.0

func (a *MetadataAgent) GetArtistMBID(ctx context.Context, id string, name string) (string, error)

GetArtistMBID retrieves the MusicBrainz ID for an artist

func (*MetadataAgent) GetArtistTopSongs added in v0.60.0

func (a *MetadataAgent) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]agents.Song, error)

GetArtistTopSongs retrieves top songs for an artist

func (*MetadataAgent) GetArtistURL added in v0.60.0

func (a *MetadataAgent) GetArtistURL(ctx context.Context, id, name, mbid string) (string, error)

GetArtistURL retrieves the external URL for an artist

func (*MetadataAgent) GetSimilarArtists added in v0.60.0

func (a *MetadataAgent) GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]agents.Artist, error)

GetSimilarArtists retrieves similar artists

func (*MetadataAgent) GetSimilarSongsByAlbum added in v0.60.0

func (a *MetadataAgent) GetSimilarSongsByAlbum(ctx context.Context, id, name, artist, mbid string, count int) ([]agents.Song, error)

GetSimilarSongsByAlbum retrieves songs similar to tracks on an album

func (*MetadataAgent) GetSimilarSongsByArtist added in v0.60.0

func (a *MetadataAgent) GetSimilarSongsByArtist(ctx context.Context, id, name, mbid string, count int) ([]agents.Song, error)

GetSimilarSongsByArtist retrieves songs similar to an artist's catalog

func (*MetadataAgent) GetSimilarSongsByTrack added in v0.60.0

func (a *MetadataAgent) GetSimilarSongsByTrack(ctx context.Context, id, name, artist, mbid string, count int) ([]agents.Song, error)

GetSimilarSongsByTrack retrieves songs similar to a specific track

type Permissions added in v0.60.0

type Permissions struct {
	// Artwork corresponds to the JSON schema field "artwork".
	Artwork *ArtworkPermission `json:"artwork,omitempty" yaml:"artwork,omitempty" mapstructure:"artwork,omitempty"`

	// Cache corresponds to the JSON schema field "cache".
	Cache *CachePermission `json:"cache,omitempty" yaml:"cache,omitempty" mapstructure:"cache,omitempty"`

	// Http corresponds to the JSON schema field "http".
	Http *HTTPPermission `json:"http,omitempty" yaml:"http,omitempty" mapstructure:"http,omitempty"`

	// Kvstore corresponds to the JSON schema field "kvstore".
	Kvstore *KVStorePermission `json:"kvstore,omitempty" yaml:"kvstore,omitempty" mapstructure:"kvstore,omitempty"`

	// Library corresponds to the JSON schema field "library".
	Library *LibraryPermission `json:"library,omitempty" yaml:"library,omitempty" mapstructure:"library,omitempty"`

	// Scheduler corresponds to the JSON schema field "scheduler".
	Scheduler *SchedulerPermission `json:"scheduler,omitempty" yaml:"scheduler,omitempty" mapstructure:"scheduler,omitempty"`

	// Subsonicapi corresponds to the JSON schema field "subsonicapi".
	Subsonicapi *SubsonicAPIPermission `json:"subsonicapi,omitempty" yaml:"subsonicapi,omitempty" mapstructure:"subsonicapi,omitempty"`

	// Taskqueue corresponds to the JSON schema field "taskqueue".
	Taskqueue *TaskQueuePermission `json:"taskqueue,omitempty" yaml:"taskqueue,omitempty" mapstructure:"taskqueue,omitempty"`

	// Users corresponds to the JSON schema field "users".
	Users *UsersPermission `json:"users,omitempty" yaml:"users,omitempty" mapstructure:"users,omitempty"`

	// Websocket corresponds to the JSON schema field "websocket".
	Websocket *WebSocketPermission `json:"websocket,omitempty" yaml:"websocket,omitempty" mapstructure:"websocket,omitempty"`
}

Permissions required by the plugin

type PluginInfo added in v0.60.0

type PluginInfo struct {
	Name    string
	Version string
}

PluginInfo contains basic information about a plugin for metrics/insights.

type PluginMetadata added in v0.60.0

type PluginMetadata struct {
	Manifest *Manifest
	SHA256   string
}

PluginMetadata holds the extracted information from a plugin file without fully initializing the plugin.

type PluginMetricsRecorder added in v0.60.0

type PluginMetricsRecorder interface {
	RecordPluginRequest(ctx context.Context, plugin, method string, ok bool, elapsed int64)
}

PluginMetricsRecorder is an interface for recording plugin metrics. This is satisfied by core/metrics.Metrics but defined here to avoid import cycles.

type SchedulerPermission added in v0.60.0

type SchedulerPermission struct {
	// Explanation for why scheduler access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

Scheduler service permissions for scheduling tasks

type ScrobblerPlugin added in v0.60.0

type ScrobblerPlugin struct {
	// contains filtered or unexported fields
}

ScrobblerPlugin is an adapter that wraps an Extism plugin and implements the scrobbler.Scrobbler interface for scrobbling to external services.

func (*ScrobblerPlugin) IsAuthorized added in v0.60.0

func (s *ScrobblerPlugin) IsAuthorized(ctx context.Context, userId string) bool

IsAuthorized checks if the user is authorized with this scrobbler. First checks if the user is allowed to use this plugin (server-side), then delegates to the plugin for service-specific authorization.

func (*ScrobblerPlugin) NowPlaying added in v0.60.0

func (s *ScrobblerPlugin) NowPlaying(ctx context.Context, userId string, track *model.MediaFile, position int) error

NowPlaying sends a now playing notification to the scrobbler

func (*ScrobblerPlugin) Scrobble added in v0.60.0

func (s *ScrobblerPlugin) Scrobble(ctx context.Context, userId string, sc scrobbler.Scrobble) error

Scrobble submits a scrobble to the scrobbler

type SubsonicAPIPermission added in v0.60.0

type SubsonicAPIPermission struct {
	// Explanation for why SubsonicAPI access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

SubsonicAPI service permissions. Requires 'users' permission to be declared.

type SubsonicRouter

type SubsonicRouter = http.Handler

SubsonicRouter is an http.Handler that serves Subsonic API requests.

type TaskQueuePermission added in v0.61.0

type TaskQueuePermission struct {
	// Maximum total concurrent workers across all queues. Default: 1
	MaxConcurrency int `json:"maxConcurrency,omitempty" yaml:"maxConcurrency,omitempty" mapstructure:"maxConcurrency,omitempty"`

	// Explanation for why task queue access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

Task queue permissions for background task processing

func (*TaskQueuePermission) UnmarshalJSON added in v0.61.0

func (j *TaskQueuePermission) UnmarshalJSON(value []byte) error

UnmarshalJSON implements json.Unmarshaler.

type ThreadsFeature added in v0.60.0

type ThreadsFeature struct {
	// Explanation for why threads support is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

Enable experimental WebAssembly threads support

type UsersPermission added in v0.60.0

type UsersPermission struct {
	// Explanation for why users access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`
}

Users service permissions for accessing user information

type WebSocketPermission added in v0.60.0

type WebSocketPermission struct {
	// Explanation for why WebSocket access is needed
	Reason *string `json:"reason,omitempty" yaml:"reason,omitempty" mapstructure:"reason,omitempty"`

	// List of required host patterns for WebSocket connections (e.g.,
	// 'api.example.com', '*.musicbrainz.org')
	RequiredHosts []string `json:"requiredHosts,omitempty" yaml:"requiredHosts,omitempty" mapstructure:"requiredHosts,omitempty"`
}

WebSocket service permissions for establishing WebSocket connections

Directories

Path Synopsis
Package capabilities defines Go interfaces for Navidrome plugin capabilities.
Package capabilities defines Go interfaces for Navidrome plugin capabilities.
Package host provides host services that can be called by plugins via Extism host functions.
Package host provides host services that can be called by plugins via Extism host functions.
pdk
go module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL