Public Go SDK for building Silo plugins. Not a runtime plugin — this is a library that plugin authors depend on via go.mod.
silo-plugin-sdk is the source of truth for the plugin authoring contract. First-party consumers (Silo host, silo-plugin-tmdb, silo-plugin-metadb, every other plugin in this repo) pin tagged semver releases. Local multi-repo workspaces may use go.work or a temporary replace, but CI and release builds resolve the SDK from a published module tag.
github.com/Silo-Server/silo-plugin-sdk/pkg/pluginproto/silo/plugin/v1— generated protobuf code.github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/capability— stable capability type constants for manifests and peer discovery.github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/config— config-schema helpers.github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/convert— type conversions.github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/manifest— manifest loading/rendering.github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/runtime—manifestsubcommand +Runtimeserver scaffolding.github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/runtimedefault— defaultRuntimeimplementation withBindHostBrokeralready wired; embed it to skip boilerplate.github.com/Silo-Server/silo-plugin-sdk/pkg/pluginsdk/runtimehost— typed client for the host'sRuntimeHostservice, including event publishing, host info, catalog browsing, installed-plugin discovery, scoped streams, plugin-to-plugin HTTP calls, and plugin-owned config writes.
The SDK ships protobuf contracts for every capability the host understands:
metadata_provider.v1marker_provider.v1media_analyzer.v1scheduled_task.v1event_consumer.v1auth_provider.v1http_routes.v1request_router.v1scan_source.v1audiobook_backend.v1ebook_backend.v1
Plugins implement one or more, advertise them in manifest.json, and serve them over gRPC.
A typical plugin:
- Defines a
manifest.jsonusing the protobuf-derived schema. - Exposes a
RuntimegRPC server plus one or more capability servers. - Supports the
manifestsubcommand viapkg/pluginsdk/runtimeso the host can introspect manifests without launching the plugin. - Is installed either from a catalog or by uploading a trusted binary to a Silo server.
For a minimal self-describing plugin, see examples/hello-scheduled-task. For a plugin that calls back into the host via RuntimeHost (publishing events, listing libraries), see examples/hello-runtime-host.
PluginManifest.presentation gives the Silo admin UI typed, plugin-level copy
and canonical links. It is optional for backward compatibility, but cataloged
plugins should provide a complete block:
{
"presentation": {
"display_name": "Example Plugin",
"summary": "A one-sentence explanation for a homelab administrator.",
"description_markdown": "A longer description of what the plugin does and when to use it.",
"setup_markdown": "1. Install the plugin.\n2. Add the required connection.\n3. Enable it for the relevant library.",
"homepage_url": "https://example.com/plugin",
"source_url": "https://github.com/Silo-Server/example-plugin",
"support_url": "https://github.com/Silo-Server/example-plugin/issues",
"changelog_url": "https://github.com/Silo-Server/example-plugin/releases",
"publisher_name": "Silo",
"publisher_url": "https://github.com/Silo-Server",
"license_spdx": "AGPL-3.0-or-later"
}
}display_nameis limited to 120 characters andsummaryto 240 characters; both are concise card copy without leading or trailing whitespace.description_markdownandsetup_markdownuse CommonMark-style Markdown; raw HTML is not part of the contract, and each field is limited to 32 KiB.- All URLs must be absolute
httporhttpslinks and must not contain embedded credentials; each URL is limited to 2048 bytes. - Publisher and source fields are self-declared identity information. Catalog
provenance and approval are assigned by the host/catalog, never by this block.
publisher_nameis limited to 120 characters. - Use an SPDX license expression of at most 120 characters. Use
NOASSERTIONwhen the repository has not declared a license rather than guessing one.
Curated catalog tooling should call
manifest.ValidateCatalogPresentation(manifest, canonicalRepositoryURL) to
require the complete block and prevent a published source_url from drifting
away from the repository that produced the release.
Plugins talk to the host through the RuntimeHost service, accessed via pkg/pluginsdk/runtimehost.Client. The host invokes Runtime.BindHostBroker on startup so plugins can dial back over the shared broker; runtimedefault handles that step for you. Available RPCs:
PublishEvent/PublishEventTo/PublishEventToInstallation— fire events into the host's bus, broadcast, addressed to a stableplugin_id, or addressed to one specific installation.GetHostInfo— read host URL metadata for callback URLs and external-facing plugin links.ListLibraries— enumerate libraries the operator has configured.CheckMediaPresence— ask whether a given external id is already in the catalog.ListInstalledPlugins— discover sibling plugins (e.g. routers a request plugin can target).ListLibraryMedia/GetCatalogStats— read public-safe catalog rows and aggregate counts.ResolveCatalogImageURLs— resolve stored poster/backdrop image paths into host-generated browser URLs.MintScopedStream— create short-lived, narrowly scoped stream grants for guest/public workflows.CallPluginHTTP— invoke another installed plugin'shttp_routes.v1handler through the host control plane.SetGlobalConfigEntry— persist plugin-owned config that admins didn't set via the manifest form.
For plugin-to-plugin JSON calls, prefer the helper layer:
plugins, err := host.ListInstalledPluginsByCapability(ctx, capability.RequestRouter)
if err != nil || len(plugins) == 0 {
return err
}
var out struct {
Accepted bool `json:"accepted"`
}
err = host.CallPluginJSON(ctx, runtimehost.CallPluginJSONRequest{
InstallationID: int(plugins[0].GetInstallationId()),
Path: "/api/request",
Request: map[string]any{"title": "The Matrix"},
Response: &out,
})The auth_provider.v1 capability also exposes OAuth-flow RPCs (InitAuthorize, ExchangeCode, RefreshSession) for plugins that wrap external identity providers.
The scan_source.v1 capability is for Autoscan providers. The host owns the
poll timer, marker persistence, path rewrites, validation, dedupe, and scan
enqueueing. The plugin only polls its upstream provider and returns changed
absolute paths in that provider's source namespace. The host applies autoscan
source rewrite rules before enqueueing scans.
The host resolves the configured upstream connection and passes it to
PollChanges for each poll. Plugins should treat request values such as API
keys as transient secrets and avoid logging them without redaction.
Direct binary upload works best when the plugin embeds a manifest template and computes its own executable checksum at runtime before returning Runtime.GetManifest. That keeps the plugin installable without requiring a checked-out Silo repository or a sibling manifest.json file at upload time. The example plugin shows the pattern.
Compatibility and versioning expectations are documented in docs/compatibility.md.
SDK releases are cut from semver tags such as v0.1.0 and published through GitHub Actions.
- Additive public API changes belong in a new minor release.
- Compatible fixes and documentation updates belong in a patch release.
- Breaking public API, protobuf, or manifest contract changes require a new major version.
Before downstream repos stop using local workspace overrides, the required SDK commit must be pushed and tagged here first.
make proto # regenerate protobuf code (uses locally vendored buf under ./bin/)
go test ./...Apache-2.0. See LICENSE.