Licensing

Capabilities, license tiers, and the license service

This page is the runtime reference for how a license shapes Doconut's behavior: what each license state allows, how capabilities are granted, and how to inspect all of it from code. (For loading a license, see Getting Started → License Setup.)

License states and what they do

StateOpens documents?WatermarkCapabilities
Full (paid) license (this build was released before its subscription date)YesNoneThe ones purchased (Has* flags)
Temporary license (Developer/NFR — the evaluation license, before its expiry date)YesNone on the base viewer; plugin output is watermarkedAll — blanket grant (the eval funnel)
Expired temporary license (past its LicenseExpiry date)YesEvaluation watermarkNone granted
No license file at allYesEvaluation watermarkNone granted
Found but rejected (invalid signature, tampered, blacklisted, or a build released after the license's subscription date)No — LicenseException on every open——

A paid license whose subscription date has passed keeps working — clean, with its capabilities — on every build released before that date. Only a build released after it is rejected.

Two consequences worth internalizing:

  • A missing license is not a temporary license. With no license file, pages render watermarked and no capability (search, annotation, plugins) is active. The full-featured, unwatermarked experience requires an actual Temporary license (get one).
  • Only a rejected license blocks opening. An expired Temporary license keeps the app running with the evaluation watermark, and a paid license past its subscription date keeps rendering clean on the builds it covers; but deploying a build released after the license's subscription date is rejected and fails fast with a LicenseException, so you catch it at deploy — not by silently shipping watermarked output. That exception (from OpenDocumentAsync) carries the reason from License.RejectionMessage.

Capabilities

Doconut gates optional functionality by capability. It covers the built-in Search and Annotation features and the opt-in Converter, Dicom and Signer plugins, each granted by your license. The base viewer is not a capability — it is the prerequisite, surfaced as IsViewerLicensed:

csharp
var lic = app.Services.GetRequiredService<IDoconutLicenseService>();
bool viewerLicensed = lic.IsViewerLicensed; // non-TRIAL license present AND covering this build's version

The single gate the whole pipeline uses is IsCapabilityGranted:

csharp
var lic = app.Services.GetRequiredService<IDoconutLicenseService>();
bool searchGranted = lic.IsCapabilityGranted(LicenseCapability.Search);

Its logic, verbatim from the implementation: an active (non-expired) Temporary license grants every capability — including capabilities that don't have a dedicated purchase flag yet; a real purchased license grants exactly its Has* flags and is default-deny for anything unmapped. (A legacy TRIAL-type file is not an entitlement — it is treated as unlicensed: watermarked, nothing granted.)

Temporary licenses

Temporary (Developer/NFR) licenses are marked by an ExpiryNote tag and carry a hard expiry date:

MemberMeaning
IsTemporaryAn ExpiryNote tag is present (detection is by tag presence — never match the note's text, it's dynamic)
ExpiryNoteInformational text embedded in the license
LicenseExpiryThe hard expiry date (DateTime?)

Expiry semantics differ by tier: a temporary license expires on its LicenseExpiry date only (its subscription window is ignored; a temporary license missing the date is treated as expired, fail-safe). Every other license expires on its subscription/update-window date; for a paid license, IsExpired only reports that this date has passed — builds released before it stay clean and keep their capabilities.

IDoconutLicenseService reference

Inject IDoconutLicenseService (namespace Doconut.Core.Interfaces; registered as a singleton — the license is parsed once at startup):

MemberMeaning
LicenseFull DoconutLicenseInfo snapshot (below)
IsTrialA legacy TRIAL-type file (treated as unlicensed — not an entitlement)
IsTemporary / ExpiryNote / LicenseExpiryTemporary-license markers
IsExpiredExpired per the tier rules above
IsVersionValidThis build falls within the license's support window
IsViewerLicensedNon-TRIAL file found and version covered — base prerequisite
IsCapabilityGranted(cap)The single capability gate
HasSearch HasAnnotation HasEditor HasConverter HasDicom HasDocumentTools HasSigner HasRedactor HasComparer HasForms HasProtector HasCompressorOne flag per capability: purchased by a license that covers this build, or granted by an active Temporary license. IsCapabilityGranted answers the same question by capability
IsValidForDomain(host)Host coverage; V4 licenses use the wildcard *.*
LicenseRejectionReasonNon-empty ⇒ opening is blocked with this message

DoconutLicenseInfo exposes the display-friendly snapshot for admin panels: Owner, LicenseType, Domain, IsLicenseFileFound, IsVersionValid, IsTrial, IsExpired, the Has* flags, the temporary-license fields, and RejectionMessage.

csharp
// Feature-flag your UI from the same source of truth the SDK uses
app.MapGet("/api/features", (IDoconutLicenseService lic) => Results.Ok(new
{
    viewer     = lic.IsViewerLicensed || lic.IsTemporary,
    search     = lic.IsCapabilityGranted(LicenseCapability.Search),
    annotation = lic.IsCapabilityGranted(LicenseCapability.Annotation),
    conversion = lic.HasConverter,
    dicom      = lic.HasDicom,
}));

Health check

AddDoconut() registers a "doconut" health check that reports license/expiry state through the standard ASP.NET Core mechanism:

csharp
app.MapHealthChecks("/health");

Wire it into your monitoring and an expiring subscription becomes an alert instead of a support ticket. The check reports Unhealthy when the license is rejected or IsExpired is true; for a paid license, that means the subscription date has passed and a package released after it will be rejected — the current build keeps rendering clean.

Version upgrades

A license covers a version window. Updating the NuGet package to a build newer than that window makes the license invalid for this build: IsVersionValid turns false and opening is blocked with a LicenseException (the reason is in License.RejectionMessage) — deliberately fail-fast, so a stale or unlicensed build is caught at deploy rather than silently watermarking production output. Renew the subscription, replace the .lic, and restart the application so AddDoconut() loads the new license.

Was this page helpful?