Flagpool.Sdk 0.3.1

dotnet add package Flagpool.Sdk --version 0.3.1
                    
NuGet\Install-Package Flagpool.Sdk -Version 0.3.1
                    
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="Flagpool.Sdk" Version="0.3.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Flagpool.Sdk" Version="0.3.1" />
                    
Directory.Packages.props
<PackageReference Include="Flagpool.Sdk" />
                    
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 Flagpool.Sdk --version 0.3.1
                    
#r "nuget: Flagpool.Sdk, 0.3.1"
                    
#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 Flagpool.Sdk@0.3.1
                    
#: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=Flagpool.Sdk&version=0.3.1
                    
Install as a Cake Addin
#tool nuget:?package=Flagpool.Sdk&version=0.3.1
                    
Install as a Cake Tool

Flagpool C# SDK

Official C# SDK for Flagpool — feature flags with local evaluation, deterministic rollouts, encrypted target lists, and analytics.

NuGet

Installation

dotnet add package Flagpool.Sdk

Requirements: .NET 6.0 or later

Quick Start

using Flagpool.Sdk;

var client = new FlagpoolClient(new FlagpoolClientOptions
{
    ProjectId = "your-project-id",
    ApiKey = "env_production_xxx",
    DecryptionKey = "tlk_xxx",
    Context = new Dictionary<string, object?>
    {
        ["userId"] = "user-123",
        ["email"] = "alice@example.com",
        ["plan"] = "pro"
    }
});

await client.InitAsync();

if (client.IsEnabled("new-dashboard"))
{
    // Feature is enabled for this user
}

var theme = client.GetValue("theme-color"); // "blue", "dark", etc.

client.Dispose();

Configuration

var client = new FlagpoolClient(new FlagpoolClientOptions
{
    // Required
    ProjectId = "your-project-id",
    ApiKey = "env_production_xxx",
    DecryptionKey = "tlk_xxx",

    // Optional
    Context = new Dictionary<string, object?> { ["userId"] = "user-123" },
    PollingIntervalMs = 30000,      // Polling interval (default: 30000ms)
    Streaming = true,               // Enable polling for real-time updates
    UrlOverride = null,             // Override CDN URL (for testing)

    // Analytics (opt-in)
    Analytics = new AnalyticsConfig
    {
        Enabled = true,
        FlushIntervalMs = 60000,    // Min: 30000ms
        FlushThreshold = 100,
        SampleRate = 1.0            // 0.0–1.0
    }
});

Evaluation Methods

IsEnabled(key, defaultValue?)

Returns true if the flag evaluates to a boolean true. Returns defaultValue (default: false) if the flag doesn't exist or isn't a boolean.

bool enabled = client.IsEnabled("feature-x");
bool fallback = client.IsEnabled("missing-flag", false); // false

GetValue(key)

Returns the evaluated variation value. Returns null if the flag doesn't exist. Never throws.

object? value = client.GetValue("theme");
// Could be: true, false, "dark", 42, null, etc.

GetVariation(key)

Alias for GetValue.

GetAllFlags()

Returns all flag values as a dictionary.

Dictionary<string, object?> flags = client.GetAllFlags();

Context

Set user context at initialization or update it later:

// At init
var client = new FlagpoolClient(new FlagpoolClientOptions
{
    // ...
    Context = new Dictionary<string, object?>
    {
        ["userId"] = "user-123",
        ["email"] = "alice@example.com",
        ["plan"] = "enterprise"
    }
});

// Update later (merges with existing context)
client.UpdateContext(new Dictionary<string, object?>
{
    ["plan"] = "free"  // Overwrites "enterprise"
});

// Read current context
var ctx = client.GetContext();

Target Lists

Target lists let you target specific users by attribute values (e.g., beta testers, VIP users).

Plaintext Target Lists

Work automatically — no extra configuration needed.

Encrypted Target Lists

If encryption is enabled for your environment, you need a crypto adapter:

using System.Security.Cryptography;
using System.Text;

