Celerity.Sentinel 2.4.0

There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Celerity.Sentinel --version 2.4.0
                    
NuGet\Install-Package Celerity.Sentinel -Version 2.4.0
                    
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="Celerity.Sentinel" Version="2.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Celerity.Sentinel" Version="2.4.0" />
                    
Directory.Packages.props
<PackageReference Include="Celerity.Sentinel" />
                    
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 Celerity.Sentinel --version 2.4.0
                    
#r "nuget: Celerity.Sentinel, 2.4.0"
                    
#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 Celerity.Sentinel@2.4.0
                    
#: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=Celerity.Sentinel&version=2.4.0
                    
Install as a Cake Addin
#tool nuget:?package=Celerity.Sentinel&version=2.4.0
                    
Install as a Cake Tool

Celerity.Sentinel

Streaming abuse / heavy-hitter detection in fixed memory — flag the busiest IPs, tokens, or request fingerprints no matter how many distinct keys the stream contains. Built on Celerity's probabilistic sketches, generic over your key type and a zero-cost, JIT-inlined hasher.

dotnet add package Celerity.Sentinel

The categorical win: survives instead of crashing

A naive Dictionary<string,int> (or ConcurrentDictionary) request counter stores one entry per distinct key — so an attacker rotating through millions of keys grows it without bound until the process OOMs. Celerity.Sentinel is sized once (a couple of MB at the defaults) and never grows with cardinality, so it survives exactly the adversarial input that kills the exact counter — while still surfacing the true heavy hitters.

One Observe(key) fans the key into four bounded sketches:

Signal Structure Guarantee
per-key rate CountMinSketch never underestimates
top offenders TopKSketch (Space-Saving) O(k) memory; never misses a key above Total / OffenderCapacity
distinct volume HyperLogLog ~0.8% error from 16 KB
first-seen (new key) BloomFilter no false negatives

Usage

using Celerity.Sentinel;

var sentinel = new StringAbuseTracker();   // string keys, StringXxHash3Hasher

foreach (var request in requests)
{
    ObservationResult r = sentinel.Observe(request.ClientIp);
    if (r.IsFirstSeen) ApplyNewClientFriction(request);
    if (r.EstimatedCount > threshold) Throttle(request);
}

AbuseReport<string> report = sentinel.Snapshot(topN: 20);
foreach (Offender<string> o in report.Offenders)
    Console.WriteLine($"{o.Key}: ~{o.EstimatedCount} (±{o.Error})");
Console.WriteLine($"{report.DistinctKeys:N0} distinct clients, {report.TotalObservations:N0} requests");

Generic over any key type:

var byId = new AbuseTracker<long, Int64WangNaiveHasher>();

Concurrency: per-core striping + merge

A single tracker is single-threaded (like every Celerity collection). For real edge QPS, use StripedAbuseTracker — independent lanes with no lock on the observe path, rolled up on demand:

var striped = new StringStripedAbuseTracker(laneCount: Environment.ProcessorCount);

// each producer observes into a lane it exclusively owns:
striped.Observe(coreIndex, request.ClientIp);

// a coordinator periodically rolls the lanes up (quiesce or coordinate first):
AbuseReport<string> report = striped.Snapshot(topN: 20);

Merges are exact for rate / distinct / first-seen and use the standard Space-Saving approximation for offenders. The same Merge primitive rolls up per-shard trackers across a fleet.

Windowing

Rate and offender counts are cumulative. For a time-windowed rate, reset on a tumbling interval with Clear() (or swap in a fresh instance).

Why managed (and not a native binding / Redis)

The update path is maximally chatty — one or more sketch bumps per request over a managed key hashed by an inlined struct hasher. There is no in-process native sketch library to bind, and RedisBloom is an out-of-process network hop per update that cannot keep pace at edge QPS. The value — bounded, GC-friendly memory under adversarial key rotation, generic over the caller's fingerprint type — is something a native library working over flat buffers cannot express.

Part of the Celerity family.

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 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 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.

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
2.4.1-beta.4 26 7/26/2026
2.4.0 34 7/26/2026
2.3.1-beta.15 35 7/25/2026
2.3.1-beta.8 35 7/23/2026
2.3.1-beta.2 34 7/22/2026
2.3.0 92 7/19/2026
2.2.1-beta.24 56 7/18/2026
2.2.1-beta.18 47 7/14/2026
2.2.1-beta.15 47 7/12/2026