Skip to content

Configuration

Settings live in appsettings.json, with environment-specific overrides layered on top. Options are bound with validation on start, so an invalid value stops the host at startup rather than surfacing as a confusing failure at first use.

Any key can be overridden by environment variable, using __ as the section separator:

Terminal window
DmsDataAccess__MaxPoolSize=200
RateLimiting__Enabled=false

This page is the guided version. For the exhaustive key-by-key table, see Configuration keys.

Key Consequence if wrong
ConnectionStrings:DefaultConnection Nothing works. Must authenticate as a least-privilege DocsentryAppRole member, not the deployment or schema-owner principal - a startup probe enforces this.
DataProtection:KeyPath Outside Development the host refuses to start. Must be an absolute path outside the content root.
DataProtection:CertificateThumbprint Required for a non-Windows host or a multi-instance deployment. Omitted means Windows DPAPI, which is machine-scoped and cannot be restored onto a different machine.
AllowedHosts Ships as the placeholder docsentry.example.com. Leaving it unchanged makes host filtering reject every request with a bare 400 and no explanatory log entry.

The Data Protection key ring protects every authentication cookie, the “remember this machine” two-factor cookie, stored secrets such as the antivirus API key, and the keys that encrypt every stored document. Reading it is enough to forge a session for any user; losing it without a backup means losing every encrypted document. Treat it as a credential: back it up, restrict access to it, and never place it inside the content root where a misconfigured static file handler could serve it.

"DmsDataAccess": {
"MaxPoolSize": 100,
"CommandTimeoutSeconds": 30,
"DefaultPageSize": 50,
"MaxPageSize": 200
}

MaxPoolSize is the ceiling on concurrent SQL connections and is the single most important capacity lever. MaxPageSize bounds how much a caller can request in one page, which stops a client asking for the whole repository at once. See Performance and tuning.

Storage:RootPath is the root of the file store; TempSubpath, QuarantineSubpath, and BlobSubpath are resolved beneath it. All of these are storage-root-relative - an absolute path in a subpath is rejected at startup.

"Uploads": {
"ChunkSizeBytes": 4194304,
"MaxFileSizeBytes": 104857600,
"SessionLifetime": "02:00:00"
}

Storage:WorkingCopyPath is where preview, OCR and bulk export get a short-lived decrypted copy of a document. Leave it empty for a folder under the system temp directory, or point it at a local directory outside the storage root. It holds plaintext briefly, so keep it out of backups.

That is a 4 MB chunk, a 100 MB per-file ceiling, and a two-hour window to finish a resumable upload. AllowedContentTypes is the global allowlist; per-folder policies layer on top of it. See Upload allowlist.

PreviewOptions bounds generated artefacts at 1600x2000 with a 320-pixel thumbnail edge, stored as WebP. ImageContentTypes, PdfContentType, and OfficeContentTypes decide which renderer handles a given file. SofficePath may be left empty for auto-discovery.

BackgroundJobs holds one block per recurring job, each with at least Enabled, Interval, and MaxItems. Seventeen jobs are configured here; FolderEmailIntake is the only one disabled by default. A disabled job does nothing, even if a run was already queued.

Cadence is a real cost, not a free knob. The configuration file itself records why the permission rebuild job runs every 30 seconds rather than every 5: access-control writes drain the rebuild queue inline within the same transaction, so the job is a safety net rather than the primary path, and a 5-second cadence made the job runner’s own bookkeeping the dominant cost of an otherwise idle system.

See Background jobs for the full table.

"RateLimiting": {
"Enabled": true,
"WindowSeconds": 10,
"AuthenticatedPermitLimit": 200,
"AnonymousPermitLimit": 50,
"AuthenticationWindowSeconds": 300,
"AuthenticationPermitLimit": 100,
"AuthenticationAccountPermitLimit": 10,
"CredentialVerificationPermitLimit": 10
}

Authentication forms use two independent fixed windows: a strict per-account bucket that applies across all source addresses, and a looser per-address bucket sized for shared office egress. The two-bucket design exists so that one user failing to log in repeatedly cannot lock out everyone else behind the same public address.

CredentialVerificationPermitLimit meters signed-in pages that check a password or authenticator code - change password, enable authenticator, delete personal data - per user, across addresses and sessions.

AuthenticationConcurrency separately bounds simultaneous password verification, which is CPU-intensive. Its PermitLimit defaults in code to the processor count and its QueueLimit to four times that; override them only after measuring the host.

"AccountSecurity": {
"MaxFailedAccessAttempts": 5,
"LockoutMinutes": 15,
"MandatoryTwoFactorRoles": [ "Administrator" ],
"SecurityStampValidationMinutes": 2,
"InvitationLifetime": "7.00:00:00"
},
"BreachedPasswords": {
"Source": "None",
"OfflineFilePath": null,
"OnSourceUnavailable": "Permit"
}
  • MandatoryTwoFactorRoles - members of these roles must enrol an authenticator before they can reach anything else.
  • SecurityStampValidationMinutes - how quickly a revoked session stops working (1 to 60).
  • InvitationLifetime - how long an invitation link to set a password stays valid.
  • BreachedPasswords - set Source to OfflineFile with a local Pwned Passwords file to refuse known-breached passwords. See Security hardening.

Accounts are created by administrators; there is no public registration.

The connection string, Email:Password and EmailIntake:Password are credentials. Supply them as environment variables (ConnectionStrings__DefaultConnection, Email__Password) or from a secret store. Outside Development, startup logs a warning if any of them came from a JSON file.

  • ContentIsolation:ContentOrigin and ApplicationOrigin enable the optional cookieless content host. See Security hardening.
  • PublicSite:BaseUrl is the externally reachable address used to build links in email. It falls back to ContentIsolation:ApplicationOrigin.

Security:Csp:Mode switches the Content Security Policy between enforcing and reporting. Security:Hsts controls max-age, subdomain inclusion, and preload.

Security:ForwardedHeaders is the one to get right behind a reverse proxy. Populate KnownProxies or KnownNetworks with your proxy. The handling is fail-closed: with neither configured, forwarded headers are not trusted at all, so a spoofed client address cannot be used to slip past address-based rate limiting. The cost of getting this wrong is that every request appears to come from the proxy.

Workflows:Engine selects Dapper (the default, built in) or Elsa. Elsa requires its deploy script to have been applied; the application fails fast at startup otherwise rather than failing at the first workflow. See Workflow concepts.

Logging:SlowRequestThresholdMs (default 1000) is the boundary above which a request is logged as slow. Serilog handles structured output. Lower the threshold when hunting a latency problem; leave it alone otherwise, because request logging at volume is itself a cost.