Maxlona.FeatureFlags 1.4.1

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

Maxlona.FeatureFlags for .NET

The official .NET SDK for Maxlona. It covers the complete public feature-flag API:

  • Evaluate boolean, string, number, and JSON variants with targeting context.
  • Request decision explanations and read additional evaluation metadata.
  • List, get, create, edit, archive, and delete feature flags.
  • Configure environment/client state, percentage rollouts, targeting rules, dependencies, variants, and enrollment cutoffs.
  • Use application services, structured exceptions, cancellation tokens, and .NET dependency injection.
  • Evaluate every check live against the Maxlona service, so a decision always reflects the current flag configuration with nothing to invalidate or refresh.
  • Survive an unreliable network: Polly retries with exponential backoff and jitter, a per-attempt timeout, and plain-language firewall/proxy/DNS diagnostics.
  • Keep a local Serilog log of every retry and failure, on by default.

The package targets .NET 8 and is published on NuGet.org as Maxlona.FeatureFlags.

Full integration documentation is available in the Maxlona SDK Wiki.

Package

Current SDK release documented here: Maxlona.FeatureFlags 1.4.1

1.4.1 adds per-flag environment management to the live-evaluation package. Every flag check is evaluated live against the Maxlona service. The opt-in encrypted local cache and its realtime SignalR synchronization, added here in 1.3.0, now ship separately as Maxlona.FeatureFlags.Offline; install that package instead if your application evaluates flags while disconnected. Everything else — evaluation, the Management API, the retry pipeline, connection diagnostics, and logging — is unchanged.

Licensed under the Maxlona Proprietary SDK License, included as LICENSE.txt in the package. Copyright (c) 2026 Maxlona. All rights reserved. This SDK is not open source. The license permits integration with Maxlona and distribution of unmodified SDK binaries as part of integrating applications; see the included license for the full terms. Third-party dependencies retain their own licenses.

Package information Value
Package ID Maxlona.FeatureFlags
Version 1.4.1
NuGet.org https://www.nuget.org/packages/Maxlona.FeatureFlags/1.4.1
Published by Maxlona
Target framework net8.0 — runs on .NET 8, 9, and 10
Documentation https://maxlona.com/wiki/sdk

Dependencies, all pulled in automatically:

Package Minimum version
Microsoft.Extensions.Http 8.0.1
Polly.Core 8.5.2
Serilog 4.2.0
Serilog.Sinks.File 6.0.0

Install

dotnet add package Maxlona.FeatureFlags

A newly published version takes a few minutes to be indexed by NuGet.org. If a version that was just released is not found yet, wait and retry, or run dotnet restore --no-cache to bypass a stale local index. Versions already published are unaffected.

Or pin the version:

dotnet add package Maxlona.FeatureFlags --version 1.4.1
<PackageReference Include="Maxlona.FeatureFlags" Version="1.4.1" />

No custom feed is required: NuGet.org is a default source in a standard NuGet configuration. If your build uses a nuget.config that clears the default sources, restore nuget.org there or point at the internal mirror that proxies it.

Minimal setup

Register the service with a key and environment, then inject IFeatureFlags into your application:

using Maxlona.FeatureFlags;
using Maxlona.FeatureFlags.Models;

builder.Services.AddMaxlonaFeatureFlags(
    builder.Configuration["Maxlona:ManagementKey"]!, "production");

public sealed class Checkout(IFeatureFlags flags)
{
    public Task<bool> UseNewCheckout(string userId) =>
        flags.IsEnabledAsync("checkout-redesign",
            new EvaluationContext { UserId = userId }, defaultValue: false);
}

For administration, register AddMaxlonaFeatureFlagManagement(managementKey) and inject IFeatureFlagManagement. The SDK handles endpoint selection, authentication, transport, retries, and logging. An environment is still required for evaluation because it selects which flag configuration to use.

Evaluate flags

Use a Management Key with Evaluate permission. Scope it to the application, and optionally to one environment. Keep trusted-server keys out of browser-delivered code.

using Maxlona.FeatureFlags;
using Maxlona.FeatureFlags.Models;

builder.Services.AddMaxlonaFeatureFlags(options =>
{
    options.ManagementKey = builder.Configuration["Maxlona:ManagementKey"]!;
    options.Stage = "production";
    options.ClientId = "checkout-api"; // optional
});

// Resolve IFeatureFlags through DI.
var result = await flags.EvaluateAsync(
    "checkout-redesign",
    new EvaluationContext
    {
        UserId = user.Id,
        Attributes = new Dictionary<string, object?>
        {
            ["plan"] = user.Plan,
            ["country"] = user.Country
        }
    },
    explain: true,
    cancellationToken);

bool enabled = await flags.IsEnabledAsync(
    "checkout-redesign",
    new EvaluationContext { UserId = user.Id },
    cancellationToken);

CheckoutSettings? settings = await flags.GetValueAsync<CheckoutSettings>(
    "checkout-settings",
    new EvaluationContext { UserId = user.Id },
    cancellationToken);

For console applications without DI, the SDK owns the transport:

using var flags = new FeatureFlags(managementKey, "production");
bool enabled = await flags.IsEnabledAsync("checkout-redesign",
    new EvaluationContext { UserId = "user-123" }, defaultValue: false);

using var management = new FeatureFlagManagement(managementKey);
var definitions = await management.ListAsync();

Reuse these services for the application lifetime and dispose them at shutdown. DI manages service lifetimes automatically. The existing FeatureFlagClient, FeatureFlagManagementClient, and their interfaces remain available for compatibility. BaseUri is an advanced override for private deployments or tests; ordinary applications never configure an endpoint.

Manage flags

Use the same Management Key format. CanRead keys can list and get; create, update, delete, and configuration operations require CanWrite.

builder.Services.AddMaxlonaFeatureFlagManagement(options =>
{
    options.ManagementKey = builder.Configuration["Maxlona:ManagementKey"]!;
});

var created = await management.CreateAsync(new CreateFeatureFlagRequest
{
    Name = "checkout-redesign",
    Project = "Main App",
    Type = "release",
    Description = "New checkout flow",
    Tags = ["checkout", "q3"],
    Variants =
    [
        FlagVariant.Create("on", true, "Feature enabled"),
        FlagVariant.Create("off", false, "Feature disabled")
    ],
    DependsOn = []
}, cancellationToken);

// Add an organization-level opt-in environment before creating a config there.
var environments = await management.GetEnvironmentsAsync(created.Name, cancellationToken);
await management.UpdateEnvironmentsAsync(created.Name, new UpdateFlagEnvironmentsRequest
{
    Environments = environments.Environments.Concat(["production"]).Distinct().ToArray()
}, cancellationToken);

await management.UpsertConfigurationAsync(created.Name, new UpsertFlagConfigurationRequest
{
    Stage = "production",
    Enabled = true,
    DefaultVariantKey = "off",
    RolloutPercentage = 25,
    Rules =
    [
        new TargetingRule
        {
            Id = "pro-users",
            Priority = 10,
            Conditions = [TargetingCondition.Create("plan", "==", "pro")],
            Allocations = [new VariantAllocation { VariantKey = "on", Percentage = 100 }]
        }
    ]
}, cancellationToken);

Resilience

Every call goes through a Polly pipeline: 3 retries by default with exponential backoff and jitter, plus a per-attempt timeout (10s by default). All of it applies whether the client came from DI or was constructed by hand.

builder.Services.AddMaxlonaFeatureFlags(options =>
{
    options.ManagementKey = builder.Configuration["Maxlona:ManagementKey"]!;
    options.Stage = "production";

    options.Timeout = TimeSpan.FromSeconds(10);
    options.Retry.MaxRetryAttempts = 3;      // 0 disables retries
    options.Retry.BaseDelay = TimeSpan.FromMilliseconds(200);
    options.Retry.MaxDelay = TimeSpan.FromSeconds(5);
    options.Retry.HonorRetryAfter = true;    // a 429 Retry-After header wins, capped by MaxDelay
});

What gets retried depends on whether repeating the request is safe:

Failure Reads, updates, deletes, upserts, evaluations CreateAsync
Connection refused / DNS / host unreachable retried retried (nothing was delivered)
408, 429, 503 retried retried (the server declined it)
500, 502, 504 retried not retried (the write may have landed)
Connection reset mid-flight, timeout retried not retried
400, 401, 403, 404, 409 never retried never retried

Creating the same flag twice is not the same as creating it once, so a create is repeated only when the SDK can prove the request never reached the server.

Degrading gracefully

A feature flag is a configuration lookup, and an outage in a configuration lookup should not take down the feature it configures. Pass a fallback and an unreachable Maxlona stops being an exception:

// Returns false if Maxlona is unreachable or refuses the call. The failure is logged.
bool enabled = await flags.IsEnabledAsync("checkout-redesign", context, defaultValue: false, ct);

CheckoutSettings? settings = await flags.GetValueAsync("checkout-settings", context, Defaults.Checkout, ct);

// Or inspect the failure yourself.
var attempt = await flags.TryEvaluateAsync("checkout-redesign", context, explain: true, ct);
if (!attempt.Succeeded)
    logger.LogWarning(attempt.Error, "Falling back to the default variant.");

The overloads without a fallback still throw, for callers that want to know.

Errors and diagnostics

Exception Meaning
FeatureFlagApiException The API answered and refused. Carries StatusCode, ErrorCode, TraceId, ResponseBody, RetryAfter.
FeatureFlagConnectionException The API could not be reached. Carries Problem, Guidance, Endpoint, Attempts.
JsonException The response body was not the shape the SDK expected.

Corporate networks break SDKs in ways that all look identical from .NET: every one of them arrives as the same opaque HttpRequestException. The SDK classifies them instead and says what to check.

