1.5.45
Released 21 August 2026
New
-
The audit-chain signing key can be set independently of the JWT secret. A new
audit.signing_keysetting (falling back to the JWT secret when unset) lets you separate the tamper-evidence key from the authentication key. See the configuration reference for the break-free migration. -
Renewal information (ARI) now honors the CA's suggested polling cadence. The platform re-checks a certificate's ARI window when the CA's Retry-After says to, rather than on a fixed six-hour timer, and surfaces the CA's optional explanation link on the certificate detail page.
-
A failed revocation now raises a notification. A new critical-severity event, Revocation Failed (
cert.revocation_failed), fires when a revocation attempt fails terminally at the CA — previously the only trace was a failed job and an audit row, easy to miss when the certificate (and possibly a compromised key) remains trusted. The failure also appears on the certificate's timeline. -
Findings can now be resolved manually. A new Resolve action on the Security Findings page closes a finding as fixed; if a later scan re-detects the issue the finding reopens with a note that the fix regressed. This is also the durable closure for CT-log findings, which are never resolved automatically. The All status filter now genuinely lists every status (it previously omitted suppressed and resolved findings).
Improved
- Certificates CertAutoPilot issued are recognized automatically in discovery. After a full scan, a discovered certificate is now linked on its own to the managed certificate that shares its fingerprint — so a certificate you issued here and later rediscovered flips from unmanaged to managed without running the Manage wizard. Deleting the managed certificate unlinks it again.
Changed behaviour
- Every feature is now available in both licensing tiers. LDAP / Active Directory login, mandatory two-factor (OTP) policy, and syslog/SIEM forwarding were previously Enterprise-only; they now work on every installation, with or without a license. Licensing is simplified to two tiers — Free (no license key) and Enterprise (a valid license) — and the only difference between them is how many certificates you can hold: Free allows one certificate, and a valid license raises the limit to the number carried in the license (unlimited when the license does not specify one).
Re-running a finished bulk renew now starts a new run. Previously, submitting a bulk renew for a selection identical to an already-finished one returned the old, finished record — the selection could not actually be re-run until the record expired after 90 days. Requests identical to a run that is still pending or in progress are deduplicated exactly as before.
Fixed
-
The interface no longer shows a previous user's data after a re-login. On logout the cached data of the signed-out user was kept in memory; a different user signing in in the same tab could briefly see the previous user's profile and requests. The cache is now cleared on logout and on session expiry.
-
Creating or deleting a project updates the project switcher immediately. A new project no longer requires a page reload to appear, and deleting the currently-selected project no longer leaves the app stuck loading a deleted project.
-
Filters no longer show "no data" when data exists. Changing a status, severity or type filter on the certificates, findings, requests and notification-history lists now returns to the first page instead of requesting a page number that the filtered result no longer has.
-
A discovery scan now updates the page when it finishes. Triggering a scan polls the run to completion and refreshes the inventory, summary and findings it produced, instead of showing a permanently "running" row and stale numbers until a manual refresh.
-
Executing an approved configuration change refreshes the affected lists. After an approved target, module, path-set, action-set or variable change is executed, the corresponding list reflects it immediately rather than after a delay.
-
Live resource pickers can no longer send a credential to an arbitrary host. The MerlinCDN resource browser now rejects a host that is not the MerlinCDN API, closing a path by which an operator could redirect a stored token to another endpoint.
-
A failed DNS validation no longer loops on stale records. When a DNS-01 order can no longer be validated, the certificate is returned to a pending state with a clear message to regenerate the records, instead of repeatedly retrying the dead order; automated renewals classify a dead order the same way and re-order instead of burning retries.
-
A revocation is no longer abandoned on a transient CA condition. A revocation that hit a temporary "bad nonce" or rate-limit response from the CA is now retried rather than failing terminally.
-
Session cookies can be marked Secure behind a TLS-terminating proxy. A new
server.secure_cookiessetting forces the Secure flag when TLS is terminated upstream. -
Admin password reset now enforces a minimum length (8 characters), matching account creation and self-service password change.
-
The DNS propagation deadline now runs its final check. The last poll was scheduled exactly at the deadline "so a final check is guaranteed", but the deadline test ran before the check — a TXT record that became visible in the final minutes was thrown away and the issuance failed. The final tick now polls before giving up.
-
Distribution schedule windows shorter than the sweep interval are no longer missed. A deferred distribution now arms a wake-up job at the window's start (and re-checks the window when it fires); previously only the periodic sweep could catch a window, so a short maintenance window could be missed indefinitely.
-
Cross-midnight schedule windows now bind to the correct day. A window such as "Monday 22:00–04:00" previously allowed the night before Monday and blocked the Monday-night tail after midnight; the after-midnight portion now belongs to the window's start day.
-
A key-compromise revocation now waits out long renewals. The revoke retry budget (one hour) was shorter than the states it must wait for — a DNS propagation deadline of up to 4 hours or a renewal backoff of up to 24 hours — so the revocation was silently abandoned. The budget now spans those windows with increasing intervals.
-
Module health checks are bounded. A health check is now limited to 100 targets and two minutes overall; previously a large target group pointing at an unresponsive host could pin a server connection for hours per request.
-
Discovery pages scale to large inventories. The findings refresh no longer issues one query per discovered certificate, and the inventory summary is computed once and cached briefly instead of scanning every certificate on each page view.
-
Short-lived certificates no longer renew on every scheduler cycle. When the renewal threshold met or exceeded the certificate's actual lifetime (for example a 6-day certificate under the default 30-day threshold), the next-renewal time landed permanently in the past and the certificate re-renewed continuously until the CA's duplicate limits started failing it. The renewal time is now clamped to the midpoint of the certificate's lifetime.
-
A certificate that expires while awaiting renewal is no longer dropped from automation. A certificate that crossed its expiry date (for example during downtime) was marked expired and then permanently excluded from auto-renewal — and the manual Renew button enqueued a job that failed. Expired certificates are now renewable: automation picks up certificates expired within the last 30 days (older ones would mass-resurrect long-abandoned domains on upgrade day — renew those manually), and the manual Renew button works for any expired certificate.
-
Notification webhook addresses can no longer leak through error messages. When a Slack, Teams or generic webhook delivery failed at the network level, the error text contained the full webhook URL — which for Slack and Teams is itself the credential — and that text was stored in the delivery history and on the channel, both readable by any project member. Transport errors now record only the destination host.
-
Windows file-transfer errors can no longer echo transferred content. A failed chunk write over WinRM could quote fragments of the transferred file (private key or PFX bytes — in the worst case a complete PFX password) into the stored target error. Transfer errors now strip any embedded payload data.
-
A crashed request no longer prints session cookies to the server log. The panic handler previously dumped request headers, including authentication cookies; it now logs only the panic, the route and the stack trace.
-
Bulk actions can no longer stick at "in progress" forever. A crash during submission, or a lost final progress write, left the run showing in-progress indefinitely — and permanently blocked re-running the same selection. Progress finalization is now retried on every reported outcome, a periodic recovery sweep closes out runs whose certificates can no longer report (marking the missing ones as errors), and every run now carries an expiry from creation.
-
Stalled distribution recovery no longer interrupts still-running batches — and now actually runs. The recovery sweep could declare a distribution stalled after 15 quiet minutes and stamp it failed while a long sequential batch (for example ~25 IIS hosts) or a device-lock retry was still legitimately working — discarding the batch's real result when it arrived. The sweep now checks whether any child job is still pending or active and defers. Separately, the stalled-recovery passes were unreachable unless an unrelated stuck record happened to exist at the same time; they now run on every sweep.
-
A worker that lost a job can no longer overwrite the outcome of the run that took it over. When a worker's database connection stalled long enough for its job to be picked up by another worker, the original worker's late status write could resurrect a completed job (running it twice — for notification jobs, a duplicate send), revive a dead job, or mark a still-running job completed. Late writes from a superseded run are now ignored and counted in a new metric.
-
Cancelling a job now tells the truth. Cancelling a pending job raced the worker picking it up: the job could run to completion while the operator was told it was cancelled. "Cancelled" is now guaranteed to mean the job never ran; a job already picked up reports "cancellation requested" and stops at its next checkpoint. The certificate status restore after a cancel now also only happens when the job truly never ran, and never overwrites a status another process just set.
-
Audit log integrity verification now works. The tamper-evidence check compared signatures computed at a higher precision than the database stores, so it reported every entry — including untouched ones — as a chain break. Entries written from this release onward verify correctly, including the oldest surviving entry once retention has removed its predecessor; entries written before this release still show as breaks until they age out of the 30-day audit retention window. The check also no longer reports a false break when two entries were recorded in the same instant by parallel workers — such concurrent writes are now recognized as legitimate, and any chain already flagged for this reason clears on the next verification.
-
A failed revocation is no longer recorded as a success. Several error paths (a rejected request at the CA, a missing account or key, a read error) previously completed the revocation job as successful and wrote a "certificate revoked" audit entry while the certificate remained valid at the CA. These now fail the job loudly, retry when the cause is transient, and audit the failure.
-
Removing a rule from the certificate policy now actually takes effect. Clearing a restriction (allowed key types, naming pattern, SAN rules, the auto-renew requirement) was silently ignored in storage: the API confirmed the change but issuance kept enforcing the old rule. The only workaround was deleting and recreating the policy.
-
Changing a target group's default credential now takes effect. The update was acknowledged but never stored, so distributions kept authenticating with the group's previous credential.
-
Discovery findings no longer count one server as many endpoints. Discovery stores one endpoint row per host, port and SNI name, so a single server probed under many SNI candidates showed up as "Certificate deployed on 13 endpoints" with 13 identical host:port chips. Endpoint counts in finding titles (blast radius, TLS 1.0/1.1, weak cipher, forward secrecy and related) now count distinct host:port servers, and the affected-endpoints list labels each entry with its SNI name so entries are distinguishable.
-
Findings whose condition has cleared now resolve automatically. A certificate-scoped finding (for example TLS 1.0 support, or a blast radius that shrank below the threshold) previously stayed open forever with a stale title once the underlying issue was fixed — auto-resolve only handled certificates that disappeared entirely. The next scan refresh now resolves findings whose condition is no longer detected. A few signals that can briefly misread on a single slow scan — legacy TLS 1.0/1.1 support and blast radius — wait until the issue has stayed undetected for about a day before resolving, so they no longer flicker resolved-and-reopened between scans.
-
CT-log discovery findings no longer vanish. The first scan refresh after a CT-log finding was created silently auto-resolved it — CT alerts disappeared from the default view before anyone reviewed them, and an acknowledgement was wiped. CT findings now stay open until you acknowledge or suppress them.
-
A discovery source's Certificates tab now shows the real endpoint count. A certificate listed under a discovery source showed "Endpoints: 0" even when its Endpoints tab listed several; the count is now populated to match the certificate's detail view.