Lesson 1.1 (Embedded track): Mount the workbench in your host
Difficulty: Beginner | Duration: 12 min | Prerequisites: .NET 10 SDK; familiar with ASP.NET
WebApplication/Program.csshape
Overview
Add the Kuestenlogik.Bowire NuGet to a small REST service and mount the workbench at /bowire on the host's own port. One process, no second terminal, no --url flag — Bowire reads the same IServiceProvider your own routes do.
Why mount inside the host
The CLI shape (bowire --url ...) makes Bowire an external probe — it talks to your service across the wire. Embedded flips that: the workbench is a regular ASP.NET endpoint of your host, so it can see anything the host's DI container exposes:
- the OpenAPI document
MapOpenApi()generates, - the gRPC service registry from
AddGrpc(), - SignalR hubs from
AddSignalR(), - custom protocols you register through
IBowireProtocol,
…all in one pass, without you having to enumerate URLs. The trade-off: the workbench's lifecycle becomes the host's lifecycle.
Steps
1. Take the sample REST service
cd units/unit-1-samples/HelloApi
Program.cs already looks like this:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/hello/{name}", (string name) =>
new { greeting = $"Hello, {name}!", receivedAt = DateTimeOffset.UtcNow });
app.MapPost("/echo", (EchoRequest req) =>
new { message = req.Message, receivedAt = DateTimeOffset.UtcNow });
app.MapGet("/health", () => new { status = "ok" });
app.Run("http://localhost:5001");
Two routes plus a health probe. We'll keep all of that — only add Bowire alongside.
2. Add the embedded NuGet
dotnet add package Kuestenlogik.Bowire
This pulls in the workbench, its bundled protocols (REST, gRPC, SignalR, &c.), and the plugin host. Roughly 18 MB of static assets ship inside the NuGet — they're served from the package without you touching wwwroot/.
3. Wire the workbench into Program.cs
Two changes — one line near AddOpenApi(), one line near MapOpenApi():
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenApi();
builder.Services.AddBowire(); // ← new
var app = builder.Build();
app.MapOpenApi();
app.MapBowire(); // ← new
app.MapGet("/hello/{name}", …); // your existing routes stay put
app.MapPost("/echo", …);
app.MapGet("/health", …);
app.Run("http://localhost:5001");
AddBowire() registers the plugin host into DI; MapBowire() mounts the workbench at /bowire. Your existing routes stay exactly where they are.
4. Run the host
dotnet run
You should see:
Now listening on: http://localhost:5001
Application started. Press Ctrl+C to shut down.
One process — no second terminal, no bowire CLI involved.
5. Invoke a method
Open http://localhost:5001/bowire in your browser. The workbench reads the same OpenAPI document MapOpenApi() already serves; the sidebar populates without you doing anything.
- Click HelloApi (or whatever your sample's
info.titlesays) in the sidebar. - Click GetGreeting.
- The right pane shows a form — fill in
name = "Bowire". - Click Invoke.
You'll see the JSON response:
{
"greeting": "Hello, Bowire!",
"receivedAt": "2026-05-31T08:30:14.123Z"
}
Try PostEcho too — the form auto-builds a JSON body editor from the EchoRequest schema.
Key Takeaways
- Two calls —
AddBowire()andMapBowire(). That's the whole integration surface from the host's side. Everything else is auto-discovery against the same DI container the host already populates. - The workbench shares the host's port + lifecycle. Same
http://localhost:5001, sameCtrl+C, same deploy bundle. No second process to orchestrate. - Your existing routes don't move.
AddBowire()doesn't claim any existing route;MapBowire()mounts at/bowire(configurable viaBowire:MountPath). Existing endpoints, middleware, auth are untouched. - The same NuGet powers production deploys. Wrap the registrations in
IsDevelopment()or#if DEBUGto gate the workbench out of release builds — same package, two configurations.
What's Next
Continue: → Lesson 1.2 — Add gRPC to the same host