FeatCtrl 1.0.0

dotnet add package FeatCtrl --version 1.0.0
                    
NuGet\Install-Package FeatCtrl -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FeatCtrl" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FeatCtrl" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="FeatCtrl" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FeatCtrl --version 1.0.0
                    
#r "nuget: FeatCtrl, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FeatCtrl@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FeatCtrl&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=FeatCtrl&version=1.0.0
                    
Install as a Cake Tool

CI NuGet

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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