public class AesCryptoAdapter : ICryptoAdapter
{
    public Task<string> DecryptAsync(string ciphertext, string key, string iv, string tag)
    {
        // Derive key with PBKDF2-SHA256 (100,000 iterations)
        var salt = Encoding.UTF8.GetBytes(key.PadRight(16, '0')[..16]);
        using var kdf = new Rfc2898DeriveBytes(key, salt, 100000, HashAlgorithmName.SHA256);
        var derivedKey = kdf.GetBytes(32);

        // Decrypt with AES-256-GCM
        using var aes = new AesGcm(derivedKey, 16);
        var ivBytes = Convert.FromBase64String(iv);
        var cipherBytes = Convert.FromBase64String(ciphertext);
        var tagBytes = Convert.FromBase64String(tag);
        var plainBytes = new byte[cipherBytes.Length];

        aes.Decrypt(ivBytes, cipherBytes, tagBytes, plainBytes);
        return Task.FromResult(Encoding.UTF8.GetString(plainBytes));
    }

    public Task<EncryptResult>? EncryptAsync(string plaintext, string key) => null;
}

// Register before InitAsync
FlagpoolCrypto.SetCryptoAdapter(new AesCryptoAdapter());

var client = new FlagpoolClient(options);
await client.InitAsync();

Error: "Encrypted target lists received but no crypto adapter configured"

This warning appears when your environment has encryption enabled but no ICryptoAdapter is registered. Flags without target list rules will still evaluate correctly. Only inTargetList / notInTargetList rules are affected.

To fix: Register a crypto adapter as shown above before calling InitAsync().

Analytics

Opt-in evaluation analytics — fire-and-forget, never blocks flag evaluation.

var client = new FlagpoolClient(new FlagpoolClientOptions
{
    // ...
    Analytics = new AnalyticsConfig
    {
        Enabled = true,
        FlushIntervalMs = 60000,    // Flush every 60s (minimum: 30s)
        FlushThreshold = 100,       // Flush when buffer reaches 100 hits
        SampleRate = 1.0            // Track 100% of evaluations
    }
});
// Check analytics state
var state = client.GetAnalyticsState();
Console.WriteLine($"Buffer: {state?.BufferSize}, Flushed: {state?.TotalFlushes}");

// Manual flush
client.FlushAnalytics();

Analytics are automatically flushed on process exit.

Real-Time Updates (Polling)

Enable polling to automatically refresh flags:

var client = new FlagpoolClient(new FlagpoolClientOptions
{
    // ...
    Streaming = true,
    PollingIntervalMs = 30000  // Every 30 seconds
});

await client.InitAsync();

// Listen for changes
var unsubscribe = client.OnChange((flagKey, newValue) =>
{
    Console.WriteLine($"Flag changed: {flagKey} = {newValue}");
});

// Later: stop listening
unsubscribe();

Debugging

// Get all flags with their evaluation state
var flagStates = client.GetAllFlagsWithState();
foreach (var (key, state) in flagStates)
{
    Console.WriteLine($"{key}: value={state.Value}, evaluated={state.Evaluated}");
}

ASP.NET Core Integration

Register as a singleton service:

// Program.cs
var flagpool = new FlagpoolClient(new FlagpoolClientOptions
{
    ProjectId = builder.Configuration["Flagpool:ProjectId"]!,
    ApiKey = builder.Configuration["Flagpool:ApiKey"]!,
    DecryptionKey = builder.Configuration["Flagpool:DecryptionKey"]!,
    Streaming = true
});
await flagpool.InitAsync();

builder.Services.AddSingleton(flagpool);
// In a controller or service
public class FeatureController : ControllerBase
{
    private readonly FlagpoolClient _flags;

    public FeatureController(FlagpoolClient flags) => _flags = flags;

    [HttpGet("dashboard")]
    public IActionResult GetDashboard()
    {
        // Update context per-request
        _flags.UpdateContext(new Dictionary<string, object?>
        {
            ["userId"] = User.FindFirst("sub")?.Value
        });

        if (_flags.IsEnabled("new-dashboard"))
            return Ok(new { version = "v2" });

        return Ok(new { version = "v1" });
    }
}

Lifecycle

// Stop polling (keeps analytics running)
client.Close();

// Full teardown (stops polling + flushes analytics)
client.Dispose();

// Or use 'using' statement
using var client = new FlagpoolClient(options);
await client.InitAsync();
// Automatically disposed at end of scope

Missing Flags

Accessing a flag that doesn't exist is safe and never throws:

client.IsEnabled("nonexistent");   // false
client.GetValue("nonexistent");    // null

Supported Platforms

Framework Status
.NET 6.0 ✅ Supported
.NET 8.0 ✅ Supported
.NET 10.0 ✅ Supported

License

MIT — see LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net6.0

    • No dependencies.
  • 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
0.3.1 162 3/16/2026
0.2.0 125 3/16/2026
0.1.1 127 3/16/2026