Pular para o conteúdo principal

Configuration

Every appsettings.json key for Lacuna Bulk Signer — type, default, environment-variable override, and whether it is required.

Configuration sources, in precedence order

Later sources override earlier ones:

  1. appsettings.json (built-in defaults)
  2. appsettings.{Environment}.json (e.g. appsettings.Production.json)
  3. appsettings.json + appsettings.{Environment}.json found under BULK_SIGNER_CONFIG_DIR
  4. Environment variables (Section__Sub__Key)

The BULK_SIGNER_CONFIG_DIR step is what lets the binary live in a read-only install location (/opt/bulksigner, %ProgramFiles%\Lacuna\BulkSigner) while the operator-edited production config lives elsewhere (/etc/bulksigner, %ProgramData%\Lacuna\BulkSigner\config). The install scripts set this variable; if you change install paths, update the variable in lockstep.

Environment-variable mapping follows the ASP.NET Core rule: a JSON key like Signing:Certificate:Pfx:Password maps to Signing__Certificate__Pfx__Password (double underscore is the separator).

Markers used in the tables

MarkerMeaning
REQUIREDThe service refuses to start (or signing refuses to run) without a non-empty value.
SECRETSensitive — prefer the environment-variable override over a value committed to a file.

Logging / Logging:File

Standard Microsoft.Extensions.Logging knobs (Logging:LogLevel:*) work as usual; the Logging:File block configures the file sink.

KeyTypeDefaultEnv overrideNotes
Logging:LogLevel:DefaultstringInformationLogging__LogLevel__DefaultStandard logging level.
Logging:LogLevel:Microsoft.AspNetCorestringWarningLogging__LogLevel__Microsoft.AspNetCoreLowers framework chatter.
Logging:File:Pathstringdata/logs/bulksigner-.logLogging__File__PathREQUIRED. File-sink path template. The trailing - before .log plus daily rolling produces bulksigner-yyyyMMdd.log.
Logging:File:RollingIntervalstringDayLogging__File__RollingIntervalOne of Day, Hour, Minute, Infinite.
Logging:File:FileSizeLimitByteslong50000000Logging__File__FileSizeLimitBytesPer-file cap; the sink rolls to …_001.log past this. Bounds: 64 KB to 10 GB.
Logging:File:RetainedFileCountLimitint14Logging__File__RetainedFileCountLimitOlder files are deleted as rotation advances. Bounds: 1–365.
Logging:File:MinimumLevelstringInformationLogging__File__MinimumLevelOne of Verbose, Debug, Information, Warning, Error, Fatal.
Logging:File:WriteToConsolebooltrueLogging__File__WriteToConsoleWhen true, also writes to stdout. The same redacting formatter runs on both sinks.

ConnectionStrings

KeyTypeDefaultEnv overrideNotes
ConnectionStrings:DefaultstringData Source=data/db/bulksigner.dbConnectionStrings__DefaultREQUIRED. SQLite connection string. Point at a path under Storage:Root so one mount covers both data and DB.

Signing

Validation fails fast at startup if any required key is missing or invalid.

KeyTypeDefaultEnv overrideNotes
Signing:Licensestring""Signing__LicenseREQUIRED, SECRET. Lacuna PKI SDK license string (base64). Environment-variable form preferred.
Signing:Certificate:SourceenumPfxSigning__Certificate__SourceREQUIRED. One of Pfx, Pkcs11, WindowsStore. Only the matching subtree below is consulted.

Signing:Certificate:Pfx — when Source = Pfx

KeyTypeDefaultEnv overrideNotes
Signing:Certificate:Pfx:Pathstring""Signing__Certificate__Pfx__PathREQUIRED. Absolute path to the .pfx/.p12 file.
Signing:Certificate:Pfx:Passwordstring""Signing__Certificate__Pfx__PasswordSECRET. Empty string is allowed for passwordless test fixtures. Prefer the env-var form.

Signing:Certificate:Pkcs11 — when Source = Pkcs11

