Enforce PolicyRule.maxUsageCount live at sign time (needs a durable signing log) #28
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?
Summary
PolicyRule.maxUsageCountis written and displayed but never enforced — a usage-capped policy signs forever. This is the third sibling of the materialization-drift family from #25 (alongside token expiry #24 and revoke). #27 closed expiry + revoke but deferred usage caps, because enforcing them correctly needs infrastructure that doesn't exist yet.Why it couldn't ride along in #27
The Option D approach (per the #25 discussion and the lnbits/nostr_bunker prior art) is derive-don't-count: usage =
COUNT(allowed signings for this KeyUser+method)evaluated live, rather than a mutablecurrentUsageCountthat drifts. But there is no durable record of allowed signings to count:Requestrows are created only on the manual-approval path (authorize.ts:createRecord) and are auto-deleted after 60s (authorize.ts:79).Logmodel exists in the schema but is never written anywhere (grep prisma.log→ no hits).So a
COUNTquery today would count almost nothing.What's already in place (groundwork from #27)
Request.keyUserId(FK) +@@index([keyUserId, method])— added in the #27 schema migration precisely so usage can be counted per grant.Proposed work
Requestwith a durableallowed=truerow carryingkeyUserId+method(and stop auto-deleting those), or (b) start writing the dormantLogmodel as a signing-usage log. Lean toward a dedicated, clearly-named usage/audit record with a retention story (these grow unbounded).checkIfPubkeyAllowedstep 4. When a matchingPolicyRulehasmaxUsageCount != null,COUNTprior allowed signings for(keyUserId, method)and deny once>= maxUsageCount. Read-only count keeps the gatekeeper a predicate; the write happens at the permit call site (run.ts) when a request is allowed.PolicyRule.currentUsageCount— the mutable counter is the drift-prone cache we're replacing.maxUsageCountshape) vs per-window. A per-day cap (lnbits/nostr_bunker style) would need a window column — out of scope unless we want it.Notes
spirekeeper#22(revoke sibling).