Skip to content

Getting Started

Sharp Ninja edited this page May 16, 2026 · 1 revision

Getting Started with SharpNinja Feature Flags

This guide walks you from NuGet installation to your first evaluated flag in a .NET 10 application.

Prerequisites

Requirement Details
.NET SDK 10.0 or later
Supported platforms net10.0 (Linux, Windows), net10.0-android, net10.0-ios, net10.0-maccatalyst, net10.0-windows10.0.19041.0
Dependency injection Microsoft.Extensions.DependencyInjection (any version compatible with .NET 10)

Installation

Install the packages from NuGet. You will need at minimum the runtime SDK. The Build package is required only if you want MSBuild manifest embedding (recommended for production).

<!-- runtime SDK - evaluates flags and uploads exposures -->
<PackageReference Include="SharpNinja.FeatureFlags" Version="1.0.0" />

<!-- public contracts, types, and interfaces -->
<PackageReference Include="SharpNinja.FeatureFlags.Abstractions" Version="1.0.0" />

<!-- MSBuild integration: manifest embedding, identity stamping, source generation -->
<PackageReference Include="SharpNinja.FeatureFlags.Build" Version="1.0.0" />

SharpNinja.FeatureFlags already depends on SharpNinja.FeatureFlags.Abstractions, so you only need to list Abstractions explicitly when a project references the contracts without the full runtime (for example, a library project that only uses ISharpNinjaFeatureClient).

MSBuild Configuration

When SharpNinja.FeatureFlags.Build is referenced, its build-transitive targets run automatically. Tell them about your product by setting two properties:

<PropertyGroup>
  <!-- required: identifies which product's manifest to load -->
  <ProductId>truckmate</ProductId>

  <!-- required: identifies the exact release being shipped -->
  <ReleaseId>truckmate-1.2.0-stable-0</ReleaseId>

  <!-- optional: path to the manifest JSON file (default: $(MSBuildProjectDirectory)\flags\flags.json) -->
  <SharpNinjaFeatureFlagsManifest>$(MSBuildProjectDirectory)\flags\flags.json</SharpNinjaFeatureFlagsManifest>

  <!-- optional: path to the Ed25519 public key file (default: $(MSBuildProjectDirectory)\flags\public-key.ed25519) -->
  <SharpNinjaFeatureFlagsPublicKey>$(MSBuildProjectDirectory)\flags\public-key.ed25519</SharpNinjaFeatureFlagsPublicKey>
</PropertyGroup>

See MSBuild Integration for the full property reference.

Minimal Service Registration

Call AddSharpNinjaFeatureFlags on your IServiceCollection. The method requires an options record and the raw manifest JSON.

using Microsoft.Extensions.DependencyInjection;
using SharpNinja.FeatureFlags;
using SharpNinja.FeatureFlags.Abstractions.Options;

// Build your options. All five constructor parameters are required.
var options = new SharpNinjaFeatureFlagOptions(
    productId: "truckmate",
    releaseId: "truckmate-1.2.0-stable-0",
    environment: "production",
    manifestRefreshInterval: TimeSpan.FromMinutes(5),
    exposureUploadInterval: TimeSpan.FromSeconds(30));

// Load the manifest JSON however is appropriate for your app.
// For a bundled resource, use Assembly.GetManifestResourceStream.
string manifestJson = File.ReadAllText("flags/flags.json");

ServiceCollection services = new();
services.AddSharpNinjaFeatureFlags(options, manifestJson);

ServiceProvider provider = services.BuildServiceProvider();

Generated zero-argument overload

When SharpNinjaFeatureFlagsGenerateRegistrationSource is set to true in your project file, the Build target generates a zero-argument overload that reads the embedded resource and uses the MSBuild property values for ProductId, ReleaseId, and environment automatically:

// Generated by the Build target - no arguments needed.
services.AddSharpNinjaFeatureFlags();

See MSBuild Integration for how to enable source generation.

Full method signatures

// Overload 1: raw manifest JSON (uses a development-only structural signature internally)
public static IServiceCollection AddSharpNinjaFeatureFlags(
    this IServiceCollection services,
    SharpNinjaFeatureFlagOptions options,
    string manifestJson);

// Overload 2: pre-built signed envelope (production path - carries signature, key ID, and algorithm)
public static IServiceCollection AddSharpNinjaFeatureFlags(
    this IServiceCollection services,
    SharpNinjaFeatureFlagOptions options,
    SignedManifestEnvelope manifestEnvelope);

Evaluating Your First Flag

Resolve ISharpNinjaFeatureClient from the container and call Evaluate<T>. The method never blocks on network state - it always returns synchronously from the in-memory manifest.

using SharpNinja.FeatureFlags.Abstractions;

ISharpNinjaFeatureClient client = provider.GetRequiredService<ISharpNinjaFeatureClient>();

// Synchronous evaluation
EvaluationResult<bool> result = client.Evaluate(
    key: "dashboard.enabled",
    defaultValue: false);

if (result.Value)
{
    Console.WriteLine("Dashboard is enabled.");
}
Console.WriteLine($"Resolved by: {result.Reason}"); // e.g. RuleMatch, Default, Disabled

Supplying evaluation context

Pass caller-specific attributes via EvaluationContext to target rules by user, tenant, or any custom dimension:

EvaluationContext context = EvaluationContext.Builder()
    .Set("project.id", "truckmate")
    .Set("user.role", "admin")
    .Build();

EvaluationResult<string> title = client.Evaluate(
    key: "reports.title",
    defaultValue: "Reports",
    context: context);

Console.WriteLine(title.Value); // e.g. "TruckMate Reports"

Async evaluation

EvaluateAsync provides an async-compatible surface. The current implementation resolves synchronously but the signature supports future network-aware evaluation without breaking callers.

EvaluationResult<bool> result = await client.EvaluateAsync(
    key: "new-ui.enabled",
    defaultValue: false,
    context: context,
    cancellationToken: cancellationToken);

Reading the result

Property Type Description
Value T The resolved flag value
Reason EvaluationReason Why the value was chosen
Variant string? Matched variant identifier, if any
RuleIndex int? Zero-based index of the matched rule, if any
ErrorMessage string? Error detail when Reason is Error

EvaluationReason values: Unknown, Default, RuleMatch, TargetingMatch, Disabled, Error.

What Happens at Startup

  1. Bundled manifest loaded first. The manifest JSON you provide (or embed via the Build target) is parsed and validated against ProductId, ReleaseId, and Environment. A mismatch throws at registration time so misconfigured apps fail fast.

  2. Disk cache checked. SharpNinjaDiskManifestCacheStore looks for a previously fetched manifest in %LOCALAPPDATA%\SharpNinja\FeatureFlags\{productId}\{releaseId}\{environment}\manifest-cache.json. When found and its signature is valid, the cached manifest replaces the bundled one. Evaluation is immediately available with the fresher data.

  3. Background refresh scheduled. If DistributionBaseUri is configured, SharpNinjaRemoteFetchCoordinator fetches the manifest from the distribution service on the ManifestRefreshInterval cadence. Successful responses are signature-verified, activated in memory, and written to the disk cache.

  4. Exposure events buffered and uploaded. Every Evaluate call records a SharpNinjaExposureEvent to the file-backed outbox. A background coordinator drains the outbox on the ExposureUploadInterval cadence and POSTs batches to the configured ExposureUploadEndpoint or derives the endpoint from DistributionBaseUri.

All evaluation calls are lock-free reads against the current in-memory manifest. Network failures never affect evaluation latency.

Clone this wiki locally