KeyTypeDefaultEnv overrideNotes
Signing:Certificate:Pkcs11:ModulePathstring""Signing__Certificate__Pkcs11__ModulePathREQUIRED. Absolute path to the vendor PKCS#11 driver (.so/.dll/.dylib).
Signing:Certificate:Pkcs11:Thumbprintstring""Signing__Certificate__Pkcs11__ThumbprintREQUIRED. SHA-1 thumbprint (hex, no spaces) of the signing cert on the token. Required even when the token holds a single identity.
Signing:Certificate:Pkcs11:PinEnvVarstringBULK_SIGNER_PKCS11_PINSigning__Certificate__Pkcs11__PinEnvVarName of the env var that supplies the PIN. The validator refuses to start if a literal Pin key appears under Pkcs11.

Signing:Certificate:WindowsStore — when Source = WindowsStore

Windows-only. The validator refuses this source on non-Windows hosts at startup.

KeyTypeDefaultEnv overrideNotes
Signing:Certificate:WindowsStore:StoreLocationstringCurrentUserSigning__Certificate__WindowsStore__StoreLocationCurrentUser or LocalMachine. Use LocalMachine when the cert was imported machine-wide; the service account does not see the operator's CurrentUser store.
Signing:Certificate:WindowsStore:StoreNamestringMySigning__Certificate__WindowsStore__StoreNameLogical store name. My is the personal store.
Signing:Certificate:WindowsStore:Thumbprintstring""Signing__Certificate__WindowsStore__ThumbprintREQUIRED. SHA-1 thumbprint (hex, no spaces).

See Certificates for thumbprint discovery commands and a deeper walkthrough.

Signing:Profiles[] — per-folder signing profiles

A signing profile bundles every per-folder decision (format, certificate, verify, encrypt, certificate validation) under a name. Watched folders reference the profile by name via Storage:Inputs[].Profile. Two configuration modes are supported:

  • Legacy mode (default — Signing:Profiles[] omitted or empty). The service synthesises one profile named default from the existing Signing:Certificate block. No config changes are needed for a simple single-certificate install.
  • Profile mode (declare Signing:Profiles[]). Each entry is a named profile with its own certificate and posture. Signing:Certificate is ignored. Every entry validates as if it were the global certificate block — the same Pfx / Pkcs11 / WindowsStore rules apply per profile.
KeyTypeDefaultEnv overrideNotes
Signing:Profiles[].Namestringn/aSigning__Profiles__0__NameREQUIRED. Same regex as folder names: ^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$. Unique across the list. The name is referenced from Storage:Inputs[].Profile and surfaces in metric labels, dashboard chips, and audit messages.
Signing:Profiles[].Formatenumn/aSigning__Profiles__0__FormatREQUIRED for every operator-declared profile: Pades, Cades, Xades, or XmlNFe. Only the synthesised legacy default may leave this unset — in which case the format is detected per file by extension.
Signing:Profiles[].MethodenumLocalSigning__Profiles__0__MethodLocal (sign with the configured local certificate) or LacunaSigner (dispatch to Lacuna Signer for a human participant). See Lacuna Signer integration.
Signing:Profiles[].VerifybooltrueSigning__Profiles__0__VerifyWhen false, the worker skips the post-sign verification round-trip. The startup banner emits a warning so the low-trust posture is operator-visible.
Signing:Profiles[].EncryptboolfalseSigning__Profiles__0__EncryptWhen true, the worker AES-256-GCM-encrypts the signed output. Requires Encryption:Enabled = true (the validator refuses the broken combination at startup).
Signing:Profiles[].ValidateCertificatebooltrueSigning__Profiles__0__ValidateCertificateWhen false, the worker skips the pre-sign certificate chain / revocation check. The startup banner emits a warning. Must be false when Method = LacunaSigner — there is no local cert to validate.
Signing:Profiles[].Certificate.*nestedn/aSigning__Profiles__0__Certificate__…REQUIRED when Method = Local. Same shape as the global Signing:Certificate block. Each profile loads its own certificate at boot; misconfiguration on any profile fails startup with an aggregated error. Refused when Method = LacunaSigner.
Signing:Profiles[].Signer.Namestringn/aSigning__Profiles__0__Signer__NameREQUIRED when Method = LacunaSigner. Display name of the participant Lacuna Signer will send the document to.
Signing:Profiles[].Signer.Emailstringn/aSigning__Profiles__0__Signer__EmailREQUIRED when Method = LacunaSigner. Participant's email address — must contain @.
Signing:Profiles[].Signer.Identifierstringn/aSigning__Profiles__0__Signer__IdentifierREQUIRED when Method = LacunaSigner. Participant's national identifier (CPF in Brazil).

