hostgen
A code generator for Navidrome's plugin host functions. It reads Go interface definitions with special annotations and generates Extism host function wrappers.
Usage
hostgen -input <dir> -output <dir> -package <name> [-v] [-dry-run] [-host-only] [-plugin-only]
Flags
| Flag | Description | Default |
|---|---|---|
-input |
Directory containing Go source files with annotated interfaces | Required |
-output |
Directory where generated files will be written | Required |
-package |
Package name for generated files | Required |
-v |
Verbose output | false |
-dry-run |
Parse and validate without writing files | false |
-host-only |
Generate only host-side wrapper code | false |
-plugin-only |
Generate only plugin/client-side wrapper code | false |
By default, both host and plugin code are generated. Use -host-only or -plugin-only to generate only one type.
Example
go run ./plugins/cmd/hostgen \
-input ./plugins/host \
-output ./plugins/host \
-package host
Or via go generate (recommended):
//go:generate go run ../cmd/hostgen -input . -output . -package host
package host
Annotations
//nd:hostservice
Marks an interface as a host service that will have wrappers generated.
//nd:hostservice name=<ServiceName> permission=<permission>
type MyService interface { ... }
| Parameter | Description | Required |
|---|---|---|
name |
Service name used in generated type names and function prefixes | Yes |
permission |
Permission required by plugins to use this service | Yes |
//nd:hostfunc
Marks a method within a host service interface for export to plugins.
//nd:hostfunc [name=<export_name>]
MethodName(ctx context.Context, ...) (result Type, err error)
| Parameter | Description | Required |
|---|---|---|
name |
Custom export name (default: <servicename>_<methodname> in lowercase) |
No |
Input Format
Host service interfaces must follow these conventions:
- First parameter must be
context.Context- Required for all methods - Last return value should be
error- For proper error handling - Annotations must be on consecutive lines - No blank comment lines between doc and annotation
Example Interface
package host
import "context"
// SubsonicAPIService provides access to Navidrome's Subsonic API.
// This documentation becomes part of the generated code.
//nd:hostservice name=SubsonicAPI permission=subsonicapi
type SubsonicAPIService interface {
// Call executes a Subsonic API request and returns the response.
//nd:hostfunc
Call(ctx context.Context, uri string) (response string, err error)
}
Generated Output
For each annotated interface, hostgen generates:
Request/Response Types
// SubsonicAPICallRequest is the request type for SubsonicAPI.Call.
type SubsonicAPICallRequest struct {
Uri string `json:"uri"`
}
// SubsonicAPICallResponse is the response type for SubsonicAPI.Call.
type SubsonicAPICallResponse struct {
Response string `json:"response,omitempty"`
Error string `json:"error,omitempty"`
}
Registration Function
// RegisterSubsonicAPIHostFunctions registers SubsonicAPI service host functions.
func RegisterSubsonicAPIHostFunctions(service SubsonicAPIService) []extism.HostFunction {
return []extism.HostFunction{
newSubsonicAPICallHostFunction(service),
}
}
Host Function Wrappers
Each method gets a wrapper that:
- Reads JSON request from plugin memory
- Unmarshals to the request type
- Calls the service method
- Marshals the response
- Writes JSON response to plugin memory
Supported Types
hostgen supports these Go types in method signatures:
| Type | JSON Representation |
|---|---|
string, int, bool, etc. |
Native JSON types |
[]T (slices) |
JSON arrays |
map[K]V (maps) |
JSON objects |
*T (pointers) |
Nullable fields |
interface{} / any |
Converts to any |
| Custom structs | JSON objects (must be JSON-serializable) |
Multiple Return Values
Methods can return multiple values (plus error):
//nd:hostfunc
Search(ctx context.Context, query string) (results []string, total int, hasMore bool, err error)
Generates:
type ServiceSearchResponse struct {
Results []string `json:"results,omitempty"`
Total int `json:"total,omitempty"`
HasMore bool `json:"hasMore,omitempty"`
Error string `json:"error,omitempty"`
}
Output Files
Host Code (Navidrome-side)
Generated files are named <servicename>_gen.go (lowercase) and placed in the output directory. Each file includes:
// Code generated by hostgen. DO NOT EDIT.header- Required imports (
context,encoding/json,extism) - Request/response struct types
- Registration function
- Host function wrappers
- Helper functions (
writeResponse,writeErrorResponse)
Plugin/Client Code (TinyGo WASM)
Generated files are named nd_host_<servicename>.go (lowercase) and placed in the go/ subdirectory of the output directory. These files are intended for use in Navidrome plugins built with TinyGo. Each file includes:
// Code generated by hostgen. DO NOT EDIT.header- Required imports (
encoding/json,errors,github.com/extism/go-pdk) //go:wasmimportdeclarations for each host function- Response struct types
- Wrapper functions that handle memory allocation and JSON parsing
Example Output Structure
output/
├── subsonicapi_gen.go # Host-side code (for Navidrome)
└── go/
└── nd_host_subsonicapi.go # Plugin-side code (for TinyGo plugins)
Troubleshooting
Annotations Not Detected
Ensure annotations are on consecutive lines with no blank // lines:
// ✅ Correct
// Documentation for the service.
//nd:hostservice name=Test permission=test
// ❌ Wrong - blank comment line breaks detection
// Documentation for the service.
//
//nd:hostservice name=Test permission=test
Methods Not Exported
Methods without //nd:hostfunc annotation are skipped. Ensure the annotation is directly above the method:
// ✅ Correct
// Method documentation.
//nd:hostfunc
MyMethod(ctx context.Context) error
// ❌ Wrong - annotation not directly above method
//nd:hostfunc
MyMethod(ctx context.Context) error
Generated Files Skipped
Files ending in _gen.go are automatically skipped during parsing to avoid processing previously generated code.
Development
Run tests:
go test -v ./plugins/cmd/hostgen/...
The test suite includes:
- CLI integration tests
- Complex type handling (structs, slices, maps, pointers)
- Multiple return value scenarios
- Error cases and edge conditions