fix(autotls): renewal never fires and renewBufferTime is applied twice - #2933
fix(autotls): renewal never fires and renewBufferTime is applied twice#2933rlve wants to merge 3 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #2933 +/- ##
==========================================
+ Coverage 82.19% 82.42% +0.23%
==========================================
Files 177 177
Lines 32369 32368 -1
Branches 12 11 -1
==========================================
+ Hits 26606 26680 +74
+ Misses 5763 5688 -75
🚀 New features to boost your workflow:
|
10e5b52 to
684aec4
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes AutoTLS certificate renewal scheduling so that renewal triggers correctly using wall-clock time (instead of monotonic Moment), and ensures renewBufferTime is applied exactly once. It also updates the AutoTLS certificate expiry representation from chronos.Moment to times.DateTime and adds targeted tests to cover the previously untested renewal behavior.
Changes:
- Replace monotonic-clock-based expiry handling with wall-clock
DateTimeand computetimeUntilExpiryagainsttimes.now(). - Fix renewal buffer logic so renewal starts at
renewBufferTimebefore expiry (not2 * renewBufferTime). - Add a stub ACME API and new unit tests for renewal/no-renewal behavior; update integration test assertions accordingly.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/stubs/acme_api_stub.nim | Adds an ACME API stub that refuses requests while recording URIs to detect renewal attempts in tests. |
| tests/libp2p/autotls/test_service.nim | Adds unit tests verifying renewal fires near expiry and does not fire outside the buffer window. |
| tests/integration/test_autotls_integration.nim | Updates integration test to use DateTime expiry and wall-clock comparisons. |
| libp2p/autotls/utils.nim | Removes asMoment helper that incorrectly converted wall-clock DateTime into monotonic Moment. |
| libp2p/autotls/service.nim | Changes AutotlsCert.expiry to DateTime and fixes renewal scheduling logic. |
| libp2p/autotls/mockservice.nim | Updates mock certificate creation to use times.now() for DateTime expiry. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Summary
Fixes two independent defects in AutoTLS certificate renewal. Neither had test coverage.
Certificate expiry was compared against the wrong clock.
asMomentbuilt aMomentout of Unix epoch seconds, butMomentis monotonic (chronos defaults toasyncTimer = "mono", and its docs say a Moment's value has no direct meaning). Socert.expiry - Moment.nowevaluated to the epoch value minus the machine's uptime, which is around 56 years on any real host. The renewal branch was never taken and certificates expired in place.asMomentis deleted.ACMECertificateResponse.certificateExpiryis already aDateTime, so the service now stores it unchanged andmanageCertsubtracts wall-clock now.renewBufferTimewas applied twice. The old code computedwaitTime = timeUntilExpiry - renewBufferTimeand then testedwaitTime <= renewBufferTime, which reduces totimeUntilExpiry <= 2 * renewBufferTime. Renewal began two hours before expiry with the default one hour buffer, while the comment directly above promised one hour. The expression collapses toif timeUntilExpiry <= self.config.renewBufferTimeand the comment goes, since the code now states it.Affected Areas
AutoTLS certificate service (
libp2p/autotls/). Certificate renewal scheduling only. No transport, muxer or protocol code is touched.Compatibility & Downstream Validation
No downstream validation was run. AutoTLS is opt-in through
.withAutotls(), andAutotlsCertis not re-exported throughlibp2p.nim, so only projects that importlibp2p/autotls/servicedirectly can see the type change.Impact on Library Users
AutotlsCert.expiryis now atimes.DateTimeinstead of achronos.Moment, andAutotlsCert.newtakes aDateTime. Five call sites exist in this repo and all are updated here.Renewal behavior fires at all now, where before it never did, and it starts at
renewBufferTimebefore expiry rather than at twice that.Risk Assessment
The renewal path has never run in production, so treat it as new code even though it is not. Nodes that previously served a certificate until it expired will now place real ACME orders against a rate-limited CA.
Additional Notes
Tests are in
tests/libp2p/autotls/test_service.nim, with a newtests/stubs/acme_api_stub.nimthat refuses every ACME request and records the URI. Each issuance attempt fails on its first request, so a recorded request is an attempted renewal.The integration test's renewal half passed for the wrong reason. It overwrote
expiryby hand before waiting, which stepped over the clock bug, and its follow-upcertAfter.expiry > Moment.nowwas always true because the value was an epoch number. That assertion is meaningful now.