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

# .NET SDK - SSE Live Updates

> How SSE live propagation works in the .NET SDK, including 429 handling.

The SDK can hold a persistent `GET /v1/stream` connection open in a background `Task`. Whenever the Nexus backend emits a change event (`config.updated`, `flag.toggled`, `secret.created`, `resync`, ...), the SDK triggers an immediate full sync - so the cache is updated within milliseconds of the change, instead of waiting for the next TTL tick.

## Enabling it

Call `ConnectStream()` after the client is created (or rely on your DI factory to do it):

```csharp theme={null}
await using var client = await NexusClient.CreateAsync(config);
client.ConnectStream();
```

`ConnectStream()` is idempotent. Calling it twice is a no-op.

## What happens under the hood

1. The SDK opens an HTTP streaming response to `<BaseUrl>/v1/stream` with `Accept: text/event-stream` and the `X-Nexus-API-Key` header.
2. The server immediately sends a `resync` event, which causes the SDK to call `SyncAsync()` once to align with the current state.
3. The connection stays open. Every change event triggers another full sync.
4. While the stream is up, `IsStreamConnected` reports `true` and the effective polling TTL is bumped to 60 s as a safety net.

## Reconnection

Any transport error (server restart, TCP drop, network blip) triggers a reconnect with **exponential backoff**: 1s - 2s - 4s - 8s - 16s - 30s (capped at 30s).

After **`MaxStreamErrors` consecutive transport errors** the SDK does not give up permanently. Instead, it sleeps for the next interval in the `SseReconnectCooldown` schedule and then retries SSE automatically.

```
nexus: SSE transport error (attempt 1/3)
nexus: SSE transport error (attempt 2/3)
nexus: SSE transport error (attempt 3/3)
nexus: SSE 3 consecutive transport errors - sleeping reconnect schedule, will retry SSE
```

The reconnect schedule defaults to `[5, 10, 20, 40, 60]` minutes. After the cooldown elapses the SDK resets the transport-error counter and reconnects. Configure the schedule via `SseReconnectCooldown` in `NexusConfig` - see [Configuration](configuration).

## `429 Too Many Requests`

Every project tier has a per-service stream connection limit:

| Tier   | Connections |
| ------ | ----------- |
| `free` | 5           |
| `xs`   | 10          |
| `s`    | 25          |
| `m`    | 50          |
| `l`    | 150         |
| `xl`   | 500         |

### Behaviour (v0.6.0+)

| Server response                                                     | SDK behaviour                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `429` **with** parseable `Retry-After` (delta-seconds or HTTP-date) | Parse the header, clamp the wait to `[5 s, 5 min]`, fire `OnFallback(FallbackReason.RateLimited)`, `await Task.Delay(delay)`, then **reconnect automatically**. The episode does **not** increment the transport-failure counter.                                        |
| `429` **without** parseable `Retry-After`                           | Fire `OnFallback(FallbackReason.RateLimited)`, then schedules a reconnect using the `SseReconnectCooldown` schedule (default 5 - 10 - 20 - 40 - 60 minutes). TTL polling is used during the cooldown window, and SSE reconnects automatically when the interval elapses. |

`Retry-After` parsing uses `RetryConditionHeaderValue.Delta` with a `Date - DateTimeOffset.UtcNow` fallback for the HTTP-date form.

The `[5 s, 5 min]` clamp is deliberate:

* **5 s floor** - protects the SDK against a hostile or misconfigured server advertising `Retry-After: 0`.
* **5 min ceiling** - caps the indefinite-stall risk.

```csharp theme={null}
// Observer flow on the SDK side
public sealed class RateLimitObserver : NoopStreamObserver
{
    public override void OnFallback(string reason)
    {
        if (reason == FallbackReason.RateLimited)
        {
            // v0.6.0+: the SDK always reconnects automatically (via Retry-After
            // or SseReconnectCooldown schedule). No manual re-launch needed.
            _metrics.IncrementRateLimited();
        }
    }

    public override void OnConnected() => _metrics.SetStreamUp(true);
    public override void OnDisconnected(Exception? _) => _metrics.SetStreamUp(false);
}
```

## Disabling SSE

Just don't call `ConnectStream()`. The SDK then runs in pure TTL-polling mode.

You can also disconnect at runtime:

```csharp theme={null}
client.DisconnectStream();
```

## Lifecycle

The stream is bound to the client. `Dispose()` / `DisposeAsync()` stops the background task before releasing the underlying HTTP client - always dispose the client (or use `await using`) on shutdown.

```csharp theme={null}
public class CleanShutdown(NexusClient nexus) : IHostedService
{
    public Task StartAsync(CancellationToken ct)
    {
        nexus.ConnectStream();
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken ct)
    {
        nexus.DisconnectStream();
        return Task.CompletedTask;
    }
}
```

## What events trigger a sync

| Event                                                 | secret keys | public keys          |
| ----------------------------------------------------- | ----------- | -------------------- |
| `resync`                                              | yes         | yes                  |
| `config.created` / `.updated` / `.deleted`            | yes         | yes                  |
| `flag.created` / `.updated` / `.toggled` / `.deleted` | yes         | yes                  |
| `secret.created` / `.updated` / `.deleted`            | yes         | filtered server-side |

## IConfiguration live reload

When the SDK is wired into ASP.NET Core via `AddWestyx(...)`, the `NexusConfigurationProvider` calls `IConfiguration.Reload()` automatically after every sync that returns new data. This means:

* `IOptionsMonitor<T>.CurrentValue` is immediately updated.
* `IOptionsMonitor<T>.OnChange(...)` callbacks fire without any manual intervention.
* No application restart is required.

The reload fires only when new data arrives (HTTP 200 with a changed payload). A 304 Not Modified response does not trigger a reload.

See [ASP.NET Core configuration](aspnetcore-configuration) for the full setup guide.
