FeatCtrl 1.0.0
dotnet add package FeatCtrl --version 1.0.0
NuGet\Install-Package FeatCtrl -Version 1.0.0
<PackageReference Include="FeatCtrl" Version="1.0.0" />
<PackageVersion Include="FeatCtrl" Version="1.0.0" />
<PackageReference Include="FeatCtrl" />
paket add FeatCtrl --version 1.0.0
#r "nuget: FeatCtrl, 1.0.0"
#:package FeatCtrl@1.0.0
#addin nuget:?package=FeatCtrl&version=1.0.0
#tool nuget:?package=FeatCtrl&version=1.0.0
Based on the common specifications FeatCtrl SDK Specifications.
Server-side C# SDK for FeatCtrl — feature flag management.
This package provides an SSE client that connects to https://sdk.featctrl.com and keeps an in-memory flag store up to date in real time. It targets .NET 8 and has no external runtime dependencies beyond the .NET base class library.
Installation
dotnet add package FeatCtrl
Requires .NET 8 or later.
Quick start
using FeatCtrl;
// Build a client from environment variables.
// Requires FEATCTRL_SDK_KEY to be set.
var client = FeatCtrlClient.FromEnvironment();
// Start the SSE connection in the background.
client.Connect();
// Wait until the first flag snapshot has been received.
await client.ReadyAsync();
// Evaluate a flag (returns the default value when the flag is absent).
if (client.Flags.IsEnabled("new-checkout", defaultValue: false))
{
// render new checkout flow
}
// Clean up when your application shuts down.
await client.DisconnectAsync();
Environment variables used by FromEnvironment():
| Variable | Required | Default | Description |
|---|---|---|---|
FEATCTRL_SDK_KEY |
✅ yes | — | SDK key issued by FeatCtrl |
FEATCTRL_URL |
no | https://sdk.featctrl.com |
Override the FeatCtrl backend URL |
FEATCTRL_MODE |
no | livestreaming |
livestreaming (persistent SSE) or snapshot (connect once, then disconnect) |
FEATCTRL_HEARTBEAT_WATCHDOG_SECS |
no | 120 |
Seconds without a heartbeat before the client reconnects automatically. Must be > 0. |
FEATCTRL_MAX_RETRIES |
no | unlimited | Maximum reconnect attempts in degraded mode. |
FEATCTRL_BACKOFF_BASE_SECS |
no | 3 |
Initial backoff delay in seconds. |
FEATCTRL_BACKOFF_MAX_SECS |
no | 30 |
Maximum backoff delay in seconds. |
Manual construction
You can also create a client programmatically instead of reading from environment variables:
var client = new FeatCtrlClient(
sdkKey: "my-sdk-key",
options: new FeatCtrlOptions
{
BaseUrl = "https://sdk.featctrl.com",
Mode = ConnectionMode.Livestreaming,
HeartbeatWatchdogSeconds = 120,
MaxRetries = null, // unlimited
BackoffBaseSeconds = 3,
BackoffMaxSeconds = 30,
});
Lifecycle hooks
Register event handlers on the client to react to connection events and flag changes.
client.Connected += (_, e) =>
Console.WriteLine($"[FeatCtrl] connected conn={e.ConnectionUuid} inst={e.InstanceUuid}");
client.Disconnected += (_, _) =>
Console.WriteLine("[FeatCtrl] disconnected");
client.SnapshotReceived += (_, e) =>
Console.WriteLine($"[FeatCtrl] snapshot — {e.Flags.Count} flag(s)");
client.FlagChanged += (_, e) =>
Console.WriteLine($"[FeatCtrl] flag updated: {e.Flag.Key} = {e.Flag.Enabled}");
client.FlagDeleted += (_, e) =>
Console.WriteLine($"[FeatCtrl] flag deleted: {e.Key}");
client.WatchdogTimeout += (_, _) =>
Console.Error.WriteLine("[FeatCtrl] heartbeat watchdog timed out — reconnecting");
client.Forbidden += (_, _) =>
Console.Error.WriteLine("[FeatCtrl] 403 Forbidden — SDK key rejected, retries disabled");
// On application shutdown:
await client.DisconnectAsync();
Waiting for the first snapshot
FeatCtrlClient exposes two readiness APIs:
| API | Type | Description |
|---|---|---|
client.IsReady |
bool |
true once the first flags.snapshot has been received. Never resets to false. |
client.ReadyAsync() |
Task |
Resolves as soon as the first snapshot arrives. Resolves immediately (next await) if already ready. |
await pattern — useful at application startup to block until flags are available:
client.Connect();
await client.ReadyAsync();
// FlagStore is now populated with the initial snapshot.
bool enabled = client.Flags.IsEnabled("new-checkout", defaultValue: false);
Boolean guard — useful for synchronous checks:
if (client.IsReady)
{
// flags are available right now
}
Connection modes
Livestreaming (default)
The client maintains a persistent SSE connection and keeps its flag cache up to date in real time.
var client = new FeatCtrlClient("my-sdk-key");
// or: FEATCTRL_MODE=livestreaming
Snapshot
The client connects once to receive the initial flags.snapshot, then immediately calls DELETE /disconnect and closes the stream. Suitable for short-lived processes, batch jobs, or AWS Lambda.
var options = new FeatCtrlOptions { Mode = ConnectionMode.Snapshot };
var client = new FeatCtrlClient("my-sdk-key", options);
// or: FEATCTRL_MODE=snapshot
API reference
FeatCtrlClient
| Member | Description |
|---|---|
FeatCtrlClient(sdkKey, options?, httpMessageHandler?) |
Construct a client. httpMessageHandler is useful for testing. |
FeatCtrlClient.FromEnvironment() |
Build a client from environment variables. |
void Connect() |
Start the background SSE connection loop (non-blocking). |
Task ReadyAsync(CancellationToken) |
Wait for the first flag snapshot. |
Task DisconnectAsync(CancellationToken) |
Gracefully disconnect (sends DELETE /disconnect). |
ValueTask DisposeAsync() |
Calls DisconnectAsync and releases resources. |
bool IsReady |
true once the first snapshot has been received. |
FlagStore Flags |
In-memory flag cache. |
event Connected |
SSE connection established. |
event Disconnected |
Client disconnected. |
event SnapshotReceived |
Full flag snapshot received. |
event FlagChanged |
A flag was created or updated. |
event FlagDeleted |
A flag was deleted. |
event WatchdogTimeout |
Heartbeat watchdog expired — the client is reconnecting. |
event Forbidden |
Server returned 403 — SDK key rejected, retries permanently disabled. |
FlagStore
| Member | Description |
|---|---|
bool IsEnabled(string key, bool defaultValue = false) |
Returns the flag value, or defaultValue when the flag is absent. |
IReadOnlyDictionary<string, FeatCtrlFlag> All |
Read-only snapshot of the entire flag cache. |
FeatCtrlFlag
public sealed record FeatCtrlFlag
{
public required string Key { get; init; }
public required string Name { get; init; }
public required string FlagType { get; init; } // always "boolean" in this version
public required bool Enabled { get; init; }
}
Dependency injection (.NET Generic Host)
FeatCtrlClient implements IAsyncDisposable and integrates naturally with IHostedService or background workers. A minimal setup:
// Program.cs
builder.Services.AddSingleton(_ =>
{
var client = FeatCtrlClient.FromEnvironment();
client.Connect();
return client;
});
// In any service:
public class MyService(FeatCtrlClient featCtrl)
{
public async Task DoWorkAsync()
{
await featCtrl.ReadyAsync();
if (featCtrl.Flags.IsEnabled("my-feature"))
// ...
}
}
Building from source
git clone https://github.com/featctrl/sdk-c-sharp.git
cd sdk-c-sharp
dotnet build FeatCtrl.sln
dotnet test FeatCtrl.sln
Running the sample console
export FEATCTRL_SDK_KEY=your-sdk-key
dotnet run --project samples/FeatCtrl.SampleConsole
Releasing
Releases are created by pushing a version tag. The Pack NuGet workflow builds, tests, packs and publishes to NuGet.org automatically.
git tag v1.0.0
git push origin v1.0.0
The NUGET_API_KEY secret must be configured in the repository settings.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- System.Text.Json (>= 8.0.5)
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 117 | 7/14/2026 |