security: enforce AI daily limit server-side via atomic RPC#14
Open
jaxhemopo wants to merge 1 commit into
Open
security: enforce AI daily limit server-side via atomic RPC#14jaxhemopo wants to merge 1 commit into
jaxhemopo wants to merge 1 commit into
Conversation
… RPC
The 35/day AI limit (UsageService.AI_LIMIT) was enforced only client-side, so a
user with a valid JWT could call functions.invoke('openai-chat') in a loop and
run up the OpenAI bill (CODE_REVIEW.md #4). The client daily-reset also had a
read-then-write race (#17).
- New increment_ai_call(p_limit) RPC (migration): SECURITY DEFINER, atomic
upsert + daily rollover, keyed on auth.uid() so a caller can only touch their
own counter. Returns { allowed, count_today }.
- openai-chat calls it after validation and returns 429 when the cap is hit.
- UsageService.recordAICall is now a no-op (server is authoritative) to avoid
double-counting; the client pre-check canMakeAICall stays as advisory UX.
DEPLOY ORDER (important): apply the migration and deploy openai-chat FIRST, then
ship the client. Edge-first means counting stays conservative during rollout
(old clients briefly double-count) rather than risking an uncounted window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The 35/day AI cap (
UsageService.AI_LIMIT) was enforced only client-side, so a JWT holder could callfunctions.invoke('openai-chat')in a loop and run up the OpenAI bill (CODE_REVIEW.md #4). The client daily-reset also had a read-then-write race (#17).increment_ai_call(p_limit)RPC (migration):SECURITY DEFINER, atomic upsert + daily rollover, keyed onauth.uid()so a caller can only ever touch their own counter (no spoofing ap_user_id). Returns{ allowed, count_today }. The atomic upsert also fixes the #17 reset race.openai-chatcalls it after validation and returns 429 when the cap is hit.UsageService.recordAICallis now a no-op (server is authoritative) to avoid double-counting; thecanMakeAICallpre-check stays as advisory UX. (One method changed rather than 5 call sites across 3 screens.)Apply the migration + deploy
openai-chatfirst, then ship the client. Edge-first keeps counting conservative during rollout (old clients briefly double-count) rather than risking an uncounted window (new client shipped before the server enforces).Note
openai-chatis also touched by PR #12 (model allowlist) — different region, merges cleanly.openai-vision/openai-testaren't client-called so they're not rate-limited here; add the same RPC call if they're ever exposed.🤖 Generated with Claude Code