Problem
frontend/app/api/license/check/route.ts reads pagesUsed with kv.get then increments with kv.incrby in two separate operations. Concurrent uploads can both pass the limit check before either increments, allowing over-consumption of the page quota.
const used = (await kv.get<number>(key)) ?? 0 // read
if (used + pages > limit) { ... deny ... }
await kv.incrby(key, pages) // write — not atomic with read
Fix
Use a single atomic INCRBY then check if the result exceeds the limit. Roll back if over:
const newUsed = await kv.incrby(key, pages)
if (newUsed > limit) {
await kv.incrby(key, -pages) // undo
return deny(newUsed - pages, limit)
}
return allow(newUsed, limit)
Or use a Lua script for true atomicity (single round-trip, no undo needed).
Priority
Low — only matters under concurrent uploads from the same token. Not an issue at current usage levels.
Problem
frontend/app/api/license/check/route.tsreadspagesUsedwithkv.getthen increments withkv.incrbyin two separate operations. Concurrent uploads can both pass the limit check before either increments, allowing over-consumption of the page quota.Fix
Use a single atomic
INCRBYthen check if the result exceeds the limit. Roll back if over:Or use a Lua script for true atomicity (single round-trip, no undo needed).
Priority
Low — only matters under concurrent uploads from the same token. Not an issue at current usage levels.