Storage:Inputs[].Profile — per-folder routing

KeyTypeDefaultEnv overrideNotes
Storage:Inputs[].Profilestring?null (→ "default")Storage__Inputs__0__ProfileOptional. References a Signing:Profiles[].Name. Null or empty falls back to the default profile. Unknown names fail validation at startup.

Example: two profiles routed by folder

"Signing": {
"License": "<env-var>",
"Profiles": [
{
"Name": "nfe",
"Format": "Xades",
"Verify": true,
"Encrypt": false,
"ValidateCertificate": true,
"Certificate": {
"Source": "Pkcs11",
"Pkcs11": { "ModulePath": "/usr/lib/x86_64-linux-gnu/pkcs11/libsofthsm2.so", "Thumbprint": "...", "PinEnvVar": "BULK_SIGNER_PKCS11_PIN" }
}
},
{
"Name": "contracts",
"Format": "Pades",
"Verify": true,
"Encrypt": true,
"ValidateCertificate": true,
"Certificate": {
"Source": "Pfx",
"Pfx": { "Path": "/etc/bulksigner/contracts.pfx", "Password": "" }
}
}
]
},
"Storage": {
"Root": "/var/lib/bulksigner",
"Inputs": [
{ "Name": "nfe-incoming", "Path": "/var/lib/bulksigner/input-nfe", "Profile": "nfe" },
{ "Name": "contracts-incoming", "Path": "/var/lib/bulksigner/input-contracts", "Profile": "contracts" }
]
}

The startup banner lists every resolved profile with its format, certificate source, and verify/encrypt/validate-cert flags. Profiles with Verify=false or ValidateCertificate=false emit additional warnings so the low-trust posture is captured in durable logs.

Signer — Lacuna Signer connection

One Lacuna Signer tenant per host — the endpoint and API key are global, not per-profile. The validator is self-gating: it only enforces Endpoint + ApiKey when at least one Signing:Profiles[] entry has Method = LacunaSigner. Local-only deployments don't need to configure any of this. See Lacuna Signer integration.

KeyTypeDefaultEnv overrideNotes
Signer:Endpointstring""Signer__EndpointREQUIRED when any profile uses LacunaSigner. Base URL for the Lacuna Signer instance. Cloud default: https://signer.lacunasoftware.com. On-prem deployments point at the customer's instance.
Signer:ApiKeystring""Signer__ApiKeyREQUIRED, SECRET when any profile uses LacunaSigner. Expected shape: application-id|secret. The literal value is scrubbed from logs.
Signer:PollIntervalSecondsint30Signer__PollIntervalSecondsHow often the poll worker walks every AwaitingSigner row. Bounds: 1–3600.
Signer:TimeoutHoursint168 (7 days)Signer__TimeoutHoursHow long a job may sit in AwaitingSigner before it is failed with code = signer.timeout. Bounds: 1–8760.
Signer:MaxConsecutiveApiFailuresint5Signer__MaxConsecutiveApiFailuresPer-document consecutive transient-error budget before the poll worker gives up on that document. In-memory counter — restart resets it.

Encryption

Off by default. The validator runs only when Enabled = true.

KeyTypeDefaultEnv overrideNotes
Encryption:EnabledboolfalseEncryption__EnabledMaster switch. When true, the worker AES-256-GCM-encrypts the signed artifact between verify and promote.
Encryption:Passwordstring""Encryption__PasswordSECRET. PBKDF2 password. Allowed in config (use appsettings.Production.json, which is gitignored) but env-var form is preferred.
Encryption:PasswordEnvVarstringBULK_SIGNER_ENCRYPTION_PASSWORDEncryption__PasswordEnvVarName of the env var that supplies the password. If non-empty at boot, it overrides Encryption:Password.
Encryption:Saltstring""Encryption__SaltREQUIRED when Enabled = true. Base64-encoded PBKDF2 salt; must decode to at least 16 bytes. Salts are not secret. Changing the salt invalidates every prior envelope.
Encryption:Iterationsint600000Encryption__IterationsPBKDF2-HMAC-SHA256 iteration count. Rejected below 10000.
perigo

