Sidub.Licensing.Client
2.2.0
Prefix Reserved
dotnet add package Sidub.Licensing.Client --version 2.2.0
NuGet\Install-Package Sidub.Licensing.Client -Version 2.2.0
<PackageReference Include="Sidub.Licensing.Client" Version="2.2.0" />
<PackageVersion Include="Sidub.Licensing.Client" Version="2.2.0" />
<PackageReference Include="Sidub.Licensing.Client" />
paket add Sidub.Licensing.Client --version 2.2.0
#r "nuget: Sidub.Licensing.Client, 2.2.0"
#:package Sidub.Licensing.Client@2.2.0
#addin nuget:?package=Sidub.Licensing.Client&version=2.2.0
#tool nuget:?package=Sidub.Licensing.Client&version=2.2.0
Sidub.Licensing.Client
.NET client SDK for Monaiq licensing by Sidub — license enforcement, feature gating, rate limiting, and metered consumption reporting for applications that hold a Monaiq license.
Install
dotnet add package Sidub.Licensing.Client
Targets netstandard2.1 — usable from .NET Core 3.0+, .NET 5+ and modern .NET. Built on Sidub.Platform 1.11.4.
Setup
Register the SDK and bind your credential. The encoded credential (SIDUB_LIC_…) comes from the Monaiq portal or your license-delivery email and bundles everything the SDK needs.
using Sidub.Licensing;
using Sidub.Licensing.Client;
builder.Services.Configure<LicensingServiceOptions>(options =>
{
options.LicenseServiceUri = "https://api.monaiq.com/licensing";
options.ConsumptionServiceUri = "https://api.monaiq.com/consumption";
options.EncodedCredential = builder.Configuration["Licensing:EncodedCredential"];
options.IssuerClientId = Guid.Parse(builder.Configuration["Licensing:IssuerClientId"]!); // your account's client id
options.ProductId = Guid.Parse(builder.Configuration["Licensing:ProductId"]!); // the product this application is
});
builder.Services.AddSidubLicensing();
IssuerClientId and ProductId bind the application to your account and product. The platform refuses a seat of another seller or product with LicenseAuthorizationDeniedException (reason ProductMismatch), and the SDK refuses an authorization or grant whose signed issuer is not the declared seller. Set both: without them, any credential whose features carry the same keys is accepted.
Check a license
ILicensingService is the enforcement surface: fetch a signed authorization, gate features against it, and record metered operations. The LicensingContextAccessor resolves the configured credential.
using Sidub.Licensing;
using Sidub.Licensing.Context;
using Sidub.Licensing.Exceptions;
using Sidub.Licensing.Features;
using Sidub.Licensing.Services;
public sealed class ReportService(ILicensingService licensing, LicensingContextAccessor contextAccessor)
{
private static readonly LicensingServiceReference ServiceReference = new();
public async Task RunAsync()
{
var context = await contextAccessor.GetContextAsync()
?? throw new InvalidOperationException("No license is configured.");
// A cryptographically signed, cached authorization for the configured license.
var authorization = await licensing.GetAuthorization(ServiceReference, context);
// Gate a rate-limited feature; the sliding window is enforced locally.
// GetLicenseFeature returns null when the license does not carry the feature —
// handle that as "not licensed", never suppress it with the ! operator.
var feature = await licensing.GetLicenseFeature<RateLimitLicenseFeature>(
ServiceReference, context, "reports.generate");
if (feature is null)
{
// The feature is not on this license — surface an upgrade path to the user.
return;
}
try
{
await licensing.PerformOperation<RateLimitLicenseFeature, RateLimitLicenseFeatureState>(
ServiceReference, context, new RateLimitLicenseFeatureOperation(feature, 1));
}
catch (RateLimitException)
{
// The throw IS the enforcement signal — surface an upgrade path to the user.
}
}
}
AssertLicense evaluates declarative assertions (feature existence, service access, rate limits, allowances) against an authorization; rate-limit and allowance violations throw RateLimitException and AllowanceExhaustedException rather than returning false.
Allowances
An allowance is so many uses per billing period, counted by the platform across every runtime of the seat. Record a use; a use the period cannot cover throws.
var exports = await licensing.GetLicenseFeature<QuotaLicenseFeature>(ServiceReference, context, "exports.monthly");
if (exports is null)
return; // not on this license
try
{
await licensing.PerformOperation(ServiceReference, context, new QuotaLicenseFeatureOperation(exports, 1));
}
catch (AllowanceExhaustedException ex)
{
// Refused: not counted, not reported. ex.PeriodEndUtc says when the period resets.
}
To refuse before doing the work, check first with await licensing.AssertAllowance(ServiceReference, context, "exports.monthly"), then record the use after the work. Work that records several uses passes how many to both calls.
How the count is kept is the SDK's: it reserves signed grants of units from the platform as it goes, takes the next grant before the units held run out so a use does not wait, and returns what it did not spend when the process stops. There is nothing to configure, wire or call. An access check (AssertLicense) is never counted as a use. The return runs from the host's StoppedAsync lifecycle step (Microsoft.Extensions.Hosting 8.0 or later); an older host returns the units when the container is disposed or the process exits, within 5 seconds.
The period's figures are on the state view, not the authorization: ILicenseStateProvider.GetState(...) returns one AllowanceView per allowance — Used, Reserved (held by the seat's other runtimes), Remaining, PeriodEndUtc and AsOfUtc (when the platform gave the figures). Reading it takes no units. The authorization's QuotaLicenseFeature carries the Allowance alone.
Sell from inside your app
An application you distribute — desktop, mobile, CLI — can sell your offerings from inside itself
with IPurchaseClient. The buyer signs in with their Monaiq identity and buys with their own
token; no key of yours ships with the app, and the client never sends one.
using Sidub.Licensing.Checkout;
using Sidub.Licensing.Client;
using Sidub.Licensing.Client.Purchase;
builder.Services.AddSidubLicensingPurchaseClient(options =>
{
options.PurchaseServiceUri = "https://api.monaiq.com/purchase";
options.AccessTokenProvider = ct => signIn.AcquireAccessTokenAsync(ct); // MSAL (desktop) or native auth (mobile)
});
// Read the catalog live (anonymous), then buy as the signed-in buyer.
var catalog = await purchase.ListOfferingsAsync(issuerClientId);
var session = await purchase.CreateCheckoutAsync(new PurchaseCheckoutRequest
{
RequestId = Guid.NewGuid(), // idempotency key — reuse on retry
OfferingId = offering.Id,
IssuerClientId = issuerClientId
});
if (session.SessionUrl is not null)
OpenSystemBrowser(session.SessionUrl); // paid: Stripe Checkout; free: null, license already minted
var result = await purchase.PollResultAsync(session.SessionId);
// result.EncodedCredential is the credential the runtime SDK takes.
The guide, the sign-in options and the raw HTTP contract: https://docs.monaiq.com/integrate/in-app-purchase/
Resilience built in
- Authorization fetches retry transient failures with bounded backoff and a per-attempt timeout; exhaustion surfaces as a typed
LicenseAuthorizationUnavailableException. - The last signature-verified authorization bridges brief API outages inside a bounded grace window — denials and revocations are never bridged.
- Metered consumption is buffered, aggregated, and delivered with exactly-once submission identity; buffered usage flushes on shutdown.
- Delivery is bounded by
LicensingConsumptionBufferOptions, validated at start: a submission attempt times out (SubmitTimeoutMs, 10 s), aggregates go a few at a time (MaxConcurrentSubmissions, 4), one that keeps failing waits longer between flushes (MaxRetryBackoffMs, 5 min), and the buffer is capped (MaxPendingAggregates, 10000). - Every signed answer — authorization, allowance grant, allowance reading — is verified with the seat's service key in the signature layout the SDK states on every call; one that does not verify is
LicenseVerificationException. - Authorizations and feature state live in the SDK's own
LicensingMemoryCache; a hostIMemoryCachewith aSizeLimit, or under pressure, does not affect them.
Structured errors
API failures surface as LicensingApiException carrying the server's error contract: a
machine-readable Code (PascalCase, e.g. ValidationError, AuthorizationDenied), the
CorrelationId to quote when contacting support, the HTTP StatusCode, structured
Details, and the server's Retry-After hint when backpressure applies. Branch on Code,
not on message text.
Learn more
- Documentation: https://docs.monaiq.com
- Getting started: https://docs.monaiq.com/integrate/
- .NET quickstart: https://docs.monaiq.com/integrate/dotnet/
- Support: https://docs.monaiq.com/resources/legal-and-support/
© Sidub Inc. Proprietary license — see the LICENSE file.
| 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
- Microsoft.Extensions.Caching.Memory (>= 10.0.12)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Http (>= 10.0.12)
- Sidub.Licensing (>= 2.2.0)
- Sidub.Platform.Authentication.Gremlin (>= 1.11.4)
- Sidub.Platform.Cryptography.AzureKeyVault (>= 1.11.4)
- Sidub.Platform.Storage (>= 1.11.4)
- Sidub.Platform.Storage.Gremlin (>= 1.11.4)
- Sidub.Platform.Storage.Http (>= 1.11.4)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Sidub.Licensing.Client:
| Package | Downloads |
|---|---|
|
Docuoria
Deterministic, local-first PDF data extraction pipeline. Template-driven extract / classify / dry-run / execute engine with a CLI script surface suitable for AI agent automation. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.2.0 | 41 | 10/8/2026 |
| 2.1.0 | 87 | 10/3/2026 |
| 2.0.10 | 243 | 9/10/2026 |
| 1.6.13 | 121 | 9/3/2026 |
| 1.6.7 | 111 | 8/31/2026 |
| 1.6.0 | 119 | 8/30/2026 |
| 1.5.170 | 115 | 8/19/2026 |
| 1.5.87 | 109 | 8/9/2026 |
| 1.5.85 | 118 | 8/8/2026 |
| 1.5.74 | 128 | 8/3/2026 |
| 1.5.73 | 121 | 8/3/2026 |
| 1.5.72 | 119 | 8/3/2026 |
| 1.5.59 | 127 | 8/2/2026 |
| 1.5.57 | 121 | 8/1/2026 |
| 1.5.55 | 124 | 7/31/2026 |
| 1.5.51 | 124 | 7/31/2026 |
| 1.5.48 | 119 | 7/30/2026 |
| 1.5.1 | 172 | 3/24/2026 |
| 1.4.0 | 129 | 3/21/2026 |
| 1.3.44 | 128 | 3/17/2026 |