Open-source SDK and reference implementations of OmnibusCloud controllers. Distributed via nuget.org as a set of consumable NuGet packages. Each controller demonstrates a distinct slice of OmnibusCloud's distributed-compute capabilities — basic types, distributed iteration, control flow, linear algebra, distributed rendering — and serves as a working template for authors writing their own controllers.
Note
The OmnibusCloud runtime and orchestrator that distribute jobs across worker nodes are closed-source. This repository publishes only the controllers and their associated authoring tools, all under the MIT license, so third parties can build on the same surface.
All packages are on nuget.org under the OutWit.Controller.* namespace.
Each row is a stable, supported release.
| Package | Latest | Tier | Purpose |
|---|---|---|---|
OutWit.Controller.Variables |
1 | Primitive types (Int / Bool / String / DateTime / Color / ...) + collections + ranges + tuples. | |
OutWit.Controller.Special |
1 | Control-flow activities — If, Loop, ForEach, parallel variants, Timer, Trace, diagnostics. |
|
OutWit.Controller.Grid |
1 | Dense grid layout computation with distributed Grid.ForEach. |
|
OutWit.Controller.Matrices |
2 | Dense + sparse matrix / vector operations with Gustavson multiplication. Ships benchmark .smat data via GitHub Release. |
|
OutWit.Controller.Render.Dcc |
1 | Host-only neutral DCC scene validation and .blend build bootstrap, upstream of Render. |
|
OutWit.Controller.Render |
2 | Distributed rendering via Blender CLI + FFmpeg. Ships per-platform Blender / FFmpeg / benchmark scenes via GitHub Release. |
Each controller comes with a companion OutWit.Controller.<Name>.Model
NuGet that contains shared data types — referenced transitively by
consumers, available standalone for tooling.
Add the controllers you need. NuGet pulls Models as transitive deps; the
consumer-side build/.targets in every package stages everything into
@Controllers/<Configuration>/<name>.module/ at build time.
dotnet add package OutWit.Controller.Variables
dotnet add package OutWit.Controller.Grid
dotnet add package OutWit.Controller.Render # ~370 KB nupkg, fetches ~2.2 GB of Blender + FFmpeg at build
dotnet buildAfter dotnet build, the runtime layout looks like:
bin/Debug/net10.0/@Controllers/Debug/
variables.module/
OutWit.Controller.Variables.dll
OutWit.Controller.Variables.Model.dll
controller.json
grid.module/...
render.module/
OutWit.Controller.Render.dll
OutWit.Controller.Render.Model.dll
controller.json
blender/{windows-x64,linux-x64,macos-arm64}/...
ffmpeg/{windows-x64,linux-x64,macos-arm64}/...
benchmark_scene.blend
benchmark_scene_still.blend
benchmark_scene_video.blend
The @Controllers/ layout is what the OmnibusCloud runtime expects.
A controller is a self-contained capability — a set of activities and
variable types — packaged as a .module/ directory loadable by the
OmnibusCloud runtime. A controller declares itself in a controller.json
manifest that's bundled inside its NuGet package's content/module/.
- Activity — declarative shape of an operation. Plain data class
derived from
WitActivity*, decorated[MemoryPackable], serialised across the network when distributed. - Adapter — host- or node-side execution. Reads the activity, pulls inputs from the variables pool, runs the actual work, writes outputs.
- Model — types shared between activity and runtime (often the inputs and outputs of activities). Ships as a separate NuGet so external tooling can reference these types without taking the whole controller as a runtime dep.
| Aspect | Tier 1 | Tier 2 |
|---|---|---|
| Examples | Variables, Special, Grid, Render.Dcc | Matrices, Render |
| External assets | none | declared as <ControllerDataAsset> in csproj, hosted on a per-version GitHub Release |
| nupkg size | ~10-300 KB (pure .NET) | ~50 KB-1 MB (just .NET + manifest) |
| Consumer-side fetch | nothing extra | ResolveControllerAssetsTask downloads assets from GH Release at build time; content-addressed cache at ~/.outwit/asset-cache/ |
| When to use | logic-only controllers | controllers that need native binaries, large datasets, or platform-specific blobs |
Two ways a controller reaches an end-user environment:
-
Path A — first-party (this repo). Published to nuget.org by an automated workflow; consumer just adds the package. Tier-2 controllers additionally publish a GitHub Release pre-populated by the author tool (
outwit-assets-pack). The Publish workflow has a guard that refuses to push the nupkg until every declared GH Release asset is verifiably reachable. -
Path B — third-party contributor. Authors who don't have nuget.org publish rights pack their built controller into a self-contained zip using
outwit-controller-packand upload it through the OmnibusCloud admin UI for review. The Pack tool defaults to refusing external<ControllerDataAsset>URIs — everything must be inlined in the zip — unless the author explicitly opts in.
Tip
See docs/controller-author-guide.md
for a deep dive (under construction).
Short version: copy one of the existing controller pairs as a template,
adjust ControllerName / Description / PackageTags / activities, and
let the shared Build/OutWit.Controller.props + OutWit.Controller.targets
handle the rest of the wiring.
Minimal controller csproj:
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Build\OutWit.Controller.props" />
<PropertyGroup>
<ControllerName>MyController</ControllerName>
<Version>1.0.0</Version>
<Description>What this controller does.</Description>
<PackageTags>witengine;witcloud;controller;my-tag</PackageTags>
<ControllerFeatures>my-feature</ControllerFeatures>
<ControllerUseCases>What it's for</ControllerUseCases>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\OutWit.Controller.MyController.Model\OutWit.Controller.MyController.Model.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="build\OutWit.Controller.MyController.targets" Pack="true" PackagePath="build\" />
</ItemGroup>
<Import Project="..\..\Build\OutWit.Controller.targets" />
</Project>The Model csproj is even smaller — three property lines + the shared
imports. See Variables/OutWit.Controller.Variables.Model/ for the
canonical minimal Model shape.
.
├── Build/ # Shared MSBuild props + targets
│ ├── OutWit.Controller.props # Controller csproj boilerplate
│ ├── OutWit.Controller.targets # PostBuild + pack module + zip
│ ├── OutWit.Controller.Manifest.targets # controller.json emitter + validators
│ ├── OutWit.Controller.Model.props # Model csproj boilerplate
│ └── OutWit.Controller.Model.targets # Model PostBuild (stages into parent .module/)
│
├── Variables/ # Tier-1 controller (primitives + collections)
│ ├── OutWit.Controller.Variables/ # Activities + adapters + manifest
│ └── OutWit.Controller.Variables.Model/ # Shared data types
│
├── Special/ # Tier-1 controller (control flow)
├── Grid/ # Tier-1 controller (distributed ForEach)
├── Matrices/ # Tier-2 controller (linear algebra + sparse, ships .smat data)
│ └── OutWit.Controller.Matrices/Resources/ # The .smat asset sources
│
├── Render/
│ ├── OutWit.Controller.Render/ # Tier-2 controller (Blender + FFmpeg pipeline)
│ ├── OutWit.Controller.Render.Model/
│ ├── OutWit.Controller.Render.Dcc/ # Tier-1 host-only DCC bootstrap
│ └── OutWit.Controller.Render.Dcc.Model/
│
├── Tools/
│ ├── OutWit.Controller.Pack/ # Path-B author tool: pack module/ -> contributor zip
│ ├── OutWit.Controller.Pack.Tests/
│ ├── OutWit.Controller.Assets.Pack/ # Path-A author tool: stage Tier-2 assets + GH Release publish
│ └── OutWit.Controller.Assets.Pack.Tests/
│
├── .github/workflows/
│ ├── publish.yml # Unified publish pipeline (any controller / model / tool)
│ └── verify-render-consumer.yml # Cold-build smoke test of the published Render package
│
├── OutWit.slnx # Solution file (SLNX format, .NET 10)
├── LICENSE # MIT
└── README.md # this file
The shared Build/ props + targets handle every boilerplate concern so
each controller csproj only carries its own identity. The flow at build
time is roughly:
<ControllerName>,<Version>,<Description>etc. set in the per-controller csproj.Build/OutWit.Controller.propsinjects<TargetFramework>net10.0</TargetFramework>,<PackageLicenseExpression>MIT</PackageLicenseExpression>, common dependencies (OutWit.Engine.Data,OutWit.Engine.Assets.MSBuild), icon / readme / license packaging.Build/OutWit.Controller.Manifest.targetsvalidates the declarations (Sha256/Uri required on every asset, etc.), then emits thecontroller.jsonfor the build.Build/OutWit.Controller.targetsstages the controller's outputs into$(SolutionDir)@Controllers/<Cfg>/<name>.module/, produces an@Zips/<Cfg>/<name>.zip(used by the BlobCacheService path on the OmnibusCloud server side), and packs the staged module dir into the nupkg'scontent/module/plus alib/<tfm>/_._marker (so NuGet considers the package framework-compatible).- The consumer-side
build/OutWit.Controller.<Name>.targetsships inside each nupkg'sbuild/folder and auto-imports in any consumer csproj that references the package. It copiescontent/module/*into the consumer's$(OutputPath)@Controllers/<Cfg>/<name>.module/and invokesResolveControllerAssetsTaskfromOutWit.Engine.Assets.MSBuildto fetch external assets (Tier 2 only).
The author-side tool for Tier-2 controllers. Reads
<ControllerDataAsset> declarations from your csproj, packs the matching
source folders / files into staged artifacts, computes SHA256, rewrites
the csproj with the new SHA / Size / Uri, and optionally creates the
matching GitHub Release with everything uploaded — in a single command.
dotnet tool install -g OutWit.Controller.Assets.Pack
outwit-assets-pack My.Controller.csproj --prerequisites ./assets --version 1.2.0 --apply --push-releaseSee Tools/OutWit.Controller.Assets.Pack/README.md for the full reference.
The Path-B contributor tool. Reads a built <name>.module/ directory,
validates the manifest, refuses external asset URIs by default
(everything must be inlined in the zip), and produces a single
self-contained .zip ready for upload through the OmnibusCloud admin UI.
dotnet tool install -g OutWit.Controller.Pack
outwit-controller-pack ./bin/Release/net10.0/@Controllers/Debug/my-controller.moduleSee Tools/OutWit.Controller.Pack/README.md.
| Workflow | Trigger | Purpose |
|---|---|---|
publish.yml |
workflow_dispatch |
Publish any project. Tier-2 controllers (Render) have a release-assets-exist guard before the nuget.org push. |
verify-render-consumer.yml |
workflow_dispatch |
Cold-build smoke test: PackageReference the published Render package, assert every external asset materialised. |
A separate external smoke test lives outside this repo at
@Verify/ControllerConsumerCheck/ and consumes every published
controller through nuget.org in a single dotnet build, then runs
verify.ps1 to assert all 28 expected paths land correctly.
The engine runtime, parser, and orchestrator components of OmnibusCloud are closed-source. The data layer and the assets infrastructure are published openly so controllers and their tooling can build on the same surface.
| Package | Role |
|---|---|
OutWit.Engine.Data |
Data models + base classes (ModelBase, value comparisons, etc.). |
OutWit.Engine.Interfaces |
Core interfaces and contracts. |
OutWit.Engine.Assets.MSBuild |
The ResolveControllerAssetsTask MSBuild task that materialises Tier-2 assets. |
OutWit.Engine.Assets |
Underlying manifest reader + resolver chain + content-addressed cache. |
OutWit.Engine.Sdk |
Dev-time SDK with a single-node engine instance. Used in test projects only, never referenced by a controller's own csproj. |
The runtime split is intentional:
-
A controller's own csproj references only
OutWit.Engine.Data(andOutWit.Engine.Interfacesfor Models that need them). These are contract-level packages — types and base classes. The sharedBuild/OutWit.Controller.propspulls them in automatically. Controllers do not depend on the engine runtime — they're loaded by it. -
The controller's test project (e.g.
MyController.Tests.csproj) referencesOutWit.Engine.Sdk. The SDK ships a single-node engine you can spin up in-process, point at a built<name>.module/, and drive through job scripts:[OneTimeSetUp] public void Setup() => WitEngineSdk.Instance.Reload(); [Test] public async Task MyActivity_DoesItsThing() { var job = WitEngineSdk.Instance.Compile(@" Job:Test() { Int:result = MyActivity(21); } "); var status = await WitEngineSdk.Instance.ScheduleAndWaitAsync(job); Assert.That(status.Result, Is.EqualTo(WitProcessingResult.Completed)); }
This is how a third-party author validates that OmnibusCloud will actually load and run their controller, without needing access to the full closed engine sources.
OutWit.Engine.Sdk is dev-time only and runs with intentionally tight
limits. The production OmnibusCloud orchestrator lifts them:
| Limit | SDK value |
|---|---|
| Max activities per job | 50 |
| Max variables per job | 100 |
| Max execution time | 5 minutes |
| Max nodes | 1 (local) |
| Max variable size | 100 MB |
Pull requests for new controllers, bug fixes, or doc improvements are
welcome. For new controllers, follow the patterns in any existing
controller — same shared Build/ imports, same Tests/ project layout,
same controller.json shape. The author guide will eventually walk
through this end to end; until then, see how Variables or Grid are
structured.
For Path-B distribution (you don't have nuget.org publish rights):
build your controller, pack it with outwit-controller-pack, and
upload the produced zip through the OmnibusCloud admin UI. An admin
will review it and approve for the runtime to load.
MIT — see LICENSE. All controllers and authoring tools in this repository are released under MIT.