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

Normal Search and Annotation use their existing capabilities directly.

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.

이 페이지가 도움이 되었나요?