License: keep checking in after expiry (60-day window) so late renewals sync without re-ingesting the key #308
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Flagged by Nikola 2026-08-24, root-caused in
internal/license/manager.go. The server already supports late-renewal re-sync (records kept forever; renewal re-signs the SAME license id; check-in 200 delivers the current key viainstallRefreshedKeyLocked). The client throws that away twice:checkinRequiredLocked()requires!m.lic.Expired(now)— the moment the key expires, the instance stops phoning home entirely. A renewal one day later is never picked up; the customer must re-paste the key.checkin()maps EVERY 403 torevoked=true→st.Revoked = true→ check-ins stop permanently. But the server 403s for expired too — so an instance that checks in during a 2-day payment hiccup marks itself revoked forever.Fix
reasonfield on the 403 body (expired/revoked/unknown).checkin(): parse the 403 body; onlyreason == "revoked"(or"unknown") setsst.Revoked.reason == "expired"(and, for old servers, a 403 with no reason when the local key is itself expired) is a new outcome: expired-but-may-renew.checkinRequiredLocked(): also required when the license is expired but withinexpiredRetryWindow = 60 daysofExpiresAtand not revoked — i.e. keep trying (daily retry cadence already exists) for two months after expiry. On a 200, the refreshed key installs and everything resumes with zero admin action.licenseActive), so the extended check-ins change nothing about the seat cap — they only enable self-healing.#admin/licensing): while in the window, show "License expired — checking for renewal until YYYY-MM-DD" instead of a dead-end expired state.Docs handbook licensing page gains the behavior sentence: "If your subscription renews within 60 days of expiry, the instance picks the new key up on its own — you never re-enter it."
Shipped in v0.6.63 (PR #309), live on both dogfoods (
cairn-enc@ .249,cairn-openbao@ .247, both 1/1 with 0 restarts). Window is 180 days per Nikola.Server half deployed first (license-server#24:
reasonon the 403 + cancellation endpoints). End-to-end behavior now: expired license → daily check-ins continue up to 6 months → renewal re-signs the same id → next check-in installs the key, zero admin action.ForceCheckinreports the newErrExpiredinstead of pretending the server is unreachable.Dogfood checklist (light — the real path needs an expired license, which we can't stage without waiting):
#admin/licensingunchanged for a healthy licenseFollow-up parked for the #302 admin polish pass: surface
renewalWatchUntil("License expired — checking for renewal until …") in the licensing panel; the Status API already exposes it.