Cirreum.Coordination
1.2.0
dotnet add package Cirreum.Coordination --version 1.2.0
NuGet\Install-Package Cirreum.Coordination -Version 1.2.0
<PackageReference Include="Cirreum.Coordination" Version="1.2.0" />
<PackageVersion Include="Cirreum.Coordination" Version="1.2.0" />
<PackageReference Include="Cirreum.Coordination" />
paket add Cirreum.Coordination --version 1.2.0
#r "nuget: Cirreum.Coordination, 1.2.0"
#:package Cirreum.Coordination@1.2.0
#addin nuget:?package=Cirreum.Coordination&version=1.2.0
#tool nuget:?package=Cirreum.Coordination&version=1.2.0
Cirreum.Coordination
Atomic and ephemeral coordination primitives for .NET — single-use claims, fixed-window throttling, and pub/sub signaling over a pluggable backend
Overview
Cirreum.Coordination provides small, focused coordination primitives with a built-in in-memory default and a pluggable backend — the same charter classic coordination services (ZooKeeper, etcd, Consul) bundle as locks, leases, and watch/notify:
IReplayGuard— a single-use claim (atomic set-if-absent): the first caller to claim a token wins; replays within the window are rejected. For nonce / replay protection, idempotency keys, and message de-duplication.IRequestThrottle— an atomic fixed-window counter: records a hit against a key and reports whether the caller is still within the limit. For rate limiting.ISignalBroadcaster— an ephemeral publish/subscribe primitive: notifies whichever instances are currently listening on a channel. At-most-once, unbuffered — a live nudge, not a durable message (for that, useCirreum.Messaging.Distributed).
It depends only on Microsoft.Extensions.DependencyInjection.Abstractions, so it works standalone in any .NET application. Add Cirreum.Coordination.Redis for distributed, multi-instance coordination.
Installation
dotnet add package Cirreum.Coordination
Usage
Choose a backend once, at the application level:
// single-node / development
builder.Services.AddCoordination(c => c.UseInMemory());
// distributed (reference Cirreum.Coordination.Redis)
builder.Services.AddCoordination(c => c.UseRedis());
Consume the primitives wherever you need them:
public sealed class NonceChecker(IReplayGuard replayGuard) {
public ValueTask<bool> IsFirstUseAsync(string nonce) =>
replayGuard.TryClaimAsync(nonce, TimeSpan.FromMinutes(2)); // true once, false on replay
}
public sealed class RateLimiter(IRequestThrottle throttle) {
public async Task<bool> AllowAsync(string clientId) {
var outcome = await throttle.RecordAsync(clientId, TimeSpan.FromMinutes(1), limit: 100);
return outcome.Allowed; // outcome.RetryAfter hints when throttled
}
}
public sealed class CacheInvalidationSignal(ISignalBroadcaster broadcaster) {
public ValueTask NotifyAsync(string key) =>
broadcaster.PublishAsync("cache-invalidated", Encoding.UTF8.GetBytes(key));
public ValueTask ListenAsync() =>
broadcaster.SubscribeAsync("cache-invalidated", (payload, _) => {
var key = Encoding.UTF8.GetString(payload.Span);
// evict key locally
return ValueTask.CompletedTask;
});
}
Pull-and-validate (fail fast on a missing backend)
A component that requires coordination can pull the dependency so a mis-configured host fails fast at startup rather than at the first request:
services.AddCoordination(); // ensure: registers a fail-closed sentinel if no backend yet
// ...later, once every registration has run:
CoordinationPostureValidator.Validate(services); // throws if coordination was pulled but no backend chosen
AddCoordination() (ensure) and AddCoordination(c => c.UseX()) (choose) are idempotent and
order-independent — the chosen backend always wins, regardless of call order. Until a backend is chosen, the
primitives are backed by a fail-closed sentinel that throws rather than silently allow.
Scoping (sharing one backend across applications and environments)
A distributed backend is one flat keyspace: without a scope, two applications (or a Test and a Production
deployment of the same application) sharing an instance share replay claims, throttle windows, and signal
channels — colliding throttle keys count against each other, and one deployment's signals are delivered to
the other. CoordinationScope namespaces all of it:
builder.Services.AddCoordination(c => c
.UseRedis()
.WithScope("MyApp", "Production")); // canonical {app}:{env}, or WithScope("any-opaque-value")
Distributed adapters fold the scope into every key and channel they touch (the in-memory backend ignores
it — a process is already its own scope). The scope value is opaque to this package; composition surfaces
that know the application's identity (e.g. Cirreum's authentication composition) provide the canonical
{ApplicationName}:{EnvironmentName} default automatically when none is registered, and an explicit
WithScope(...) always wins.
Contribution Guidelines
Be conservative with new abstractions
The API surface must remain stable and meaningful.Limit dependency expansion
Only add foundational, version-stable dependencies.Favor additive, non-breaking changes
Breaking changes ripple through the entire ecosystem.Include thorough unit tests
All primitives and patterns should be independently testable.Document architectural decisions
Context and reasoning should be clear for future maintainers.Follow .NET conventions
Use established patterns from Microsoft.Extensions.* libraries.
Versioning
Cirreum.Coordination follows Semantic Versioning:
- Major - Breaking API changes
- Minor - New features, backward compatible
- Patch - Bug fixes, backward compatible
License
This project is licensed under the MIT License - see the LICENSE file for details.
Cirreum Foundation Framework
Layered simplicity for modern .NET
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Cirreum.Coordination:
| Package | Downloads |
|---|---|
|
Cirreum.AuthenticationProvider
Authentication track abstractions for the Cirreum framework (three-pillar separation and composition surface). Defines the contracts scheme packages (ApiKey, SignedRequest, SessionTicket) implement: ISchemeSelector + SchemeCategory, CredentialTransport, ISignedRequestAlgorithm + resolver, SessionTicket primitives, [AllowPendingAuth], IAuthenticationBoundaryResolver, IAuthenticationBuilder. Pure abstractions plus a default impl (DefaultAuthenticationBoundaryResolver) that ships with the contracts. Host-agnostic profile enrichment (IUserProfileEnrichmentBuilder, ClaimsUserProfileEnricher) lives in Cirreum.Contracts/Cirreum.Domain, not here. |
|
|
Cirreum.Authentication.SignedRequest
SignedRequest authentication scheme for the Cirreum framework. Verifies RFC 9421 HTTP Message Signatures (HMAC-SHA256, with a pluggable algorithm resolver) and RFC 9530 Content-Digest for partner / M2M integrations, and signs outbound requests. Ships the authentication handler, ISchemeSelector, the dynamic credential resolver, and the strict-nonce replay posture. |
|
|
Cirreum.Coordination.Redis
Redis backend for Cirreum.Coordination — distributed, multi-instance atomic and ephemeral coordination. Implements IReplayGuard (atomic SET NX claim for replay / nonce protection), IRequestThrottle (atomic Lua fixed-window counter for rate limiting), and ISignalBroadcaster (Redis pub/sub live signals for cross-instance notification) over an application-provided StackExchange.Redis IConnectionMultiplexer. CoordinationScope namespaces every key and channel per application/environment so multiple deployments can safely share one instance. The adapter is blind to connection configuration — by default it shares the same connection the rest of your app uses, or UseRedis(connectionKey) rides a dedicated keyed connection — and coordinates across every instance sharing the connection. |
GitHub repositories
This package is not used by any popular GitHub repositories.