CAXperts.Toolkit.Plugins.Abstractions
2026.4.3
Prefix Reserved
dotnet add package CAXperts.Toolkit.Plugins.Abstractions --version 2026.4.3
NuGet\Install-Package CAXperts.Toolkit.Plugins.Abstractions -Version 2026.4.3
<PackageReference Include="CAXperts.Toolkit.Plugins.Abstractions" Version="2026.4.3" />
<PackageVersion Include="CAXperts.Toolkit.Plugins.Abstractions" Version="2026.4.3" />
<PackageReference Include="CAXperts.Toolkit.Plugins.Abstractions" />
paket add CAXperts.Toolkit.Plugins.Abstractions --version 2026.4.3
#r "nuget: CAXperts.Toolkit.Plugins.Abstractions, 2026.4.3"
#:package CAXperts.Toolkit.Plugins.Abstractions@2026.4.3
#addin nuget:?package=CAXperts.Toolkit.Plugins.Abstractions&version=2026.4.3
#tool nuget:?package=CAXperts.Toolkit.Plugins.Abstractions&version=2026.4.3
CAXperts.Toolkit.Plugins.Abstractions
The backend half of the Toolkit plugin SDK. This is the one NuGet package a plugin's server/
project references: it ships the plugin contract (IToolkitPlugin, IPluginContext,
IPluginMigrations, IPluginStartup, the plugin.json manifest model) together with the host's
ToolkitDbContext and entities, so a plugin compiles against this package alone.
The frontend half is the npm package
@caxperts/toolkit-plugin-sdk. A plugin
ships as one folder with a frontend half, a server/ .NET half, or both.
Full contract, trust model and packaging rules: Toolkit Plugins on help.caxperts.com.
Install
Released versions are on nuget.org, versioned like the host release they belong to. The version must match the npm SDK and the host you build against.
dotnet add package CAXperts.Toolkit.Plugins.Abstractions --version 2026.4.3
A plugin scaffolded with npx toolkit-new-plugin already references the package: the version is
PluginSdkPackageVersion in its Directory.Build.props, and its nuget.config points at nuget.org.
Steps to create a backend plugin
The frontend steps (scaffold, build, stage, pack) are in the
@caxperts/toolkit-plugin-sdk README. The
backend-specific steps:
1. Scaffold the plugin
toolkit-new-plugin writes a full-stack plugin: a frontend page plus a server/ .NET project with its
own plg_ schema, migration and controllers, with the id already correct everywhere the host
cross-checks it.
npx toolkit-new-plugin --id acme.inspections --name "ACME Inspections" --dir ./acme.inspections
2. Implement the entry point
Exactly one public, non-abstract IToolkitPlugin per assembly, with a parameterless constructor. Its
Id must equal plugin.json's id. ConfigureServices runs once at startup, before the host
container is built; ToolkitDbContext, ICurrentUser, IBlobStorageService, IEmailService,
IConfiguration, ILogger<T> and IHttpClientFactory are available without any registration of
your own. Throwing marks the plugin failed and leaves the host running.
Optional interfaces on the same class:
IPluginMigrations— theDbContexttypes the host migrates at startup.IPluginStartup— one-off initialisation after the container is built, bounded byPlugins:StartupTimeoutSeconds(default 60).
3. Own your data
Give your DbContext its own plg_<id> schema (from IPluginContext.DatabaseSchema) with the
migrations history table inside that schema, and a hand-written migration. Resolve migrations through
the MigrationsAssembly(Assembly) overload — the string overload cannot see an assembly the host
loaded from a stream.
Key every row on the OIDC subject (PluginControllerBase.TryGetSubject or
PluginPrincipal.TryGetSubject) — never ICurrentUser.Id, which is 0 for every real user. Put the
owner in the WHERE clause of every read and delete so two users sharing a table cannot see each
other's rows.
4. Write controllers
Controllers are served under /api/plugins/<id>/. Derive from PluginControllerBase for the calling
user's identity, the standard 401 body and ServerSentEvents(...). The host injects [Authorize]
onto every plugin controller; an explicit [AllowAnonymous] still wins. Server-Sent Events are the
only way to push to the browser; use PluginSse rather than writing to Response.Body.
5. Build, test locally, package
npm run build:all runs dotnet publish into backend/. npm run stage expands the plugin into
.plugins-dev/<id>/, where the running host picks it up; a backend change needs an API restart,
because the assembly is never unloaded once loaded. npm run pack produces <id>-<version>.zip, which
an Admin installs through Settings → Plugins.
Rules the host enforces
plugin.json'sapiVersionmust equalPluginApiVersion.Current; a mismatch reports the plugin as incompatible instead of loading it.- The plugin id must match
plugin.json, theIToolkitPlugin.Idand the folder name on the plugins volume. - Never reference
Toolkit.Infrastructureor any other Toolkit project; this package is the only one a plugin needs. - The install directory is read-only and replaced on update. Anything that must survive an update goes
into
IPluginContext.DataDirectory.
Reference
- Contract, security/stability model, database and packaging rules, and a worked full-stack example: Toolkit Plugins on help.caxperts.com.
- Example plugins showing the SDK in use: caxperts/Toolkit.Plugins.Examples on GitHub.
- Frontend SDK:
@caxperts/toolkit-plugin-sdk.
License
The plugin SDK — this NuGet package and the @caxperts/toolkit-plugin-sdk npm package — is released
under the MIT License. The license covers the SDK only, not the Toolkit host.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.EntityFrameworkCore.SqlServer (>= 10.0.14)
- System.Security.Cryptography.Pkcs (>= 10.0.14)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.