Password loss is unrecoverable — there is no server-side decrypt endpoint and no escrow. See Encryption.

Auth

KeyTypeDefaultEnv overrideNotes
Auth:ApiKeystring""Auth__ApiKeyREQUIRED, SECRET. Static API key, minimum 16 characters. Sent in the X-API-Key header by programmatic clients; pasted at /login by operators to receive a cookie.
Auth:CookieNamestringlbs-authAuth__CookieNameCookie name issued by /api/auth/login. SameSite=Strict, HttpOnly, secure when the request was HTTPS.
Auth:ApiKeyHeaderstringX-API-KeyAuth__ApiKeyHeaderHTTP header the API-key scheme reads. Rename only if a reverse-proxy convention forces it.

See Security for API-key rotation and cookie session lifetime.

Storage

KeyTypeDefaultEnv overrideNotes
Storage:RootstringdataStorage__RootREQUIRED. Root under which processing/, output/, error/, db/, logs/ are created. Override per target — /var/lib/bulksigner on Linux, C:\ProgramData\Lacuna\BulkSigner\data on Windows, /var/lib/bulksigner in Docker.
Storage:Inputs[]array of {Name, Path, IgnoredExtensions?, IgnoredPrefixes?, Profile?}[{Name="default", Path="{Root}/input"}]Storage__Inputs__0__Name, Storage__Inputs__0__Path, …One or more watched input folders. Jobs are tagged with the folder's Name and resolved Profile. See below for validation rules.

Storage:Inputs[] — validation rules (enforced at startup)

  • Name must match ^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$ and be unique across the list — lowercase letters, digits, and internal hyphens only, 1–40 characters, starting and ending with an alphanumeric. Names appear in URL query strings, metric labels, and the dashboard UI.
  • Path must be non-empty and resolve to a directory that is not the same as and not a sub-/super-directory of any other entry's path. Overlapping folders would produce double-enqueues and ambiguous attribution.
  • Soft cap: 16 entries. Higher counts inflate metric cardinality and the Input page beyond useful density.
  • When Storage:Inputs is omitted entirely, the service creates one folder named default at {Storage:Root}/input.

Storage:Inputs[].IgnoredExtensions / Storage:Inputs[].IgnoredPrefixes (per-folder)

Optional arrays. The effective ignore list is the union of the global WatchedFolder:IgnoredExtensions / WatchedFolder:IgnoredPrefixes baseline and the per-folder additions. Per-folder lists add to the baseline; they cannot un-ignore something the global list already filters. Example: with the default baseline (.tmp, .part, .crdownload, .swp), a folder declaring IgnoredExtensions: [".bak"] filters .bak and .tmp etc.

Example: two folders, one with an extra ignore rule

"Storage": {
"Root": "/var/lib/bulksigner",
"Inputs": [
{ "Name": "default", "Path": "/var/lib/bulksigner/input" },
{
"Name": "legal",
"Path": "/mnt/legal/incoming",
"IgnoredExtensions": [".bak"]
}
]
}

Pipeline

KeyTypeDefaultEnv overrideNotes
Pipeline:PollIntervalSecondsint2Pipeline__PollIntervalSecondsHow often the worker polls the queue when idle. Lower = faster pickup, more SQLite reads. Bounds: 1–3600.
Pipeline:MaxConcurrencyint1Pipeline__MaxConcurrencyUpper bound on concurrent in-flight jobs. Default 1 is sequential. Increase for throughput on PFX-backed deployments. Bounds: 1–32. PKCS#11 / WindowsStore: keep at 1 unless the token / CSP allows concurrent sessions — see Certificates.

WatchedFolder

The stability detector guards against picking up a file that is still being written — the watcher waits until size and last-write-time stay identical across StabilityRequiredSamples consecutive polls.

KeyTypeDefaultEnv overrideNotes
WatchedFolder:StabilityPollIntervalMsint500WatchedFolder__StabilityPollIntervalMsInterval between stability checks. Bounds: 50–10000.
WatchedFolder:StabilityRequiredSamplesint3WatchedFolder__StabilityRequiredSamplesConsecutive identical samples needed before enqueue. Bounds: 1–100.
WatchedFolder:StabilityTimeoutSecondsint60WatchedFolder__StabilityTimeoutSecondsMaximum wait before giving up on a never-stable file. Bounds: 1–3600.
WatchedFolder:IgnoredExtensionsarray[".tmp", ".part", ".crdownload", ".swp"]n/a (use config)File extensions the watcher ignores entirely.
WatchedFolder:IgnoredPrefixesarray[".", "~$"]n/a (use config)File-name prefixes the watcher ignores (dotfiles, Office lock files).

