Dependency injection
The OpenShell.Net.DependencyInjection package registers OpenShell clients with Microsoft.Extensions.DependencyInjection, for ASP.NET Core and Generic Host apps.
dotnet add package OpenShell.Net.DependencyInjection
Register from configuration
AddOpenShell binds OpenShellOptions from a configuration section and registers a singleton IOpenShellClient, plus its per-area clients (IHealthClient, ISandboxClient, IExecClient, IWorkspaceClient, ISandboxTemplateClient, IProviderClient, IConfigClient, IPolicyClient, ISandboxWatchClient). The options are validated when the host starts, so a bad address, an unreadable certificate, or a token headed for plaintext HTTP fails at startup instead of on the first call.
var builder = WebApplication.CreateBuilder(args);
// Binds the "OpenShell" section (see appsettings.json) and validates it when the host starts.
builder.Services.AddOpenShell(builder.Configuration.GetSection("OpenShell"));
builder.Services.AddHealthChecks().AddOpenShellHealthCheck();
var app = builder.Build();
app.MapHealthChecks("/healthz");
app.MapGet("/sandboxes", async (ISandboxClient sandboxes, CancellationToken cancellationToken) =>
{
var list = await sandboxes.List().ToListAsync(cancellationToken);
return list.Select(s => new { s.Name, Phase = s.Phase.ToString() });
});
app.Run();
{
"OpenShell": {
"Address": "https://gateway.example.com",
"Workspace": "team-a",
"CaCertificatePath": "/etc/openshell/ca.crt",
"ClientCertificatePath": "/etc/openshell/client.crt",
"ClientKeyPath": "/etc/openshell/client.key",
"DefaultTimeout": "00:00:30",
"RouteToOwnerReplica": true,
"Retry": {
"MaxAttempts": 6,
"MaxBackoff": "00:00:10"
}
}
}
| Key | Meaning |
|---|---|
Address |
The gateway URL. Required. |
Workspace |
The default workspace (default). |
Token |
A static bearer token. Keep it in user secrets, environment variables, or a vault, not in the JSON file. |
CaCertificatePath |
A PEM file of CA certificates that the gateway certificate must chain to. |
ClientCertificatePath, ClientKeyPath |
A PEM client certificate and key for mTLS. |
InsecureSkipVerify |
Skip server certificate validation (development only). |
AllowInsecureAuth |
Allow credentials over plaintext HTTP to a non-loopback host. |
DefaultTimeout |
Deadline for unary calls, as a TimeSpan (00:00:30). |
Retry |
MaxAttempts, InitialBackoff, MaxBackoff, BackoffMultiplier, Jitter, RetryableStatusCodes; unset values keep the defaults. |
RouteToOwnerReplica |
Owner routing (true). |
The client is created on first use and disposed with the container. services.AddHealthChecks().AddOpenShellHealthCheck() adds a health check that calls the gateway health RPC; pass gatewayName to check a named registration.
Configure in code
services.AddOpenShell(options =>
{
options.Address = new Uri("https://gateway.example.com");
options.Workspace = "team-a";
options.Token = Environment.GetEnvironmentVariable("OPENSHELL_TOKEN");
options.DefaultTimeout = TimeSpan.FromSeconds(30);
options.Retry = new RetryOptions { MaxAttempts = 6 };
});
OpenShellOptions.Auth and OpenShellOptions.Tls take any IAuthProvider or TlsOptions and win over Token and the certificate paths. The AddOpenShell(name, (provider, options) => ...) overload configures them from other services.
The fluent builder
AddOpenShellClient returns an IOpenShellClientBuilder with a method for each setting. A name registers a separate, keyed client for each gateway:
services.AddOpenShellClient("prod")
.WithAddress("https://gateway.example.com")
.WithWorkspace("team-a")
.WithClientCredentials(
"https://idp.example.com/realms/openshell",
"openshell-service",
cancellationToken => ValueTask.FromResult(Environment.GetEnvironmentVariable("OPENSHELL_OIDC_CLIENT_SECRET")!),
"sandbox:read",
"sandbox:write")
.WithCaCertificate("/etc/openshell/ca.crt")
.WithTimeout(TimeSpan.FromSeconds(30))
.WithRetry(retry => retry.MaxAttempts = 6);
services.AddOpenShellClient("local")
.WithAddress("http://127.0.0.1:8080")
.WithoutRetry();
WithClientCredentials registers an OIDC ClientCredentialsAuth as a container singleton, so the container disposes it. Other methods: WithBearerToken, WithAuth, WithCaCertificate, WithClientCertificate, WithTls, WithInsecureTls, AllowInsecureAuth, WithTimeout, WithRetry, WithoutRetry, and BindConfiguration.
// Build the auth provider from other services, for example a token cache.
services.AddOpenShellClient()
.WithAddress("https://gateway.example.com")
.WithAuth(provider => provider.GetRequiredService<ITokenCache>().AsAuthProvider());
Named clients
A named client is a keyed IOpenShellClient. Inject it with [FromKeyedServices("name")], or resolve it at run time from IOpenShellClientFactory. The per-area clients are registered for the default (unnamed) gateway only.
internal sealed class SandboxJanitor(
[FromKeyedServices("prod")] IOpenShellClient prod,
IOpenShellClientFactory factory)
{
public async Task CleanUpAsync(CancellationToken cancellationToken)
{
var expired = new ListOptions { LabelSelector = "ttl=expired" };
await foreach (var sandbox in prod.Sandboxes.List(expired).WithCancellation(cancellationToken))
{
await prod.Sandboxes.DeleteAsync(sandbox.Name, new DeleteOptions { AllowMissing = true }, cancellationToken);
}
// Or resolve a client by name at run time. The factory creates one client per name and owns it:
// do not dispose it.
var local = factory.Create("local");
Console.WriteLine(await local.Health.CheckAsync(cancellationToken));
}
}
Logging
The registered clients use the container's ILoggerFactory; see Observability.