fix(ai): checkAiQuota e acquireAiQuota fail-closed (Onda 6, B-7)#196
Conversation
Antes: se a RPC check_ai_quota/acquire_ai_quota falhasse (banco lento,
função recriada, etc), retornava `allowed: true` — permitia chamadas
ILIMITADAS de IA. Risco financeiro alto: 8h não monitorado podia
gerar ~US$ 400 em Gemini 2.5 Pro / GPT-5.
Agora: ambas retornam `allowed: false` com reason
`{quota_check,acquire}_failed_security_lock`. Callers existentes
(callAiWithTracking, ai-router) já tratam allowed=false via
QuotaExceededError → cliente recebe HTTP 429 normal.
Console.error agora é capturado pelo GlitchTip (Onda 5) — toda
falha de quota vira issue rastreável em erros.atomicabr.com.br.
Validação:
- Sintaxe TS validada via esbuild standalone
- 11 callers indiretos (via callAiWithTracking) já tratam 429
- 1 caller direto (ai-router/index.ts) já trata !allowed
- RPCs check_ai_quota e acquire_ai_quota confirmadas no banco prod
Bloqueador: B-7 da auditoria de 10/mai/2026.
Detalhes: docs/hardening/ONDA-6-AI-QUOTA-FAIL-CLOSED.md
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughEste PR implementa fail-closed para quota de IA: ChangesFail-closed em quota de IA — implementação e validação
Estimated code review effort🎯 2 (Simples) | ⏱️ ~12 minutos 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR changes AI quota handling to fail closed when quota RPC calls fail, reducing the risk of unbounded AI usage during infrastructure/database issues. It also adds a hardening note documenting the rationale and validation.
Changes:
checkAiQuotanow returnsallowed: falseoncheck_ai_quotaRPC errors.acquireAiQuotanow returnsallowed: falseonacquire_ai_quotaRPC errors.- Added Onda 6 hardening documentation for the quota fail-closed change.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
supabase/functions/_shared/ai-usage.ts |
Updates quota RPC error fallbacks from fail-open to fail-closed. |
docs/hardening/ONDA-6-AI-QUOTA-FAIL-CLOSED.md |
Documents the fail-closed change, impact, validation, and rollback notes. |
Comments suppressed due to low confidence (1)
supabase/functions/_shared/ai-usage.ts:95
- The new fail-closed branch is the production enforcement path used by
callAiWithTracking, but there is no regression test covering the RPC error case foracquireAiQuotain the existing AI usage tests. Please add coverage that mocksacquire_ai_quotafailing and verifies it returnsallowed: false/acquire_failed_security_lock, because a future fallback toallowed: truewould reintroduce the cost-control bug.
if (error) {
// Onda 6 (B-7): fail-CLOSED. Antes era "allow but without log_id" — risco de gasto
// descontrolado de IA se a RPC acquire_ai_quota falhar. Callers (callAiWithTracking,
// ai-router) tratam allowed=false via QuotaExceededError → resposta 429 ao cliente.
// Erro é logado via console.error → capturado pelo GlitchTip (Onda 5).
console.error("[ai-usage] Atomic quota acquire failed (fail-closed):", error.message);
return {
allowed: false,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (error) { | ||
| console.error("[ai-usage] Quota check failed:", error.message); | ||
| return { allowed: true, used: 0, limit: -1, remaining: -1, reason: "quota_check_failed" }; | ||
| // Onda 6 (B-7): fail-CLOSED. Antes era fail-open ("allowed: true") — risco de gasto | ||
| // descontrolado de IA se a RPC check_ai_quota falhar (banco lento, função recriada, etc). | ||
| // Agora bloqueia. Erro é logado via console.error → capturado pelo GlitchTip (Onda 5). | ||
| console.error("[ai-usage] Quota check failed (fail-closed):", error.message); | ||
| return { | ||
| allowed: false, |
| // Agora bloqueia. Erro é logado via console.error → capturado pelo GlitchTip (Onda 5). | ||
| console.error("[ai-usage] Quota check failed (fail-closed):", error.message); |
| // Erro é logado via console.error → capturado pelo GlitchTip (Onda 5). | ||
| console.error("[ai-usage] Atomic quota acquire failed (fail-closed):", error.message); |
| A auditoria também recomenda **alerta proativo** quando a branch fail-closed dispara N vezes em M minutos (sintoma de problema infra). Isso vai num PR separado (Onda futura — `alert-quota-failures`), provavelmente como webhook → Slack ou edge function de telemetria. | ||
|
|
||
| Por agora, o monitoramento pelo GlitchTip já cobre o caso. Issues nessa rota vão receber tag `quota_check_failed_security_lock` ou `acquire_failed_security_lock` pra filtrar. |
| Todos os 11 callers indiretos de `acquireAiQuota` (via `callAiWithTracking`) já tratam `QuotaExceededError`: | ||
|
|
…I e testes - Resolve conflitos em baselines (ESLint 853, TSC 1262) e test files - Corrige non-null assertion em sentry.ts (lint-staged pre-commit) - Mantém: SidebarNavGroup tests fixed, CI workflow fixes, baseline melhorado https://claude.ai/code/session_01MuNDxFSRRaJLsvkBdyQ2dK
Onda 6 do hardening pré-prod. Bloqueador B-7 da auditoria de 10/mai.
PROBLEMA:
Quando a RPC
check_ai_quotaouacquire_ai_quotafalhava (banco lento,função recriada, conexão Postgres ruim),
ai-usage.tsretornavaallowed: true— comportamento FAIL-OPEN. Isso permitia chamadasILIMITADAS de IA enquanto o sistema de quota estava com problema.
Cenário de prejuízo: Gemini 2.5 Pro custa US$ 1.25/1M input + US$ 10/1M
output. 8h não monitoradas com bug ativo = ~US$ 400. Final de semana = US$ 2.000.
DESCOBERTA DURANTE INSPEÇÃO:
A auditoria mencionava só
checkAiQuota(linha 62), mas inspecionandoo arquivo achei o irmão MAIS CRÍTICO —
acquireAiQuota(linha ~80),que é chamado por
callAiWithTracking, o ponto de entrada de TODASas 11 edge functions de IA do sistema (expert-chat, generate-mockup,
voice-agent, visual-search, etc).
checkAiQuotatinha ZERO callers reais em produção (função exportadamas órfã). Fixed por defesa em profundidade.
FIX:
Ambas as funções agora retornam:
allowed: false
reason: {quota_check|acquire}_failed_security_lock
Callers existentes (callAiWithTracking, ai-router/index.ts) já tratam
allowed: falsevia QuotaExceededError → cliente recebe HTTP 429"Limite mensal de IA atingido. Contate o administrador."
OBSERVABILIDADE:
console.error nas falhas é capturado pelo GlitchTip (Onda 5,
captureConsoleIntegration). Toda falha de quota vira issue
rastreável em erros.atomicabr.com.br.
VALIDAÇÃO:
Risco: baixo. 1 arquivo lógico (~16 linhas), 1 doc novo.
Detalhes: docs/hardening/ONDA-6-AI-QUOTA-FAIL-CLOSED.md
Summary by cubic
Enforces fail-closed behavior in
checkAiQuotaandacquireAiQuotaso quota RPC failures block usage instead of allowing it. Prevents runaway costs and addresses audit blocker B-7 (Onda 6).supabase/functions/_shared/ai-usage.ts, both functions now returnallowed: falseon RPC errors with reasonsquota_check_failed_security_lockandacquire_failed_security_lock.callAiWithTracking,_shared/ai-router) convert this to HTTP 429 viaQuotaExceededError, matching normal quota-exceeded behavior.console.errorand captured by GlitchTip; addeddocs/hardening/ONDA-6-AI-QUOTA-FAIL-CLOSED.mdwith context and runbook.Written for commit b955a59. Summary will update on new commits.
Summary by CodeRabbit
Notas de Lançamento
Bug Fixes
Documentation