> ## Documentation Index
> Fetch the complete documentation index at: https://docs.westyx.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# .NET SDK - ASP.NET Core IConfiguration

> Plug Westyx Nexus into ASP.NET Core's configuration pipeline with IOptions and live reload support.

*Package: `WestyxNexus.Extensions.Configuration`*

The SDK ships an `IConfigurationSource` / `IConfigurationProvider` implementation. With a single `AddWestyx(...)` call, all Nexus **config** values are exposed through `IConfiguration` — bind them to `IOptions<T>` classes, read them via `configuration["key"]`, and receive live `IOptionsMonitor<T>` updates when a value changes in the console.

<Note>
  **Since v0.11.0 this lives in its own package.** `Microsoft.Extensions.Configuration` left the core package, so run `dotnet add package WestyxNexus.Extensions.Configuration`. The namespace is unchanged (`Westyx.Nexus`), so no code edit is needed.
</Note>

<Warning>
  **Binding was broken before v0.11.0 and now works.** Dotted Nexus keys were inserted verbatim, and `IConfiguration`'s section separator is a colon — so `GetSection("database")` returned an **empty** section and `Configure<T>()` bound nothing. Keys are now emitted in both forms, and nested JSON is flattened. Any workaround you built is no longer needed, but will keep working.
</Warning>

**Secrets are not included** — only configs. See [Secrets binding](/sdks/dotnet/secrets-binding) for the strongly-typed secret path, and [Design decisions](/sdks/dotnet/design-decisions) for why secrets stay out of `IConfiguration` by default.

## Basic setup

```csharp theme={null}
var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddWestyx(new NexusConfig
{
    BaseUrl = "https://<slug>.westyx.dev",
    ApiKey  = Environment.GetEnvironmentVariable("NEXUS_API_KEY")!,
});

var app = builder.Build();
app.Run();
```

`AddWestyx` performs a **blocking initial sync** inside `Build()` — the same pattern used by Azure App Configuration and other standard providers. All Nexus config values are available from the moment `builder.Build()` returns.

The SSE live-update stream starts automatically (`enableStream: true` is the default). Turn it off if you prefer TTL-only polling:

```csharp theme={null}
builder.Configuration.AddWestyx(config, enableStream: false);
```

## Binding to IOptions\<T>

After calling `AddWestyx`, bind Nexus configs to a strongly-typed class the same way you would bind any other configuration section:

```csharp theme={null}
// Nexus config keys "database.host" and "database.port" are exposed BOTH as
// "database:host" / "database:port" (section-navigable, so this binds) and in
// their original dotted form (so configuration["database.host"] still works).
builder.Services.Configure<DatabaseSettings>(builder.Configuration.GetSection("database"));
```

```csharp theme={null}
public sealed class DatabaseSettings
{
    public string Host { get; set; } = "";
    public int    Port { get; set; } = 5432;
}
```

Inject `IOptions<DatabaseSettings>` where you need it:

```csharp theme={null}
public sealed class ProductRepository(IOptions<DatabaseSettings> db)
{
    public async Task<Product[]> GetAllAsync()
    {
        // db.Value.Host => "db.internal"
        // db.Value.Port => 5432
    }
}
```

## Live reload with IOptionsMonitor\<T>

When the SSE stream is running, the configuration provider calls `IConfiguration.Reload()` automatically after every sync that returns new data. This means `IOptionsMonitor<T>` receives change notifications within milliseconds of a value being updated in the Nexus console — **without a restart**.

```csharp theme={null}
// Use IOptionsMonitor instead of IOptions to get live updates.
builder.Services.Configure<FeatureSettings>(builder.Configuration.GetSection("feature"));
```

```csharp theme={null}
public sealed class FeatureService(IOptionsMonitor<FeatureSettings> features)
{
    public bool IsNewCheckoutEnabled =>
        features.CurrentValue.NewCheckout;

    public void RegisterChangeCallback()
    {
        features.OnChange(settings =>
        {
            // Called automatically when Nexus pushes an update.
            Console.WriteLine($"feature.new_checkout changed to {settings.NewCheckout}");
        });
    }
}
```

<Info>
  Live reload requires `ConnectStream()` to be active. `AddWestyx(config)` starts the stream automatically. If you use the pre-created client overload, call `client.ConnectStream()` yourself before registering the source.
