Virtufin Workers
A C#/.NET 10 worker source package for the Virtufin WorkManager. Each worker is a self-contained nupkg consumable by the WM's DotNetDllEngine (managed, in-process) or NativeDllEngine (NativeAOT, in-process), with the engine selected at CreateWorker time by the MIME type.
The canonical worker is HttpGetWorker — it performs an outbound HTTP GET on every triggering CloudEvent and publishes the response as a new CloudEvent. It demonstrates the full pattern: a shared handler logic, a managed shim, a NativeAOT shim, content-addressed versioning, and the two packaging modes.
Key Features
| Feature | Description |
|---|---|
| Shared handler logic | One .cs file compiled into both managed and AOT projects — the bulk of the worker code is identical regardless of which engine loads it. |
| Three packaging modes | managed (lib/net10.0/.dll), native (runtimes/both (single nupkg containing both layouts). |
| Content-addressed versioning | The build script produces a deterministic, byte-stable nupkg whose filename encodes the SHA-256 short hash. Re-publishes are no-ops when source is unchanged. |
| NativeAOT-ready | The AOT csproj sets <PublishAot>true</PublishAot> + per-RID <ObjCopyName>; the build script runs dotnet publish inside the dotnet-crossbuild:10 Docker image (clang + cross-binutils). |
IGatewayClient abstraction |
For a worker that does call other services — see the Worker Author Guide's EchoWorker example — await gateway.InvokeAsync(...) routes to managed gRPC or the host's C-ABI gateway callback. HttpGetWorker itself makes no backend gateway calls, so it needs none of this wiring — a deliberate contrast worth knowing before you assume every worker needs it. |
Worker Layout
HttpGetWorker/
├── config/
│ └── config.json # script_url_template, topics, mime_type, etc.
├── src/
│ ├── Virtufin.Worker.HttpGetWorker.Managed/
│ │ ├── *.csproj # OutputType=Library, <PackageId>=...
│ │ ├── HttpGetWorker.cs # managed shim
│ │ ├── HttpGetWorkerLogic.cs # shared logic
│ │ └── HttpGetResponseCodec.cs # response CloudEvent shaping
│ └── Virtufin.Worker.HttpGetWorker.Native/
│ ├── *.Native.csproj # <PublishAot>, <RuntimeIdentifiers>=linux-x64,linux-arm64
│ └── HttpGetWorkerNative.cs # AOT shim with [UnmanagedCallersOnly]
├── tests/
│ └── Virtufin.Worker.HttpGetWorker.Tests/
├── scripts/
│ ├── build_managed.py # dotnet publish the managed csproj
│ ├── build_aot.py # dotnet publish -r <RID> --publish-aot
│ ├── build_both.py # produce a nupkg with BOTH layouts
│ ├── publish.py # push the nupkg to the Gitea NuGet feed
│ ├── create_worker.py # deploy via CreateWorker
│ ├── start_worker.py # start a deployed worker
│ └── delete_worker.py # remove a deployed worker
└── @common/ # shared with the @common scripts in the repo root
└── scripts/
├── build_managed.py # the canonical managed build (shared helpers)
├── build_aot.py # the canonical AOT build
└── publish.py # the canonical publish
Build & Run
The three packaging modes:
# 1. managed-only (lib/net10.0/*.dll)
python3 scripts/build_managed.py
python3 scripts/publish.py
# 2. NativeAOT-only (runtimes/<rid>/native/lib*.so) — requires Docker
python3 scripts/build_aot.py
python3 scripts/publish.py
# 3. both — single nupkg supporting either engine
python3 scripts/build_both.py
python3 scripts/publish.py
Then deploy and run:
# Create + start against the local WM + API + Dapr
python3 scripts/create_worker.py --api-client-host localhost --api-client-port 5002
python3 scripts/start_worker.py --id <worker_id>
Trigger it by publishing a CloudEvent on command.httpgetworker whose
data is {"url": "https://example.com/data"} — see the README
for the full configuration surface.
Get Started
- Overview — What workers are and how the WM loads them
- Getting Started — Build, publish, and run HttpGetWorker end-to-end
- Worker Author Guide — Author a new worker in the recommended shape
- Engines — DotNetDll vs NativeDll: how each loads, how the IGatewayClient routes
- Packaging Modes — managed / native / both, and which to pick