fix(server): charge an install key use when a device is accepted - #7069
Conversation
|
Claude finished @gustavosbarreto's task in 4m 17s —— View job Code Review CompleteReviewed 5 files across code quality, security, testing, language patterns, and architecture — no new issues found. The code looks good as-is. A few notes from the analysis, for the record (nothing blocking):
To request another review round, comment |
|
Two axes reviewed against the repo rules and #7068. Core fix is right; these need addressing: Blocking
Should fix
Test gaps
|
557f34f to
527f3cc
Compare
used_times was incremented only where the server decided the admission itself during registration, so accepting from the review queue never spent a use and usage_limit had no effect on a manual-mode key. The charge moves into the device status transaction, which owns the transition to accepted. The store's increment is itself the limit check (it updates under usage_limit = 0 OR used_times < usage_limit), so an exhausted key surfaces as ErrInstallKeyExhausted and the accept fails rather than silently doing nothing. Running inside the transaction also rolls the use back when the status write fails, which is why the enrollment paths and the webhook callback no longer reserve and release it by hand, and why InstallKeyDecrementUsage goes away with them. used_times is not backfilled. An installation holding a manual key whose accepted devices already outnumber usage_limit keeps admitting until new accepts catch up with the limit. Backfilling from the accepted count would be truthful and would stop those keys at once, at the cost of turning an upgrade into a provisioning stop for whoever relied on the limit not working. Fixes: #7068
527f3cc to
9020af8
Compare
|
Addressed what this PR made, pushed. Three findings I'm leaving out, with reasons. Dead reserve/release protocol — right, I made it dead.
No-backfill decision — moved into the commit message, where Test gap on the issue's reproduction — agreed, and the e2e seam was the right call: 403 copy in the console — out of scope here. The finding is real:
Pairing not double-charging — covered behaviour this PR does not alter. |
|
/review |
|
Claude finished @gustavosbarreto's task in 3m 17s —— View job Code Review CompleteReviewed 9 files across code quality, security, testing, language patterns, and architecture — no new issues found. The code looks good as-is. A few notes from the analysis, for the record (nothing blocking):
To request another review round, comment |
An install key's
used_timeswas incremented only where the server decided the admission by itself during a registration request: the automatic mode, an allowlist match, a webhook answering accept, the deferred webhook callback, and the reconcile that runs on a later agent phone-home. Accepting a device from the review queue goes throughUpdateDeviceStatus, which stamped the verdict on the history event and left the counter alone.The consequence was that
usage_limithad no effect on a key inmanualmode. A single-use manual key admitted devices forever, becauseisOverused()readsused_timesand that never grew. The namespace'slegacykey is created in manual mode, so keyless tenant-only registration was on that path too.The charge now lives in the device status transaction, which is the one place that owns a device becoming accepted. Every path that accepts a device gets it, whoever made the decision. The store's increment was already the limit check (
WHERE usage_limit = 0 OR used_times < usage_limit), so an exhausted key surfaces asErrInstallKeyExhaustedand the accept fails with 403 rather than silently doing nothing. Because the charge runs inside the transaction, a failed status write rolls the use back on its own, so the enrollment paths and the webhook callback no longer reserve and release it by hand.Automatic enrollment behaviour is unchanged: an exhausted key still leaves the device pending, logged, since no one is waiting on that response.
Note for reviewers
Existing installations may hold manual keys whose accepted device count already exceeds
usage_limit. This change does not backfillused_times, so those keys keep admitting devices until the counter catches up with the limit through new accepts. Backfilling from the accepted device count would be truthful and would block them immediately, at the cost of turning an upgrade into a provisioning stop for whoever relied on the limit not working.Closes #7068