github fabriziosalmi/certmate v2.39.0
v2.39.0 (the renewal the CA asked for)

2 hours ago

v2.39.0 (the renewal the CA asked for)

One fix and the two things that prove it.

v2.36.0 taught the renewal sweep to ask the CA when to renew, through ACME
Renewal Information (ARI, RFC 9773).
That release asked the right question and then did not act on the answer. This
release makes the answer count, shows it to you, and checks the whole path
against a real CA.

The API contract moves to 2.23, from 2.22. Read it from
X-CertMate-API-Version on any response, or api_contract_version in
/api/health. One MINOR step, written down beside the constant in
modules/core/constants.py; nothing was removed or retyped.

step what grew
2.23 a renewal_info object on GET /api/certificates and GET /api/certificates/<domain>

Read this before upgrading

From v2.36.0 to v2.38.0, ARI never brought a renewal forward

The case ARI exists for is a CA that wants a certificate replaced now,
while it still has weeks left: a mass revocation, a compromised intermediate, a
ruling that shortens everything it issued. In that case the sweep asked the
CA, got "renew now", and handed the renewal to certbot without
--force-renewal. certbot has its own gate: it renews only inside 30 days of
expiry. With 60 days left it answered "not yet due", nothing was renewed, and
the sweep's ari_advanced counter reported a renewal that had not happened.

It was measured rather than inferred, with the real certbot binary against
test certificates: 60, 45 and 31 days left, "not yet due"; 29 days left, it
renews. A renewal the CA asked for is now forced through (#963). Renewals the
threshold asks for are unchanged. ari_advanced now counts renewals that
actually happened, not attempts.

If you run v2.36.0 to v2.38.0 and a CA announced an early replacement in
that period
, those certificates were renewed on the ordinary threshold, not
when the CA asked. Nothing needs doing on upgrade. The next sweep asks again.

Your threshold above 30 days does not do what it says

This is the same certbot gate, on the other path, and it is not changed in
this release. A renewal_threshold_days of 45 has the sweep call a
certificate due at 45 days, then certbot answers "not yet due" every night
until 30 are left. In effect the threshold is min(setting, 30). Making the
setting mean what it says changes when every such certificate renews, so it is
a decision of its own: #966.

step-ca does not publish ARI yet

The v2.36.0 notes and the guide said step-ca serves a renewal window for a
private CA. It does not: step-ca 0.30.2 publishes no renewalInfo, which is
checked against a running instance, and support is still an open request
(smallstep/certificates#2162). On a private step-ca the threshold decides
alone, as before. The guide is corrected in all five languages.


What you can see now

The certificate panel has a "CA renewal window" row. It shows the window
the CA suggested, the instant inside it at which CertMate will renew, and the
CA's explanation link when it gave one. That link is how a mass revocation
tells you why. When there is no window, the row says which absence it is,
because they mean different things:

the row says meaning
The CA does not publish one a fact about the CA; it will not change
The CA did not answer at the last check it publishes one and did not answer; if this lasts, look at the CA
Cannot be asked: no Authority Key Identifier the certificate cannot be named in ARI, common on hand-made private-CA certificates
Not checked yet no sweep has asked, including right after a renewal
Off ari_enabled is false

The API returns the same thing as renewal_info. The timestamps are
RFC 3339 UTC with an explicit Z. They reach browsers, and a timestamp
without an offset is read as local time. explanation_url is kept only when
it is https, and it is checked on read as well as on write, because the record
can also arrive from a restored backup.

None of this asks the CA when you open the dashboard. The sweep keeps what the
CA said in renewal-info.json beside each certificate, and every read comes
from there. A record is shown only for the certificate it was about: after a
renewal the serial changes, and the old window is not shown against the new
certificate.


How it was checked

End to end, against Let's Encrypt staging (tests/test_ari_staging_e2e.py).
The test issues a real certificate through Cloudflare DNS-01. The sweep asks
staging's renewalInfo endpoint, and the stored window is compared with a
direct request to staging. Then the ARI client's clock, and nothing else, is
placed at the instant the sweep chose, and the sweep renews the certificate
through the real certbot, with 89 days left. With the fix reverted, the same
test fails with certbot's "not yet due". It now runs in the real-certificate
gate of every release.

The end-to-end run found a defect the unit tests had passed. The instant
shown was rounded to the second while the sweep compared against it to the
microsecond. A sweep at exactly the instant shown did not renew. The instant
is now a whole second, and what is shown is what is acted on.

On a private CA with the real scheduler, the ordinary renewal path was
re-run on a step-ca instance after the change. Four certificates were checked,
four renewed, none failed, all unforced.

Don't miss a new certmate release

NewReleases is sending notifications on new releases.