Totum.Flags
2.0.3
dotnet add package Totum.Flags --version 2.0.3
NuGet\Install-Package Totum.Flags -Version 2.0.3
<PackageReference Include="Totum.Flags" Version="2.0.3" />
<PackageVersion Include="Totum.Flags" Version="2.0.3" />
<PackageReference Include="Totum.Flags" />
paket add Totum.Flags --version 2.0.3
#r "nuget: Totum.Flags, 2.0.3"
#:package Totum.Flags@2.0.3
#addin nuget:?package=Totum.Flags&version=2.0.3
#tool nuget:?package=Totum.Flags&version=2.0.3
Totum.Flags
.NET client for Totum feature flags. Call InitAsync once at startup, then read flags synchronously from memory — with background refresh and safe fallbacks when the network is unavailable.
Targets .NET Standard 2.1 (.NET Framework 4.7.2+, .NET Core 3.0+, .NET 5+, Unity).
AI agents & developers: use AGENT.md — copy into your app repo (
.cursor/rules,AGENTS.md) for correct flag integration. This README is the full reference.
Install
The SDK is split into four NuGet packages. Pick the row that matches your app:
| Environment | Packages |
|---|---|
| ASP.NET Core | Totum.Flags.Extensions.DependencyInjection (includes core + System.Text.Json) |
| .NET (console, worker, etc.) | Totum.Flags + Totum.Flags.SystemTextJson |
| Unity (Newtonsoft.Json 13.0.2) | Totum.Flags + Totum.Flags.NewtonsoftJson |
# ASP.NET Core
dotnet add package Totum.Flags.Extensions.DependencyInjection
# .NET (non-ASP.NET)
dotnet add package Totum.Flags
dotnet add package Totum.Flags.SystemTextJson
# Unity
dotnet add package Totum.Flags
dotnet add package Totum.Flags.NewtonsoftJson
Prerequisites — proxy URL and API key
Every app needs two values from your platform team (see SETUP-WORKER §8):
| Value | Example | Sent as |
|---|---|---|
| Proxy URL | https://flags.totumcloud.com |
FlagsClientOptions.ProxyUrl |
| API key | totum-mobile-prod-key |
FlagsClientOptions.ApiKey → header X-Flags-Api-Key |
The names FLAGS_PROXY_URL and FLAGS_API_KEY are conventions for server apps (env vars, appsettings.json). The SDK does not read those names itself — you pass the strings into FlagsClientOptions.
Do not put Cloudflare account tokens or Flagship credentials in app code. The proxy API key is an app-level gate (similar to a mobile app API key): it ships with the client, but it is not a Cloudflare account secret.
.NET / ASP.NET
Load from environment variables, appsettings.json, or your secrets store:
{
"FLAGS_PROXY_URL": "https://flags.totumcloud.com",
"FLAGS_API_KEY": "your-proxy-key"
}
Unity
Unity has no process.env or appsettings.json. Use a ScriptableObject or build-time config — not PlayerPrefs. Full guide: Unity configuration below.
Quick start
.NET (System.Text.Json)
using Totum.Flags;
using Totum.Flags.SystemTextJson;
var flags = FlagsClientFactory.Create(new FlagsClientOptions
{
ProxyUrl = Environment.GetEnvironmentVariable("FLAGS_PROXY_URL")!,
ApiKey = Environment.GetEnvironmentVariable("FLAGS_API_KEY")!,
});
await flags.InitAsync(new[]
{
new FlagDeclaration("my-app-dark-mode", FlagType.Boolean, false),
new FlagDeclaration("my-app-checkout-flow", FlagType.String, "v1"),
});
// Synchronous — no await on getters
var darkMode = flags.GetBoolean("my-app-dark-mode", false);
var checkout = flags.GetString("my-app-checkout-flow", "v1");
Unity (Newtonsoft.Json)
See the full Unity section for where ProxyUrl and ApiKey come from. Minimal usage:
using Totum.Flags;
using Totum.Flags.NewtonsoftJson;
var flags = FlagsClientFactory.Create(new FlagsClientOptions
{
ProxyUrl = config.ProxyUrl,
ApiKey = config.ApiKey,
});
await flags.InitAsync(declarations);
var enabled = flags.GetBoolean("video-uploader-metrics-enabled", false);
Unity
Where do ProxyUrl and ApiKey come from?
- Platform team provisions the Worker and adds your app to
API_KEYS(SETUP-WORKER §8). - They give you the proxy URL (e.g.
https://flags.totumcloud.com) and an API key for your build (e.g.totum-mobile-prod-key). - Your Unity project stores those two strings in app config and passes them to
FlagsClientOptionsat startup.
You do not read FLAGS_PROXY_URL / FLAGS_API_KEY from the environment in Unity. You do not use PlayerPrefs for URL or API key — PlayerPrefs is per-user, editable at runtime, and the wrong place for app configuration.
Recommended pattern: ScriptableObject per environment
Use one config asset per environment. Swap the asset (or its values) for Dev, Staging, and Production builds.
1. Config asset — Assets/Scripts/Flags/FlagsRuntimeConfig.cs:
using UnityEngine;
[CreateAssetMenu(fileName = "FlagsRuntimeConfig", menuName = "Totum/Flags Runtime Config")]
public sealed class FlagsRuntimeConfig : ScriptableObject
{
[Tooltip("From platform team — e.g. https://flags.totumcloud.com")]
public string ProxyUrl = "";
[Tooltip("From platform team — one key from Worker API_KEYS")]
public string ApiKey = "";
}
2. Create assets in the Editor
| Asset | Typical use |
|---|---|
FlagsRuntimeConfig.dev.asset |
Local / Editor play mode |
FlagsRuntimeConfig.staging.asset |
QA builds |
FlagsRuntimeConfig.prod.asset |
Store builds — do not commit prod key to git; inject via CI |
3. Bootstrap — wire config into the client once at startup:
using System.Collections.Generic;
using System.IO;
using Totum.Flags;
using Totum.Flags.NewtonsoftJson;
using UnityEngine;
public sealed class FlagsBootstrap : MonoBehaviour
{
[SerializeField] private FlagsRuntimeConfig config = null!;
private FlagsClient? _client;
private async void Start()
{
_client = FlagsClientFactory.Create(new FlagsClientOptions
{
ProxyUrl = config.ProxyUrl,
ApiKey = config.ApiKey,
PersistFilePath = Path.Combine(Application.persistentDataPath, "flags_cache.json"),
});
await _client.InitAsync(new[]
{
new FlagDeclaration("video-uploader-metrics-enabled", FlagType.Boolean, false),
});
}
private void OnDestroy() => _client?.Dispose();
public bool IsMetricsEnabled() =>
_client?.GetBoolean("video-uploader-metrics-enabled", false) ?? false;
}
4. Assign the right asset — drag FlagsRuntimeConfig.dev.asset onto the config field in the Inspector for local work. CI replaces or selects the prod asset before a release build.
Switching environments
| Approach | When to use |
|---|---|
| Different ScriptableObject assets | Most teams — swap asset reference per build profile |
#if DEVELOPMENT_BUILD / custom defines |
Pick asset or URL in code at compile time |
| CI injects values | Prod key never in repo — pipeline writes FlagsRuntimeConfig.prod.asset or patches fields before build |
| Resources JSON | Alternative to ScriptableObject if you prefer a TextAsset |
What to use — and what to avoid
| Storage | Proxy URL / API key? | Flag cache? |
|---|---|---|
| ScriptableObject (per env) | Yes — recommended | No |
| Resources / TextAsset JSON | Yes | No |
| Build pipeline / CI secrets | Yes (prod) | No |
Application.persistentDataPath file |
No | Yes — set PersistFilePath here |
| PlayerPrefs | No | No — use PersistFilePath instead |
| Hard-coded in C# | Dev smoke tests only | No |
CI / release builds (sketch)
Your pipeline holds the prod API key as a secret. Before BuildPlayer, generate or patch the prod config:
# Example: write prod config (paths vary per project)
echo '{"ProxyUrl":"https://flags.totumcloud.com","ApiKey":"'"$FLAGS_API_KEY"'"}' \
> Assets/StreamingAssets/flags-config.json
Or use Unity -executeMethod to assign fields on FlagsRuntimeConfig.prod.asset from environment variables your CI provides.
Troubleshooting (Unity)
| Symptom | Check |
|---|---|
401 / flags always default |
ApiKey on config asset matches a key in Worker API_KEYS |
| Works in Editor, fails on device | Device build uses prod/staging asset with correct URL (not localhost) |
| Stale flags after deploy | Delete cache file under Application.persistentDataPath, or wait for TTL refresh |
JsonCodec is required |
Install Totum.Flags.NewtonsoftJson and use FlagsClientFactory.Create |
Flag keys
Use hyphen-separated prefixes per product:
totum-admin-dark-modevideo-uploader-metrics-enabled
Each flagKey must exist in the flag dashboard with the matching type.
Dashboard: Enabled is not the same as true
| Control | Meaning | Typical reason |
|---|---|---|
| Enabled | Flag is active | DISABLED if off |
| Default variant | Value when no rules match | DEFAULT |
Integration pattern
- One
FlagsClientper app (singleton). await InitAsync(declarations[, context])once — list every flag you read.GetBoolean/GetString/GetNumber/GetObjectare synchronous.await RefreshAsync(context)after login or targeting changes.Dispose()when tearing down (stops background refresh).
Targeting context
await flags.InitAsync(declarations, EvaluationContext.From(new Dictionary<string, object>
{
["userId"] = "anonymous",
}));
await flags.RefreshAsync(EvaluationContext.From(new Dictionary<string, object>
{
["userId"] = user.Id,
["plan"] = user.Plan,
}));
Optional file persistence
.NET — any writable path:
PersistFilePath = Path.Combine(Path.GetTempPath(), "flags_cache.json"),
Unity — use Application.persistentDataPath (see Unity bootstrap example).
ASP.NET Core
Install Totum.Flags.Extensions.DependencyInjection and register in Program.cs:
using Totum.Flags;
using Totum.Flags.Extensions.DependencyInjection;
builder.Services.AddTotumFlags(
builder.Configuration,
new[]
{
new FlagDeclaration("my-app-dark-mode", FlagType.Boolean, false),
new FlagDeclaration("my-app-checkout-flow", FlagType.String, "v1"),
});
var app = builder.Build();
// InitAsync runs automatically via IHostedService — inject IFlagsClient in controllers/services
Custom TTL, cache path, or HttpClient:
builder.Services.AddTotumFlags(options =>
{
options.ProxyUrl = builder.Configuration["FLAGS_PROXY_URL"]!;
options.ApiKey = builder.Configuration["FLAGS_API_KEY"]!;
options.Ttl = TimeSpan.FromMinutes(5);
}, declarations);
Advanced: custom JSON codec
If you need direct control, set JsonCodec on FlagsClientOptions and construct FlagsClient yourself:
var options = new FlagsClientOptions
{
ProxyUrl = proxyUrl,
ApiKey = apiKey,
JsonCodec = SystemTextJsonCodec.Instance,
};
var flags = new FlagsClient(options);
Client options
| Option | Default | Description |
|---|---|---|
ProxyUrl |
required | Flags API base URL |
ApiKey |
required | X-Flags-Api-Key header |
JsonCodec |
required | Set via adapter FlagsClientFactory or manually |
Ttl |
30 seconds | Background refresh interval |
PersistFilePath |
null | Optional cache file path |
HttpClient |
new instance | Inject for DI / testing |
Caching and failures
Fallback order:
- Fresh batch response
- In-memory cache
- Persisted file (if
PersistFilePathset) defaultValuepassed to getters
InitAsync and RefreshAsync do not throw on network errors. Diagnostics use System.Diagnostics.Trace with prefix [Totum.Flags].
Anti-patterns
| Wrong | Correct |
|---|---|
| Infrastructure credentials in the app | Proxy URL + API key only (from platform team) |
PlayerPrefs for URL or API key (Unity) |
ScriptableObject or build-time config per environment |
Totum.Flags without a JSON adapter |
Add Totum.Flags.SystemTextJson or Totum.Flags.NewtonsoftJson |
await flags.GetBoolean(...) |
flags.GetBoolean(...) |
| HTTP per flag per request | InitAsync once; sync reads |
Flag not in InitAsync list |
Declare every flag up front |
AI agents
- .NET: load proxy URL and API key from env /
appsettings(FLAGS_PROXY_URL,FLAGS_API_KEY). - Unity: load from ScriptableObject or build config — not env vars, not PlayerPrefs.
- List every flag in
InitAsyncbefore calling getters. - Do not bypass this client for routine flag reads.
License
Closed Source. Proprietary & Confidential. See package LICENSE.
| Product | Versions 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 was computed. 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. |
-
.NETStandard 2.1
- System.Net.Http (>= 4.3.4)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Totum.Flags:
| Package | Downloads |
|---|---|
|
Totum.Flags.NewtonsoftJson
Newtonsoft.Json adapter for Totum.Flags (Unity-compatible). |
|
|
Totum.Flags.SystemTextJson
System.Text.Json adapter for Totum.Flags. |
GitHub repositories
This package is not used by any popular GitHub repositories.