KnOwl.ControlPlane.Bootstrap
2.0.0
dotnet add package KnOwl.ControlPlane.Bootstrap --version 2.0.0
NuGet\Install-Package KnOwl.ControlPlane.Bootstrap -Version 2.0.0
<PackageReference Include="KnOwl.ControlPlane.Bootstrap" Version="2.0.0" />
<PackageVersion Include="KnOwl.ControlPlane.Bootstrap" Version="2.0.0" />
<PackageReference Include="KnOwl.ControlPlane.Bootstrap" />
paket add KnOwl.ControlPlane.Bootstrap --version 2.0.0
#r "nuget: KnOwl.ControlPlane.Bootstrap, 2.0.0"
#:package KnOwl.ControlPlane.Bootstrap@2.0.0
#addin nuget:?package=KnOwl.ControlPlane.Bootstrap&version=2.0.0
#tool nuget:?package=KnOwl.ControlPlane.Bootstrap&version=2.0.0
KnOwl
KnOwl is a set of reusable .NET libraries for designing, versioning, promoting, distributing, and consuming async contract metadata. It gives teams a Control Plane where contracts are authored and released, plus a Runtime surface where deployed contracts can be consumed by running services.
The goal is to keep product hosts thin. Your app owns the executable project, configuration, and EF migrations; KnOwl packages provide the domain model, application services, storage adapters, Razor UI, catalog endpoints, distribution endpoints, and runtime synchronization behavior.
What KnOwl Solves
- Design event and command contracts with versioned payload schemas.
- Model commands with a required request schema and optional reply schema.
- Promote versions through lifecycle states and generate immutable artifacts.
- Distribute deployed artifacts from a Control Plane to one or more Runtime hosts.
- Expose deployed schemas through split event and command catalog endpoints.
- Keep host-specific database migrations outside the reusable NuGet libraries.
Packages
Install only the layer your host needs:
| Package | Purpose |
|---|---|
KnOwl.Contracts |
Shared DTOs for artifacts, delivery, catalog responses, and security. |
KnOwl.ControlPlane |
Control Plane domain model and repository contracts. |
KnOwl.ControlPlane.Application |
Control Plane services for design, lifecycle, artifacts, releases, and delivery. |
KnOwl.ControlPlane.Api |
Minimal API endpoints for Control Plane automation and external integrations. |
KnOwl.ControlPlane.Storage.EntityFramework |
Provider-agnostic EF Core storage for Control Plane state. |
KnOwl.ControlPlane.WebUI |
Reusable Razor UI for Control Plane hosts. |
KnOwl.ControlPlane.Bootstrap |
ASP.NET Core composition for Control Plane hosts. |
KnOwl.Runtime |
Runtime domain model and repository contracts. |
KnOwl.Runtime.Application |
Runtime catalog, deployment, pull, and security services. |
KnOwl.Runtime.Api |
Minimal API endpoints for Runtime administration and artifact consumption. |
KnOwl.Runtime.Storage.EntityFramework |
Provider-agnostic EF Core storage for Runtime state. |
KnOwl.Runtime.WebUI |
Reusable Razor UI for Runtime hosts. |
KnOwl.Runtime.Bootstrap |
ASP.NET Core composition for Runtime hosts. |
KnOwl.Security |
Provider-agnostic subject resolution, roles, permissions, and ASP.NET Core authorization policies. |
KnOwl.Security.Storage.EntityFramework |
Provider-agnostic EF Core storage for KnOwl subjects, role assignments, permission assignments, and external group mappings. |
All packages target net9.0 and net10.0.
The *.Storage.EntityFramework and bootstrap packages depend on EF Core relational APIs, not on a concrete database provider. Hosts choose the provider by configuring the DbContext options, the same way they would configure EF Core directly.
Getting Started
1. Create a Control Plane host
dotnet new web -n MyCompany.Contracts.ControlPlane
cd MyCompany.Contracts.ControlPlane
dotnet add package KnOwl.ControlPlane.Bootstrap
dotnet add package KnOwl.ControlPlane.Storage.EntityFramework
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
Use the bootstrap package in Program.cs:
using KnOwl.ControlPlane.Bootstrap;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
var migrationsAssembly = typeof(Program).Assembly.GetName().Name!;
var connectionString = builder.Configuration.GetConnectionString("KnOwlDb");
builder.Services.AddKnOwlControlPlane(builder.Configuration, options =>
{
options.MigrationsAssembly = migrationsAssembly;
options.ConfigureStorage = db => db.UseSqlServer(
connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
options.Theme.Title = "My Contracts";
options.Theme.IconImageUrl = "/img/company-icon.png";
options.Theme.PrimaryColor = "#2563eb";
options.Theme.PrimaryHoverColor = "#1d4ed8";
options.Theme.SidebarBackgroundColor = "#0f2a44";
options.Theme.SidebarBrandBackgroundColor = "#0b1f33";
});
var app = builder.Build();
app.MapKnOwlControlPlane();
app.Run();
Add configuration:
{
"ConnectionStrings": {
"KnOwlDb": "Server=localhost;Database=KnOwlControlPlane;Trusted_Connection=True;TrustServerCertificate=True"
}
}
Create migrations in the host project:
dotnet ef migrations add InitialKnOwlControlPlane `
--context KnOwlDbContext `
--output-dir Migrations
dotnet ef migrations add InitialKnOwlSecurity `
--context KnOwlSecurityDbContext `
--output-dir Migrations/Security
dotnet ef database update --context KnOwlDbContext
dotnet ef database update --context KnOwlSecurityDbContext
Run the host and open the Control Plane UI. From there you can create data types, custom metadata fields, events, commands, versions, artifacts, runtime environments, runtime nodes, and releases.
The bootstrap package also maps the Control Plane REST API at /api/v1/control-plane.
Theme configuration is optional. When omitted, the reusable Web UI uses KnOwl's default purple and white theme. Hosts can override the title, icon, and colors from AddKnOwlControlPlane without changing package assets.
To use a different EF Core provider, install that provider in the host and configure storage with that provider:
using KnOwl.ControlPlane.Bootstrap;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddKnOwlControlPlane(builder.Configuration, options =>
{
options.ConfigureStorage = db => db.UseNpgsql(
builder.Configuration.GetConnectionString("KnOwlDb"),
provider => provider.MigrationsAssembly(typeof(Program).Assembly.GetName().Name));
options.ConfigureSecurityStorage = db => db.UseNpgsql(
builder.Configuration.GetConnectionString("KnOwlSecurityDb"),
provider => provider.MigrationsAssembly(typeof(Program).Assembly.GetName().Name));
});
2. Create a Runtime host
dotnet new web -n MyCompany.Contracts.Runtime
cd MyCompany.Contracts.Runtime
dotnet add package KnOwl.Runtime.Bootstrap
dotnet add package KnOwl.Runtime.Storage.EntityFramework
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
Use the runtime bootstrap in Program.cs:
using KnOwl.Runtime.Bootstrap;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
var migrationsAssembly = typeof(Program).Assembly.GetName().Name!;
var connectionString = builder.Configuration.GetConnectionString("KnOwlRuntimeDb")
?? builder.Configuration.GetConnectionString("KnOwlDb");
builder.Services.AddKnOwlRuntime(builder.Configuration, options =>
{
options.MigrationsAssembly = migrationsAssembly;
options.ConfigureStorage = db => db.UseSqlServer(
connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
options.Theme.Title = "My Runtime";
options.Theme.Subtitle = "Contract cache";
options.Theme.IconImageUrl = "/img/company-icon.png";
options.Theme.PrimaryColor = "#2563eb";
options.Theme.PrimaryHoverColor = "#1d4ed8";
options.Theme.SidebarBackgroundColor = "#0f2a44";
});
var app = builder.Build();
app.MapKnOwlRuntime();
app.Run();
Add configuration:
{
"ConnectionStrings": {
"KnOwlRuntimeDb": "Server=localhost;Database=KnOwlRuntime;Trusted_Connection=True;TrustServerCertificate=True"
},
"Runtime": {
"ArtifactPull": {
"Enabled": true,
"InitialDelaySeconds": 5,
"IntervalSeconds": 30
}
}
}
Create runtime migrations in the host project:
dotnet ef migrations add InitialKnOwlRuntime `
--context KnOwlRuntimeDbContext `
--output-dir Migrations/RuntimeStorage
dotnet ef migrations add InitialKnOwlSecurity `
--context KnOwlSecurityDbContext `
--output-dir Migrations/Security
dotnet ef database update --context KnOwlRuntimeDbContext
dotnet ef database update --context KnOwlSecurityDbContext
The bootstrap package also maps the Runtime REST API at /api/v1/runtime.
Runtime theme configuration is optional and follows the same host-owned pattern as the Control Plane. Hosts can set the sidebar title, subtitle, icon, and colors directly on options.Theme.
3. Connect Runtime to Control Plane
- In the Runtime UI, create a Control Plane connection.
- Generate or import the connection credentials.
- In the Control Plane UI, register the Runtime node and credentials.
- Release deployed artifacts from the Control Plane.
- Let the Runtime pull pending artifacts, or push artifacts to the Runtime delivery endpoint.
The sample hosts show the intended shape:
samples/KnOwl.ControlPlaneHost.Samplesamples/KnOwl.RuntimeHost.Sample
The sample design-time DbContext factories target the same sample databases used at runtime. This keeps dotnet ef database update aligned with the hosts you run locally.
Contract Catalog
Control Plane hosts expose deployed source artifacts only when their artifact status is deployed:
GET /contracts/artifactsGET /contracts/events/{eventKey}/versions/{versionNumber}GET /contracts/commands/{commandKey}/versions/{versionNumber}
Runtime hosts expose deployed local artifacts:
GET /runtime/contracts/artifactsGET /runtime/contracts/events/{eventKey}/versions/{versionNumber}GET /runtime/contracts/commands/{commandKey}/versions/{versionNumber}
Event responses return one artifact. Command responses return both sides of the command version:
{
"commandKey": "inventories.reserve",
"version": "1.0.0",
"requestArtifact": {
"artifactType": "CommandRequest",
"payloadSchema": {}
},
"replyArtifact": {
"artifactType": "CommandReply",
"payloadSchema": {}
}
}
replyArtifact is optional. Request artifacts are required.
REST API
KnOwl includes Minimal API packages for automation, CI tooling, portals, and custom hosts that need to drive KnOwl without using the Razor UI.
Control Plane hosts expose:
GET /api/v1/control-plane/schema-typesPOST /api/v1/control-plane/schema-typesGET /api/v1/control-plane/metadata-fieldsPOST /api/v1/control-plane/metadata-fieldsGET /api/v1/control-plane/eventsPOST /api/v1/control-plane/eventsPOST /api/v1/control-plane/events/{id}/versionsPOST /api/v1/control-plane/events/{id}/versions/{versionId}/transitionGET /api/v1/control-plane/commandsPOST /api/v1/control-plane/commandsPOST /api/v1/control-plane/commands/{id}/versionsPOST /api/v1/control-plane/commands/{id}/versions/{versionId}/transitionGET /api/v1/control-plane/artifactsPOST /api/v1/control-plane/artifacts/events/{versionId}/buildPOST /api/v1/control-plane/artifacts/commands/{versionId}/buildGET /api/v1/control-plane/runtime-environmentsGET /api/v1/control-plane/runtime-nodesPOST /api/v1/control-plane/runtime-nodes/{id}/credentials/generatePOST /api/v1/control-plane/runtime-nodes/{id}/credentials/importPOST /api/v1/control-plane/runtime-nodes/{id}/connect/validateGET /api/v1/control-plane/releasesPOST /api/v1/control-plane/releasesPOST /api/v1/control-plane/releases/{id}/planPOST /api/v1/control-plane/releases/{id}/execute
Runtime hosts expose:
GET /api/v1/runtime/statusGET /api/v1/runtime/artifactsGET /api/v1/runtime/artifacts/events/{eventKey}/versions/{versionNumber}GET /api/v1/runtime/artifacts/commands/{commandKey}/versions/{versionNumber}GET /api/v1/runtime/control-planesPOST /api/v1/runtime/control-planesPOST /api/v1/runtime/control-planes/{id}/credentials/generatePOST /api/v1/runtime/control-planes/{id}/credentials/importPOST /api/v1/runtime/control-planes/{id}/connect/validateGET /api/v1/runtime/control-planes/{sourceKey}/artifacts/pendingPOST /api/v1/runtime/control-planes/{sourceKey}/artifacts/{releaseTargetId}/apply
Creating a command through the API captures both schemas in the first version:
POST /api/v1/control-plane/commands
{
"name": "Reserve Inventory",
"topic": "inventories.reserve",
"description": "Reserve stock before checkout.",
"versionNumber": "1.0.0",
"requestDefinitionJson": "{\"type\":\"object\",\"properties\":{\"sku\":{\"type\":\"string\"}}}",
"replyDefinitionJson": "{\"type\":\"object\",\"properties\":{\"accepted\":{\"type\":\"boolean\"}}}",
"comment": "Initial command contract."
}
Security administration endpoints are also available on the Control Plane:
GET /api/v1/control-plane/security/subjectsPUT /api/v1/control-plane/security/subjectsGET /api/v1/control-plane/security/role-assignmentsPOST /api/v1/control-plane/security/role-assignmentsGET /api/v1/control-plane/security/permission-assignmentsPOST /api/v1/control-plane/security/permission-assignmentsGET /api/v1/control-plane/security/external-group-role-assignmentsPOST /api/v1/control-plane/security/external-group-role-assignments
Hosts remain responsible for authentication. The API packages do not force JWT, cookies, managed identity, or API-key infrastructure.
Security
KnOwl keeps identity provider concerns in the host and keeps authorization rules in reusable libraries:
- The host authenticates users with Entra ID, cookies, OpenID Connect, JWT bearer tokens, or any other ASP.NET Core authentication handler.
- KnOwl resolves the authenticated principal into an external subject using configurable claims.
- KnOwl stores known subjects, direct roles, direct permissions, and external group-to-role mappings.
- KnOwl policies protect Web/API surfaces without depending on a specific identity provider.
Typical Control Plane setup:
using KnOwl.ControlPlane.Bootstrap;
using KnOwl.Security.Authorization;
using KnOwl.Security.Subjects;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddAuthentication(/* host-owned scheme */)
.AddJwtBearer(/* Entra ID, Auth0, local STS, etc. */);
builder.Services.AddKnOwlControlPlane(builder.Configuration, options =>
{
options.MigrationsAssembly = typeof(Program).Assembly.GetName().Name;
options.Security.RequireKnownSubject = true;
options.Security.Subject.Provider = "entra-id";
options.Security.BootstrapAdmins.Add(new KnOwlBootstrapSubject
{
Provider = "entra-id",
SubjectId = "<external-user-object-id>"
});
options.Authorization.SecurityManagePolicy = KnOwlAuthorizationPolicies.SecurityManage;
options.Authorization.CommandsWritePolicy = KnOwlAuthorizationPolicies.CommandsWrite;
options.Authorization.EventsWritePolicy = KnOwlAuthorizationPolicies.EventsWrite;
options.Authorization.ArtifactsBuildPolicy = KnOwlAuthorizationPolicies.ArtifactsBuild;
options.Authorization.ReleasesExecutePolicy = KnOwlAuthorizationPolicies.ReleasesExecute;
});
var app = builder.Build();
app.MapKnOwlControlPlane();
app.Run();
RequireKnownSubject blocks authenticated users until they are registered in KnOwl. Bootstrap admins are the first-run and recovery mechanism: they are matched by provider and external subject id, receive admin access, and can be synchronized into KnOwl security storage.
Built-in roles:
ReaderDesignerReleaseManagerRuntimeOperatorSecurityAdminAdmin
External identity groups can be mapped to KnOwl roles, so Entra ID or another provider can remain the system of record for users while KnOwl remains the system of record for product-specific access.
Local Development
Build and test:
dotnet restore KnOwl.slnx
dotnet build KnOwl.slnx --no-restore --configuration Release
dotnet test KnOwl.slnx --no-build --configuration Release
Pack the libraries:
Get-ChildItem src -Recurse -Filter *.csproj | ForEach-Object {
dotnet pack $_.FullName --configuration Release -o artifacts/packages
}
Run the distribution end-to-end test. This starts SQL Server in Docker, runs the Control Plane and Runtime samples, and validates SQL Server storage plus push/pull artifact distribution for both target frameworks.
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/run-knowl-distribution-e2e.ps1
Release
The release workflow builds, tests, packs, creates the GitHub release, and publishes NuGet packages. Production releases are driven by .release and CHANGELOG.md.
Current release: 1.0.4
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- ButterMorph.Web.Razor (>= 1.0.2)
- KnOwl.ControlPlane.Api (>= 2.0.0)
- KnOwl.ControlPlane.Application (>= 2.0.0)
- KnOwl.ControlPlane.Storage.EntityFramework (>= 2.0.0)
- KnOwl.ControlPlane.WebUI (>= 2.0.0)
- KnOwl.Documentation.Api (>= 2.0.0)
- KnOwl.Documentation.Application (>= 2.0.0)
- KnOwl.Documentation.Storage.EntityFramework (>= 2.0.0)
- KnOwl.Documentation.WebUI (>= 2.0.0)
- KnOwl.Security (>= 2.0.0)
- KnOwl.Security.Storage.EntityFramework (>= 2.0.0)
-
net9.0
- ButterMorph.Web.Razor (>= 1.0.2)
- KnOwl.ControlPlane.Api (>= 2.0.0)
- KnOwl.ControlPlane.Application (>= 2.0.0)
- KnOwl.ControlPlane.Storage.EntityFramework (>= 2.0.0)
- KnOwl.ControlPlane.WebUI (>= 2.0.0)
- KnOwl.Documentation.Api (>= 2.0.0)
- KnOwl.Documentation.Application (>= 2.0.0)
- KnOwl.Documentation.Storage.EntityFramework (>= 2.0.0)
- KnOwl.Documentation.WebUI (>= 2.0.0)
- KnOwl.Security (>= 2.0.0)
- KnOwl.Security.Storage.EntityFramework (>= 2.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.