</Info>

## Pre-created client (WIF, advanced lifetime)

If you need WIF authentication or want to manage the client lifetime yourself, pass an existing `NexusClient` to the overload:

```csharp theme={null}
var client = await NexusClient.CreateAsync(new NexusConfig
{
    BaseUrl = "https://<slug>.westyx.dev",
    Wif     = new WIFConfig { Enabled = true, Provider = WIFProvider.Kubernetes },
});
client.ConnectStream();

// Register as singleton so DI disposes it on shutdown.
builder.Services.AddSingleton(client);

// Wire into IConfiguration.
builder.Configuration.AddWestyx(client);
```

With this overload, **you are responsible** for starting the stream and disposing the client. The `builder.Services.AddSingleton(client)` registration ensures the host shuts the client down cleanly.

## Reading configs directly from IConfiguration

You can also read Nexus config values directly without binding to a class:

```csharp theme={null}
var host    = app.Configuration["database.host"];  // "db.internal"
var timeout = app.Configuration.GetValue<int>("api.timeout_ms");  // 3000
var debug   = app.Configuration.GetValue<bool>("app.debug");      // true
```

## Key shapes

A Nexus key `database.host` is reachable two ways, and both are supported deliberately:

| Lookup                                         | Works | Why it exists                                                                            |
| ---------------------------------------------- | ----- | ---------------------------------------------------------------------------------------- |
| `configuration["database:host"]`               | yes   | `IConfiguration`'s own separator — this is what makes `GetSection` and POCO binding work |
| `configuration["database.host"]`               | yes   | The original flat key; emitted so nothing written against earlier versions breaks        |
| `configuration.GetSection("database")["host"]` | yes   | Section navigation                                                                       |

Before v0.11.0 only the middle row worked, which is why `GetSection("database")` came back empty.

### Nested objects and arrays

A JSON object or array config value is flattened the way the built-in JSON provider does it, so it binds to a POCO or a collection:

| Nexus config                                         | IConfiguration keys                                                |
| ---------------------------------------------------- | ------------------------------------------------------------------ |
| `service.options` = `{"retries": 3, "timeout": 500}` | `service:options:retries` = `3`, `service:options:timeout` = `500` |
| `cors.origins` = `["https://a", "https://b"]`        | `cors:origins:0`, `cors:origins:1`                                 |

```csharp theme={null}
var origins = configuration.GetSection("cors:origins").Get<List<string>>();
```

Before v0.11.0 these were stored as one opaque raw-JSON string with no child keys, so they could not bind to anything.

## Value type serialization

Nexus config values are JSON. The provider serializes them to strings for IConfiguration:

| Nexus JSON value  | IConfiguration string                                       |
| ----------------- | ----------------------------------------------------------- |
| `"hello"`         | `hello`                                                     |
| `42`              | `42`                                                        |
| `3.14`            | `3.14`                                                      |
| `true`            | `True`                                                      |
| `false`           | `False`                                                     |
| `{"key":"value"}` | flattened into child keys — see [Key shapes](#key-shapes)   |
| `[1, 2]`          | flattened into indexed keys — see [Key shapes](#key-shapes) |
| `null`            | *(key absent)*                                              |

Standard binding (`IOptions<T>`) handles all of these natively. Objects and arrays no longer need manual parsing — that was a limitation of the pre-v0.11.0 raw-JSON representation.

## Combining with other configuration sources

`AddWestyx` stacks with the other sources in the builder. The last source wins for duplicate keys — so to let local environment variables override Nexus values (useful during development), add Nexus before environment variables:

```csharp theme={null}
builder.Configuration
    .AddWestyx(nexusConfig)          // Nexus loaded first
    .AddEnvironmentVariables();       // env vars override (last wins)
```

Or add it after to give Nexus higher priority than `appsettings.json` but lower than environment variables:

```csharp theme={null}
// Default ASP.NET Core order: appsettings → appsettings.{env} → env vars
// Insert Nexus after appsettings, before env vars:
builder.Configuration.Sources.Insert(
    builder.Configuration.Sources.Count - 1,
    new NexusConfigurationSource(nexusConfig));
```
