Skip to content

Packaging and Distribution

RadiatorTwo edited this page Jun 28, 2026 · 6 revisions

Packaging & Distribution

A LoupixDeck plugin is a regular .NET class library shipped as a folder containing the plugin DLL, a plugin.json manifest, and any runtime dependencies. The host reads plugin.json before loading the assembly — it determines the plugin's identity, target OS, SDK compatibility and which DLL to load. PluginMetadata (returned by your LoupixPlugin subclass) is then read at runtime and must agree with the manifest.

plugin.json

One plugin.json per plugin folder, sitting next to the entry assembly:

{
  "id": "myplugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "sdkVersion": "1.12",
  "entryAssembly": "MyPlugin.dll",
  "platform": "All"
}
Field Notes
id Stable, filesystem-safe identifier. Must match the folder name under the plugin root and PluginMetadata.Id. Required.
name Display name shown in the host UI.
version Plugin version (SemVer). Keep in sync with PluginMetadata.Version.
sdkVersion SDK contract version you compiled against (SemVer). The host enforces a major-version match — a host on SDK 2.x refuses a plugin built against 1.x.
entryAssembly File name of the DLL containing your LoupixPlugin subclass. Required.
platform One of All, Windows, Linux. Plugins targeting a platform the current host is not running on are skipped silently. Defaults to All if omitted.

The host fails the plugin if plugin.json is missing, malformed, or lacks id / entryAssembly.

Make sure the manifest reaches the build output by adding to your .csproj:

<ItemGroup>
  <None Update="plugin.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Versioning rules

Two versions matter, and they are different things:

Field What it is Bump when
PluginMetadata.Version (and plugin.json version) Your plugin's own version You ship new behavior or bug fixes.
PluginMetadata.SdkVersion (and plugin.json sdkVersion) The SDK contract version you compiled against Always SdkInfo.Version — the SDK package handles it for you.

The host enforces a major-version match on SdkVersion. Building against SDK 1.x runs on any host shipped with SDK 1.x — across the entire 1.x line the contracts are source- and binary-compatible. A host on SDK 2.x will refuse to load a plugin built against 1.x (and vice versa).

SdkVersion = SdkInfo.Version,   // always this — never hard-code

The SDK's AssemblyVersion is intentionally pinned at 1.0.0.0 across the entire 1.x package range so the plugin load context resolves one shared SDK assembly regardless of which 1.x package the plugin was built against.

Build output

dotnet build -c Release

Ship the contents of bin/Release/net9.0/ minus LoupixDeck.PluginSdk.dll:

  • MyPlugin.dll — your plugin
  • plugin.json — the manifest the host reads before loading the assembly
  • Any third-party runtime dependencies (HttpClient libs, JSON serializers your plugin uses beyond the BCL, etc.)
  • Optional MyPlugin.pdb if you want symbols available for end-user crash reports (the SDK ships embedded PDBs already)

Do not redistribute LoupixDeck.PluginSdk.dll. The host provides it. Bundling it causes assembly-load conflicts. The NuGet package reference already does the right thing — don't manually set <Private>true</Private> or copy the file by hand.

Folder layout for distribution

One folder per plugin, named after Metadata.Id, placed inside the host's plugin root:

<plugin root>/
└── myplugin/                      ← matches PluginMetadata.Id and plugin.json "id"
    ├── plugin.json
    ├── MyPlugin.dll
    ├── ThirdParty.Dep.dll
    └── (your plugin's other runtime deps)

Per-plugin data the host creates at runtime sits next to your DLLs:

└── myplugin/
    └── settings.json              ← managed by the host (IPluginSettings)

See Debugging for the exact plugin-root path on each OS.

Plugin icon

PluginMetadata.Icon is optional raw bytes (PNG recommended, SVG accepted). Read the bytes from an embedded resource so you don't depend on the install layout:

public override PluginMetadata Metadata { get; } = new()
{
    Id = "myplugin", Name = "My Plugin",
    Version = new Version(1, 0, 0), SdkVersion = SdkInfo.Version,
    Icon = LoadEmbeddedIcon("MyPlugin.icon.png")
};

private static byte[] LoadEmbeddedIcon(string name)
{
    using var s = typeof(MyPlugin).Assembly.GetManifestResourceStream(name)
                  ?? throw new InvalidOperationException($"Missing resource: {name}");
    using var ms = new MemoryStream();
    s.CopyTo(ms);
    return ms.ToArray();
}

In the .csproj:

<ItemGroup>
  <EmbeddedResource Include="icon.png" LogicalName="MyPlugin.icon.png" />
</ItemGroup>

Releasing

There is no central plugin registry yet. Typical release flow:

  1. Bump PluginMetadata.Version and the <Version> in the .csproj.
  2. dotnet build -c Release.
  3. Zip the bin/Release/net9.0/ folder (without the SDK DLL).
  4. Publish the zip as a GitHub Release on the plugin's own repo and tell users to extract it into <plugin root>/<id>/.

Clone this wiki locally