Upload

KeyTypeDefaultEnv overrideNotes
Upload:MaxByteslong104857600 (100 MiB)Upload__MaxBytesHard cap on the POST /api/files request body. Raise for scan-heavy PDFs. Minimum 1024.

Dashboard

KeyTypeDefaultEnv overrideNotes
Dashboard:PollIntervalSecondsint5Dashboard__PollIntervalSecondsServer-side refresh tick for live dashboard pages. Bounds: 1–60.

Metrics

KeyTypeDefaultEnv overrideNotes
Metrics:RequireApiKeybooltrueMetrics__RequireApiKeyWhen true, /api/metrics requires API-key or cookie auth. Set false only if your Prometheus scraper sits inside the trust boundary and the network is locked down.

See REST API for the full inventory of metrics instruments.

RateLimiting

Per-IP fixed-window policies.

KeyTypeDefaultEnv overrideNotes
RateLimiting:EnabledbooltrueRateLimiting__EnabledMaster switch. Disable for closed-network installs.
RateLimiting:Upload:PermitsPerWindowint30RateLimiting__Upload__PermitsPerWindowRequests allowed per window for POST /api/files.
RateLimiting:Upload:WindowSecondsint60RateLimiting__Upload__WindowSecondsWindow length for the upload policy.
RateLimiting:Upload:QueueLimitint0RateLimiting__Upload__QueueLimitHow many over-limit requests wait vs. being rejected immediately. 0 = reject immediately.
RateLimiting:Actions:PermitsPerWindowint60RateLimiting__Actions__PermitsPerWindowRequests allowed per window for action endpoints (retry, cancel, rescan, cleanup, pause, resume).
RateLimiting:Actions:WindowSecondsint60RateLimiting__Actions__WindowSecondsWindow length for the actions policy.
RateLimiting:Actions:QueueLimitint0RateLimiting__Actions__QueueLimitQueue depth for the actions policy.

Rate-limited responses carry code = "rate-limited" in the error envelope.

Hosting

KeyTypeDefaultEnv overrideNotes
Hosting:RequireHttpsboolfalseHosting__RequireHttpsGates the in-process HTTPS redirect. false (default) for service and Docker installs that terminate TLS at a reverse proxy. Surfaced in the ready-summary banner as https redirect = on/off.

AllowedHosts

KeyTypeDefaultEnv overrideNotes
AllowedHostsstring*AllowedHostsStandard ASP.NET Core host filtering. Override to a comma-separated list if the install is reverse-proxied with a fixed external host name.

Environment variables that have no JSON counterpart

VariablePurpose
BULK_SIGNER_CONFIG_DIRTells the binary where the production config lives when the binary is in a read-only install location. Set by the install scripts.
BULK_SIGNER_PKCS11_PINThe HSM/token PIN — read at the env-var name configured by Signing:Certificate:Pkcs11:PinEnvVar.
BULK_SIGNER_ENCRYPTION_PASSWORDPBKDF2 password — read at the env-var name configured by Encryption:PasswordEnvVar.
ASPNETCORE_ENVIRONMENTStandard ASP.NET Core environment name (Development, Production). The install scripts set Production.
ASPNETCORE_URLSStandard. The install scripts set http://0.0.0.0:8080.
ASPNETCORE_CONTENTROOTStandard. The Windows install sets it to C:\ProgramData\Lacuna\BulkSigner so file-path resolution lands on operator-writable disk.

Verifying configuration at runtime

The ready-summary banner printed on startup lists the most decision-critical settings (host mode, environment, https redirect, content root, storage root, license fingerprint, cert source, signing policy, encryption status, poll interval, pipeline mode). A mistyped key surfaces there as a default value rather than the value you intended.

/api/ready returns a JSON body describing each probe (DB, input folder, license). A 503 with a body listing the failed probe is the fast feedback loop for config mistakes — see Troubleshooting.


Next: Certificates — choosing and configuring a certificate source. Previous: Installation.