Skip to content

feat(endpoints): add native C# endpoint host - #25

Merged
KelvinTegelaar merged 6 commits into
mainfrom
dev
Aug 6, 2026
Merged

feat(endpoints): add native C# endpoint host#25
KelvinTegelaar merged 6 commits into
mainfrom
dev

Conversation

@Zacgoose

@Zacgoose Zacgoose commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Introduce opt-in native endpoint discovery, registration, routing, and response/request abstractions so C# handlers can run alongside PowerShell endpoints with configurable collision and concurrency behavior. Also add derived thread-pool minimum sizing, support Worker:HttpPoolSize=0 for fully native HTTP hosting, update sample configuration, and cover the new endpoint/threading behaviors with tests.

Introduce opt-in native endpoint discovery, registration, routing, and response/request abstractions so C# handlers can run alongside PowerShell endpoints with configurable collision and concurrency behavior. Also add derived thread-pool minimum sizing, support `Worker:HttpPoolSize=0` for fully native HTTP hosting, update sample configuration, and cover the new endpoint/threading behaviors with tests.
@Zacgoose
Zacgoose requested a review from JohnDuprey August 2, 2026 08:13
Zacgoose and others added 5 commits August 2, 2026 16:16
Timer Commands resolve native-first against the discovered
[CraftScheduledTask] classes, then fall back to the PowerShell script
table unchanged. Native tasks run on the .NET thread pool through the
existing JobManager, so priority ordering, job records and the
background concurrency limiter apply as-is. A Command present in both
worlds fires the native task and logs the shadowed function - same
rule, same visibility, as route collisions. IsOrchestratorOverride is
rejected on native commands at load and at fire time: the planner/task
split is a PowerShell construct, and native code does its own fan-out.

Worker:BgPoolSize=0 now builds no BG runspaces, mirroring
HttpPoolSize=0. The disabled pool signals ready immediately, so the
scheduler still starts; with both sizes 0 the container hosts no
PowerShell at all. BgTimeoutSeconds maps to cooperative cancellation
for native tasks - documented on the contract, since a task that
ignores its token runs on.

feat(endpoints): central handler dispatch + native task contracts

ICraftEndpointHandler is the native counterpart of Scripts:HttpHandler -
one entrypoint every Central-dispatch endpoint routes through, with
per-route opt-out declared in code ([CraftEndpoint(Dispatch = Direct)])
for routes that authenticate differently (webhook signatures) or serve
anonymous callers deliberately. At most one handler per application; a
second fails startup. Zero is legal and preserves today's behaviour.

- CraftRequest.Endpoint exposes the attribute so a handler keys
  authorization off Role, the native .ROLE equivalent
- App:Endpoints:RequireHandler fails startup when Central endpoints
  exist with no handler - the CI gate for apps whose auth lives there
- Direct-dispatch routes are enumerated in the startup log
- Discovery now returns a catalog (endpoints, handler, scheduled-task
  contracts, assemblies); service modules are found per assembly rather
  than per endpoint-bearing assembly
- ICraftScheduledTask / [CraftScheduledTask] contracts and discovery
  land here; the scheduler wires them up in the next commit
Add a shared `CraftJson.Web` serializer options instance and use it as the default for `CraftRequest.ReadJsonAsync` and `CraftResult.Json`. This aligns native endpoint JSON behavior with ASP.NET Core conventions (camelCase output and case-insensitive binding), while still honoring explicitly provided serializer options. Includes focused tests covering camelCase binding, PascalCase compatibility, explicit-option overrides, and raw JSON pass-through.
Standards batch items share FunctionName='CIPPStandard', causing task ID collisions. This fix folds in Standard name, TemplateId, and Settings.TemplateList.value to generate unique IDs — mirroring the key CIPP uses for rerun detection.

Also handles Tenant as either a plain domain string or a nested object, and guards TenantFilter extraction against non-string JSON values.
@KelvinTegelaar
KelvinTegelaar merged commit 5448f72 into main Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants