Flaggi 1.0.0
dotnet add package Flaggi --version 1.0.0
NuGet\Install-Package Flaggi -Version 1.0.0
<PackageReference Include="Flaggi" Version="1.0.0" />
<PackageVersion Include="Flaggi" Version="1.0.0" />
<PackageReference Include="Flaggi" />
paket add Flaggi --version 1.0.0
#r "nuget: Flaggi, 1.0.0"
#:package Flaggi@1.0.0
#addin nuget:?package=Flaggi&version=1.0.0
#tool nuget:?package=Flaggi&version=1.0.0
<p align="center"> <img src="docs/flaggi.png" alt="Flaggi Logo" width="200"/> </p>
<h1 align="center">π© Flaggi Β· Feature Flags for .NET</h1>
<p align="center"> <a href="https://www.nuget.org/packages/Flaggi"> <img src="https://img.shields.io/nuget/v/Flaggi.svg" alt="NuGet"> </a> <a href="https://www.nuget.org/packages/Flaggi"> <img src="https://img.shields.io/nuget/dt/Flaggi.svg" alt="NuGet Downloads"> </a> <a href="LICENSE"> <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"> </a> </p>
Flaggi is a pure and extensible feature flag engine for .NET. It aims to make feature management simple, predictable and self-contained.
Unlike many solutions that require external services or heavy dependencies, Flaggi runs entirely in your application. This repository contains the core engine, evaluators, stores and optional extension packages for configuration, dependency injection and Blazor integration.
β¨ Why Flaggi?
Modern feature management solutions often rely on hosted services or require pulling in large SDKs. Flaggi was designed from the ground up to be minimal, fast and unopinionated, so you only pay for what you use.
Key benefits
- Pure and dependency-free β no external dependencies. Works in any .NET project (console, desktop, cloud, Unity, Blazor, games).
- Self-hosted β evaluations happen locally using your own data. No network calls or external services.
- Predictable and deterministic β percentage rollout is based on deterministic hashing.
- Extensible by design β plug in your own rule evaluators and stores (EF Core, Redis, Azure App Configuration, etc.).
- Composite stores β combine multiple stores (in-memory, config, JSON, remote) in order of precedence.
- Cross-platform β .NET 6/7/8/9, Unity, Blazor Server/WASM. Works on Windows, Linux and macOS.
- Test friendly β includes an in-memory store for unit testing and overrides.
- MIT licensed β free to use in both open source and commercial projects.
π― Motivation
Flaggi exists because many teams want feature toggling without heavy SaaS or proprietary systems. Other libraries often require a server, a cloud dashboard or come with embedded A/B testing logic.
Flaggiβs goal is to provide a thin, composable layer you can adopt gradually:
- Start with a simple in-memory store for development.
- Add configuration files, DI helpers or Azure integration later.
Keeping the core small and extensible gives you full control over how and where your flags are stored and evaluated.
π οΈ How it works
Flaggi separates feature management into three concepts:
- Features β each feature has a unique key, an
IsEnabledmaster switch, and a list of rules. - Rules β reference a rule evaluator by name and parameters.
- Context β represents the user/environment (e.g. UserId, Groups, region, tenant, etc.).
Evaluation flow
Client asks the Feature Manager if a feature is enabled for a given context.
Manager retrieves the feature definition from the store (in-memory, file, config, remote, composite).
Engine processes the feature:
- If
IsEnabledisfalseor expired β returnsfalse. - Otherwise, iterates rules. If any rule evaluator returns
falseβ feature disabled. - If all rules return
trueβ feature enabled.
- If
π Built-in Evaluators
Percentage β rollout to X% of users.
new FeatureRule("Percentage", new { value = 20 }); // Enabled for 20% of usersTargeting β enable for specific users or groups, with optional fallback percentage.
new FeatureRule("Targeting", new { users = new[]{"admin@example.com"}, defaultRolloutPercentage = 10 }); // Enabled for admin + 10% of everyone elseSchedule β enable feature in a time window (ISO 8601).
new FeatureRule("Schedule", new { from = "2025-09-01T00:00:00Z", to = "2025-09-30T23:59:59Z" }); // Enabled only in September 2025
π You can also create custom evaluators by implementing IRuleEvaluator and registering them with the engine or via DI.
π¦ Packages
Flaggi is split into focused extension packages so you only add what you actually need. All are available on NuGet.
β οΈ Each extension package is optional.
Install only what you need.
The Core does not reference Microsoft.Extensions.*, so you can use it in isolated contexts (Unity, games, libraries) without pulling in ASP.NET Core dependencies.
π‘ With this modular setup:
- Start with Core in tests or console apps.
- Add Configuration to bind flags from config files.
- Add DependencyInjection for ASP.NET Core/Blazor apps.
- Add Blazor if you want UI components driven by flags.
π Quick Start (Core)
The core library exposes only the primitives β you manage your own store and rule registration. Hereβs how to get started without DI or extensions:
// 1. Define your features
var features = new[]
{
new Feature(
key: "WelcomeBanner",
isEnabled: true,
rules: new[]
{
new FeatureRule(
ruleName: "Targeting",
parameters: new Dictionary<string, object?>
{
["users"] = new [] { "vip@example.com" },
["defaultRolloutPercentage"] = 50
}
)
}
),
new Feature(
key: "ExperimentalCheckout",
isEnabled: false // master switch, always disabled
)
};
// 2. Create a store (in-memory) and the engine
var store = new InMemoryFeatureStore(features);
var engine = new FeatureEngine(new IRuleEvaluator[]
{
new PercentageRuleEvaluator(),
new TargetingRuleEvaluator(),
new ScheduleRuleEvaluator()
});
var manager = new FeatureManager(store, engine);
// 3. Create a context and check the feature
var ctx = new FeatureContext(
userId: "alice@example.com",
groups: new[] { "BetaTesters" },
properties: new Dictionary<string, object?> { ["region"] = "BR" }
);
bool isBannerEnabled = await manager.IsEnabledAsync("WelcomeBanner", ctx);
// isBannerEnabled β true or false depending on hash and rules
π Reading from a JSON file
For production deployments, you may want to load flags from a static file.
Use the built-in JsonFileFeatureStore to read from flaggi.json or appsettings.json.
flaggi.json
{
"Features": [
{
"Key": "DarkMode",
"IsEnabled": true,
"Rules": [
{ "RuleName": "Percentage", "Parameters": { "value": 30 } }
]
}
]
}
Usage
var store = new JsonFileFeatureStore("flaggi.json");
// reuse the same engine and manager
bool darkMode = await manager.IsEnabledAsync("DarkMode", ctx);
π§© Composing multiple stores
Combine multiple stores by priority using the CompositeFeatureStore.
For example, override in-memory β fallback to config β then JSON:
var composite = new CompositeFeatureStore(new[]
{
new InMemoryFeatureStore(new []
{
new Feature("KillSwitch", isEnabled: false) // local override
}),
new ConfigurationFeatureStore(builder.Configuration),
new JsonFileFeatureStore("flaggi.json")
});
var manager = new FeatureManager(composite, engine);
π§© Using Flaggi with Dependency Injection
When building ASP.NET Core or Blazor apps, you typically rely on DI.
The Flaggi.Extensions.DependencyInjection package provides fluent APIs to register and configure services.
Basic setup
builder.Services
.AddFlaggi()
.AddDefaultEvaluators()
.AddFromConfiguration(builder.Configuration);
This registers:
IFeatureStoreβ composite store usingIConfiguration("Features")IFeatureEvaluatorβ default evaluatorsIFeatureManagerβ high-level API
π§ Override features in memory
Use AddFeatures to add local overrides.
Overrides are evaluated last in a composite store, giving them highest priority.
builder.Services
.AddFlaggi()
.AddDefaultEvaluators()
.AddFeatures(new[] { new Feature("KillSwitch", isEnabled: true) })
.AddFromConfiguration(builder.Configuration);
π οΈ Register custom evaluators
public sealed class TenantRuleEvaluator : IRuleEvaluator
{
public string Name => "Tenant";
public bool Evaluate(FeatureRule rule, FeatureContext ctx, string featureKey)
{
var requiredTenant = rule.Parameters["id"]?.ToString();
return ctx.Properties.TryGetValue("tenantId", out var tenant) && tenant?.ToString() == requiredTenant;
}
}
Registering the evaluator
builder.Services
.AddFlaggi()
.AddEvaluator<TenantRuleEvaluator>()
.AddDefaultEvaluators()
.AddFromConfiguration(builder.Configuration);
Boa! Mantive o estilo uniforme do seu README, deixando essa parte de Blazor Integration bem organizada com headings, exemplos em cΓ³digo e listas para boas prΓ‘ticas. Veja como ficou:
πΈοΈ Blazor Integration
The Flaggi.Extensions.BlazorWasm package simplifies feature gating in Blazor WebAssembly.
It provides:
HttpFeatureStoreβ fetches flags from a JSON endpoint or static file.<FeatureGate>component β conditionally renders UI based on feature evaluation.
βοΈ Setup in Blazor WASM
builder.Services
.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) })
.AddFlaggi()
.AddDefaultEvaluators()
.AddFromJsonFile("flaggi.json");
π§© Using the <FeatureGate> component
@using Flaggi.Extensions.BlazorWasm
<FeatureGate Feature="Dashboard.V2" Context="@ctx">
<DashboardNew />
<Fallback>
<DashboardOld />
</Fallback>
</FeatureGate>
@code {
private FeatureContext ctx = new("user1", new[] { "BetaTesters" }, new Dictionary<string, object?>());
}
π The <FeatureGate> component will render <DashboardNew /> if the feature is enabled, or the <Fallback> content when it is not.
π₯οΈ Blazor Server
For Blazor Server, reuse the same API by injecting IFeatureManager and evaluating features directly in your component or page.
π― Scenarios & Best Practices
Flaggi can be used to implement a variety of feature-management patterns:
Beta testing β allow specific users or groups to try new features before general rollout using the Targeting rule.
Gradual rollout β slowly increase the Percentage value over time to mitigate risk and monitor impact.
Kill switch β define a global kill switch. Set
IsEnabled = falseto immediately disable a feature across all users.- In ASP.NET, add a middleware that checks a kill switch and returns 503 Service Unavailable to protect endpoints.
Scheduled releases β enable a feature only during a promotional period or event using the Schedule rule.
Multi-tenant apps β attach tenant info to the
FeatureContextand create custom evaluators (Tenant, Region) to enable features per tenant or region.Environment flags β load different config files or use environment variables to enable features only in dev/staging.
A/B experiments β while Flaggi isnβt a full experimentation platform, you can implement a custom evaluator that buckets users into variants based on a hash.
π± Future Ideas
Flaggi is intentionally small, but there are many areas for growth. Some ideas for future extensions include:
- Expression evaluator β support rules based on logical expressions (e.g. JSON Logic, NCalc, dynamic LINQ).
- Time of day / day of week β enable features only during working hours or weekends.
- Region and locale β automatically enable features based on userβs country or language.
- Remote management UI β build an admin UI to toggle flags at runtime, possibly with audit logging.
- Distributed stores β add providers for SQL Server, Redis, Cosmos DB or gRPC to share flags across instances.
- Automated rollout β integrate with metrics to automatically increase rollout percentage if error rates remain low.
π‘ Contributions are welcome! Feel free to open issues or pull requests with new ideas.
β Testing
Flaggi was designed with testability in mind.
Use the InMemoryFeatureStore or create a fake store for your tests, and register only the evaluators you need.
var features = new[]
{
new Feature("NewUI", true, new[]
{
new FeatureRule("Percentage", new Dictionary<string, object?> { ["value"] = 100 })
})
};
var store = new InMemoryFeatureStore(features);
var engine = new FeatureEngine(new [] { new PercentageRuleEvaluator() });
var manager = new FeatureManager(store, engine);
var ctx = new FeatureContext("test", Array.Empty<string>(), new Dictionary<string, object?>());
Assert.True(await manager.IsEnabledAsync("NewUI", ctx));
You can also mock IFeatureStore and IRuleEvaluator to test specific conditions without evaluating real rules.
π Contributing
We welcome contributions! If you have an idea for a new evaluator, store or integration, open an issue or submit a pull request.
Please include:
- β Tests
- π Documentation
- π‘ Clear description of the feature
See the existing codebase for reference implementations.
π‘οΈ License & Authorship
Flaggi is licensed under the MIT License.
π€ Created by Iury Ferreira
β€οΈ Thanks
Thank you for using Flaggi!
We hope it helps you manage features in a clean, maintainable way.
Feel free to reach out with feedback, questions or ideas for improvement. π
| Product | Versions 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 is compatible. 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 is compatible. 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. |
-
net6.0
- No dependencies.
-
net7.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.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 | 282 | 8/26/2025 |