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 (within its subscription/update window)YesNoneThe ones purchased (Has* flags)
Temporary license (Developer/NFR — the evaluation license, before its expiry date)YesNoneAll — blanket grant (the eval funnel)
Expired (a temporary license past its expiry date, or a paid subscription past its renewal date)YesEvaluation watermarkNone granted
No license file at allYesEvaluation watermarkNone granted
Found but rejected (invalid signature, tampered, blacklisted, or a build newer than the license's version window)No — LicenseException on every open

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. Calendar/subscription expiry degrades to watermarked rendering; but deploying a build newer than the license's version window 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. Today those are the opt-in Converter and DICOM plugins plus the built-in Search and Annotation features, each granted by your license. The base viewer is not a capability — it is the prerequisite, surfaced as IsViewerLicensed:

csharp
bool viewerLicensed = lic.IsViewerLicensed; // non-TRIAL license present AND covering this build's version

The single gate the whole pipeline uses is IsCapabilityGranted:

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

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 HasConverter HasDicomReleased per-capability purchase flags — use IsCapabilityGranted for the temporary-aware answer
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, Email, 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.

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?