Problem Detected from What the message tells you
DnsFailure SocketError.HostNotFound The host did not resolve; run nslookup and check for a DNS filter.
BlockedByFirewall connection refused, reset, unreachable, or silently dropped Allow egress to the host and port in the host firewall, network ACL, and any NSG or security group; set HTTPS_PROXY if a proxy is in use.
TlsInterception AuthenticationException during the handshake A TLS-inspecting proxy is re-signing traffic; trust its root certificate or bypass the host.
ProxyAuthenticationRequired HTTP 407 A proxy wants credentials. Maxlona itself never returns 407.
InterceptedResponse a non-JSON body on a 2xx A captive portal or web filter answered instead of Maxlona.
Timeout the attempt timeout elapsed Nothing answered, the signature of a firewall that drops rather than refuses.

Check the whole path at startup, without throwing:

var report = await management.CheckConnectivityAsync(ct);
if (!report.IsHealthy)
    logger.LogError("Maxlona unreachable: {Summary} {Guidance}", report.Summary, report.Guidance);

IsHealthy false with a null Problem means the network is fine and the API refused: a revoked key, a scope mismatch, or an inactive subscription. Problem set means the traffic never got there.

Logging

The SDK writes its own Serilog file log, on by default, recording every retry, every classified failure, and its guidance. It never touches Serilog.Log.Logger, so it cannot disturb the host application logging setup.

Logs default to AppContext.BaseDirectory/maxlona-FFlags, beside the running application. Set options.Logging.Directory to an absolute directory or a relative path such as logs/flags. Relative paths resolve from AppContext.BaseDirectory, regardless of the shell's working directory. If the chosen directory is unwritable, file logging is skipped without failing flag operations. Set options.Logging.Enabled = false to disable file logging, or supply your own Serilog logger.

Files roll daily as maxlona-featureflags-<date>.log, keeping 7 of them, capped at 16 MB each.

options.Logging.Enabled = true;                     // default
options.Logging.Directory = @"D:\logs\maxlona";     // override the location
options.Logging.MinimumLevel = LogEventLevel.Warning;
options.Logging.RetainedFileCountLimit = 7;
options.Logging.FileSizeLimitBytes = 16 * 1024 * 1024;

options.Logging.Logger = Log.Logger;                // or fold into your own Serilog logger

Logging is best-effort by design: if the folder cannot be created or the file cannot be opened, the SDK degrades to writing nothing rather than failing the call. Losing a log line must never cost a flag evaluation.

Credential permissions

Every SDK request uses x-management-key. Grant only the required permissions:

  • CanEvaluate: resolve flag values through POST /flags/{name}; application scope is required and environment scope is optional.
  • CanRead: list and retrieve flag definitions through the Management API.
  • CanWrite: create, edit, configure, archive, and delete flags; read access is included.

The key is mandatory. The SDK validates it when the client is constructed, including at DI registration, so a missing, blank, or unsendable key fails at startup rather than as a puzzling 401 on the first user request. A key carrying whitespace or a non-ASCII look-alike character (the usual result of copying one out of a rich-text document) is rejected explicitly, because HttpClient would otherwise drop the header and send the request with no credential at all.

Keep the key in a secret store or environment variable. Do not commit it.

Coverage

The SDK supports the following flag evaluation and management operations:

Endpoint SDK method Permission
POST /flags/{key} EvaluateAsync, TryEvaluateAsync, IsEnabledAsync, GetValueAsync CanEvaluate
GET /api/flags ListAsync CanRead
GET /api/flags/{name} GetAsync, TryGetAsync, ExistsAsync CanRead
GET /api/flags/{name}/environments GetEnvironmentsAsync CanRead
POST /api/flags CreateAsync CanWrite
PUT /api/flags/{name} UpdateAsync, ArchiveAsync, RestoreAsync CanWrite
PUT /api/flags/{name}/environments UpdateEnvironmentsAsync CanWrite
DELETE /api/flags/{name} DeleteAsync CanWrite
POST /api/flags/{name}/configs UpsertConfigurationAsync CanWrite

Use the Maxlona console to manage organisations, billing, users, groups, segments, webhooks, experiments, and approvals. These operations are not available through this SDK.

For organization environments configured as opt-in, call UpdateEnvironmentsAsync before creating the flag's configuration in that environment. Replacing the environment set can remove disabled configurations and can return a conflict when an enabled configuration, automation, approval policy, or dependency blocks the change.

Product Compatible and additional computed target framework versions.
.NET 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. 
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.4.1 95 9/14/2026
1.4.0 92 9/12/2026
1.3.1 106 9/9/2026
1.3.0 102 9/9/2026
1.2.1 101 9/5/2026
1.2.0 103 9/5/2026
1.1.0 102 9/3/2026
1.0.0 98 9/3/2026

1.4.1 adds per-flag environment fields and Management API operations for reading and replacing a flag's resolved environment set, including dependency conflict resolution.