License Setup

Where Doconut looks for your license file

Without a license, Doconut still renders documents — every page just carries an evaluation watermark. This page covers the four ways to supply a license, and the exact precedence when more than one is set.

Four ways to supply a license

There are four: three explicit sources on DoconutOptions — a stream, raw content, or a file path — plus automatic discovery when none of them is set. When more than one is set, the precedence is exact:

LicenseStream beats LicenseContent beats LicensePath beats auto-search.

By path

LicensePath is passed to File.Exists exactly as given. A relative path resolves against the process's current working directory — not your project folder, and not the folder Program.cs lives in. If the path does not resolve, Doconut does not throw and does not fall back to auto-search — it simply loads no license and the viewer watermarks. Auto-search only runs when none of LicensePath, LicenseContent, or LicenseStream is set.

Prefer an absolute path (for example built from IWebHostEnvironment.WebRootPath or AppContext.BaseDirectory), or skip LicensePath entirely and rely on automatic discovery below.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = Path.Combine(AppContext.BaseDirectory, "Doconut.lic");
});

By stream

LicenseStream is read once at startup — useful when the license comes from a secret store rather than a file on disk.

csharp
// Precedence: LicenseStream > LicenseContent > LicensePath > auto-search.
builder.Services.AddDoconut(options =>
{
    options.LicenseStream = licenseStream;
});

By content

LicenseContent accepts the license text itself — from an environment variable, a database, or a secret manager:

csharp
// License XML from a database, environment variable, or secret manager —
// no file on disk. Beaten only by LicenseStream.
builder.Services.AddDoconut(options =>
{
    options.LicenseContent = Environment.GetEnvironmentVariable("DOCONUT_LICENSE") ?? "";
});

Automatic discovery

Configure none of the three explicit sources, and Doconut searches for the license itself:

csharp
// Configure nothing, and Doconut searches for the license itself:
//   1. {CurrentDirectory}/wwwroot
//   2. {CurrentDirectory}/wwwroot/lib
//   3. AppContext.BaseDirectory  (the build output folder)
// It looks for Doconut.lic first, then Doconut.Viewer.lic plus any per-plugin
// Doconut.Viewer.<Capability>.lic files alongside it.
builder.Services.AddDoconut();

The probe directories, in order, and the filenames looked for in each:

text
1. {CurrentDirectory}/wwwroot
2. {CurrentDirectory}/wwwroot/lib
3. AppContext.BaseDirectory

Filenames (checked in each directory above, in order):
  Doconut.lic                          — all-in-one license, checked first
  Doconut.Viewer.lic                   — base viewer license
  Doconut.Viewer.<Capability>.lic      — per-plugin license, alongside Doconut.Viewer.lic

Copy the license to your output folder

LicensePath, and auto-search's AppContext.BaseDirectory probe, both need the .lic file to exist next to the built app — not just in your source wwwroot. The SDK's own test app copies it on every build with this MSBuild target:

xml
<Target Name="CopyLicensesToOutput" AfterTargets="Build">
  <ItemGroup>
    <DoconutLicenseFiles Include="$(MSBuildProjectDirectory)\wwwroot\*.lic" />
  </ItemGroup>
  <Copy SourceFiles="@(DoconutLicenseFiles)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" />
</Target>

Keep .lic files out of source control — deploy them alongside the app, or inject the license through LicenseContent or LicenseStream from your secret store.

What happens without a license

A missing license does not throw. AddDoconut() succeeds, the app starts, and the viewer runs — but every page carries an evaluation watermark and no optional capability is granted.

A license file that is found but rejected is different. An invalid signature, tampering, blacklisting, or a build outside the license's version window causes OpenDocumentAsync to throw LicenseException with License.RejectionMessage. A calendar-expired license that has no rejection message continues in watermarked mode.

Plugins need capabilities

Registering a plugin with no corresponding entitlement is different: for a missing license, a legacy TRIAL file, or a paid license without that capability, AddDoconut() throws InvalidOperationException, so the app does not start. For example, registering the Converter plugin without a license that grants Converter:

text
InvalidOperationException: The Converter plugin is registered via AddPlugin but no active
license grants Converter. Remove the AddPlugin<...>() call or install a license (or
trial/demo) that includes Converter.

The message tells you the fix directly: either remove the options.AddPlugin<...>() call for that plugin, or install a paid license or an active Temporary/Demo (NFR) license that grants the capability. Temporary registrations are allowed to survive their expiry date so an already configured app can degrade at runtime instead of crashing during restart; once expired, their capabilities are still revoked.

Verify the loaded license

Use IDoconutLicenseService, the same source of truth used by the SDK, to expose an authenticated diagnostic endpoint or to drive feature flags. Do not return license contents or keys.

csharp
app.MapGet("/api/doconut/license", (IDoconutLicenseService license) => Results.Ok(new
{
    viewer = license.IsViewerLicensed || license.IsTemporary,
    temporary = license.IsTemporary,
    search = license.IsCapabilityGranted(LicenseCapability.Search),
    annotation = license.IsCapabilityGranted(LicenseCapability.Annotation),
    converter = license.HasConverter,
    dicom = license.HasDicom
}));

The license is read during AddDoconut() registration. ResetLicense is currently a compatibility property with no active reload path, so replacing a license file requires restarting the application.

Troubleshooting matrix

SymptomLikely causeCheck
Viewer works but every page is watermarkedNo license was loaded, or the license is calendar-expiredResolve IDoconutLicenseService; verify the output directory and process working directory
AddDoconut() throws for a pluginThe license does not grant that plugin capabilityCheck IsCapabilityGranted(...) and remove registrations you did not purchase
A configured relative path works locally but not in IIS/containerThe process working directory changedUse AppContext.BaseDirectory or an absolute path
Replaced .lic file has no effectThe singleton license service was already createdRestart the application
OpenDocumentAsync throws LicenseExceptionSignature, domain, version window, blacklist, or plugin runtime gate rejected the licenseRead the exception/rejection message without exposing it to untrusted clients

Next steps

  • Licensing — capabilities, license tiers, and verifying what was loaded at runtime.
  • Troubleshooting — watermarks, rejected licenses, and capability errors.

Was this page helpful?