Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

feat(coderd): enforce ai budget on pre-request path - #26915

#26915
Merged
ssncferreira merged 6 commits into
maincoder/coder:mainfrom
ssncf/ai-budget-enforcementcoder/coder:ssncf/ai-budget-enforcementCopy head branch name to clipboard
Jul 2, 2026
Merged

feat(coderd): enforce ai budget on pre-request path#26915
ssncferreira merged 6 commits into
maincoder/coder:mainfrom
ssncf/ai-budget-enforcementcoder/coder:ssncf/ai-budget-enforcementCopy head branch name to clipboard

Conversation

@ssncferreira

@ssncferreira ssncferreira commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Description

Adds pre-request AI budget enforcement to aibridged. Requests are rejected with HTTP 403 when the user's aggregated spend for the current period has reached their effective limit.

Changes

  • Add IsBudgetExceeded RPC to aibridgedserver. Resolves the user's effective budget, aggregates spend over the caller-supplied [period_start, now] window, and returns whether the limit has been reached along with the effective limit.
  • Wire the check into aibridged's HTTP handler. The caller computes the period start (monthly for now) and passes it in the request.
  • Reject exceeded requests with HTTP 403 Forbidden and a message directing the user to contact an administrator.
  • Add dbtime.StartOfMonth alongside StartOfDay for period computation.
  • Add real-DB tests covering the enforcement path: month-boundary excludes prior-period spend, and a new user override unblocks a previously-exceeded user.

Closes https://linear.app/codercom/issue/AIGOV-428/add-pre-request-budget-enforcement

Note

Initially generated by Claude Opus 4.7, modified and reviewed by @ssncferreira

ssncferreira commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@ssncferreira
ssncferreira force-pushed the ssncf/ai-budget-enforcement branch from 8fe4e34 to e83bc03 Compare July 1, 2026 12:09
@ssncferreira
ssncferreira force-pushed the ssncf/ai-spend-on-token-usage branch from f1c6469 to 13eb001 Compare July 1, 2026 12:09
@ssncferreira
ssncferreira force-pushed the ssncf/ai-budget-enforcement branch 3 times, most recently from a90d3f2 to af61bce Compare July 1, 2026 17:01
@linear-code

linear-code Bot commented Jul 1, 2026

Copy link
Copy Markdown

AIGOV-428

@ssncferreira
ssncferreira force-pushed the ssncf/ai-budget-enforcement branch from af61bce to 90b1aaa Compare July 1, 2026 17:12
@ssncferreira

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-07-01 17:13 UTC by @ssncferreira
Spend: $27.60 / $100.00

Review history
  • R1 (2026-07-01): 16 reviewers, 3 Nit, 2 Note, 4 P3, COMMENT. Review

deep-review v0.9.0 | Round 1 | 13eb001..90b1aaa

Last posted: Round 1, 9 findings (4 P3, 3 Nit, 2 Note), COMMENT. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P3 Open http.go:162 402 error body exposes internal group UUID to API caller R1 Chopper P3, Hisoka P3, Kite P3, Knov P3, Mafuuu P3, Meruem P3, Pariston P3, Ryosuke P3, Leorio P3, Zoro P3 Yes
CRF-2 P3 Open aibridgedserver.go:804 TOCTOU race on concurrent requests is undocumented (chatd documents same trade-off) R1 Hisoka P2, Meruem P3, Knov Note, Pariston Note Yes
CRF-3 P3 Open http.go:28 ErrInternalServerError names HTTP status, not failure condition R1 Gon P3, Leorio P3, Zoro P3 Yes
CRF-4 P3 Open http.go:157 Budget check failure log missing user_id structured field R1 Chopper P3 Yes
CRF-5 Nit Open http.go:151 dbtime.Now().UTC() is redundant R1 Chopper Note, Meruem Note, Ryosuke Nit Yes
CRF-6 Nit Open http.go:165 Magic number 1_000_000 for micros-to-USD conversion R1 Gon P3 Yes
CRF-7 Nit Open aibridgedserver_test.go:399 Validation error tests assert only require.Error without checking which validation failed R1 Chopper Nit Yes
CRF-8 Note Open aibridged.proto:37 GetUserAISpendStatus placed on Authorizer service widens its contract R1 Kite P3, Knov Note, Mafuuu Note, Meruem Nit, Pariston Note, Ryosuke Note, Zoro Note Yes
CRF-9 Note Open aibridgedserver.go:804 0 limit blocks all requests (0 >= 0 = true), untested edge case R1 Kite Note Yes

Round log

Round 1

Netero: no findings. Panel: Bisky, Chopper, Ging-Go, Gon, Hisoka, Killua, Kite, Knov, Komugi, Leorio, Mafu-san, Mafuuu, Meruem, Pariston, Ryosuke, Zoro. 4 P3, 3 Nit, 2 Note. Reviewed against 13eb001..90b1aaa.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, focused PR that adds a real enforcement gate where none existed. Test density is 65%, coverage is thorough (9 unit scenarios + a real-DB boundary test), and the code reuses budget.ResolveUserAIBudget from the post-request path. Well-decomposed.

Severity count: 4x P3, 3x Nit, 2x Note. No P0-P2.

This is a first panel round. All findings are below P0-P1, so no changes requested.

"The PR title says 'enforce.' The implementation checks, but enforcement requires the check to exclude the gap." — Hisoka

🤖 This review was automatically generated with Coder Agents.

Comment thread coderd/aibridged/http.go Outdated
Comment thread coderd/aibridgedserver/aibridgedserver.go Outdated
Comment thread coderd/aibridged/http.go Outdated
Comment thread coderd/aibridged/http.go Outdated
Comment thread coderd/aibridged/http.go
Comment thread coderd/aibridged/http.go Outdated
Comment thread coderd/aibridgedserver/aibridgedserver_test.go
Comment thread coderd/aibridged/proto/aibridged.proto Outdated
Comment thread coderd/aibridgedserver/aibridgedserver.go Outdated
Comment thread coderd/aibridged/http.go Outdated
Comment thread coderd/aibridged/http.go Outdated

// TODO: make the budget period configurable; monthly for now.
periodStart := dbtime.StartOfMonth(dbtime.Now().UTC())
spendStatus, err := client.GetUserAISpendStatus(ctx, &proto.GetUserAISpendStatusRequest{

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In here, I opted for a different approach from the other RPC calls. On the other RPC calls, we don't distinguish between internal server errors and logic errors (like authentication), both return the same response, e.g., ErrUnauthorized.Error() . To distinguish between internal server errors and the actual budget error, err handles only the first case. When the budget is exceeded, we check spendStatus.GetExceeded() and send the appropriate error response. It is not really consistent with the other cases, but I thought it would result in clearer errors. Let me know what you think.

// - Overage is bounded by request cost × concurrency; once the accumulated
// spend crosses the limit, subsequent requests are blocked.
// - Fail-open is the deliberate design choice for this feature.
func (s *Server) resolveUserAISpendStatus(ctx context.Context, userID uuid.UUID, periodStart time.Time) (userAISpendStatus, error) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enforcement is a pre-LLM-request check on the hot path. In the worst case, in addition to the authorization database queries, we do 3 more queries:

  1. GetUserAIBudgetOverride
  2. GetHighestGroupAIBudgetByUser
  3. GetUserAISpendSince

I think it's clearer to keep it this way for now as each query is focused and self-contained. If this becomes a bottleneck, we can consolidate into a single optimized query.

@ssncferreira
ssncferreira force-pushed the ssncf/ai-budget-enforcement branch from 3243363 to 957b043 Compare July 1, 2026 19:41
@ssncferreira
ssncferreira marked this pull request as ready for review July 1, 2026 20:11
@coder-tasks

coder-tasks Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Documentation Check

Updates Needed

  • docs/ai-coder/agents/platform-controls/usage-insights.md - The docs state that budget-exceeded requests return a 409 Conflict response (line 39), but this PR returns 403 Forbidden. The status code needs to be updated.
  • docs/ai-coder/agents/platform-controls/usage-insights.md - The docs state the system is fail-open (line 40: "if the limit query itself fails, the message is allowed through"), but this PR implements fail-closed behavior (returns 500 Internal Server Error when the budget check fails). This needs to be updated to reflect the new behavior.

Automated review via Coder Agents

Comment thread coderd/aibridged/proto/aibridged.proto Outdated
Comment thread coderd/aibridged/proto/aibridged.proto Outdated
Comment thread coderd/aibridgedserver/aibridgedserver.go
Comment thread coderd/aibridged/http.go Outdated
Comment thread coderd/aibridged/http.go Outdated
Comment thread coderd/aibridged/http.go Outdated
PeriodStart: timestamppb.New(periodStart),
})
if err != nil {
logger.Warn(ctx, "ai spend status check failed", slog.Error(err))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be an error not a warning I reckon.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, it should. But all the other logs here use Warn as well for error message. And my assumption is that this is due to the tests, because then we would need to update them to ignore errors. I can address this in a follow-up and update all the logs as well as tests if that makes sense

Comment thread coderd/aibridged/http.go Outdated
@ssncferreira
ssncferreira requested a review from dannykopping July 2, 2026 11:10
@ssncferreira
ssncferreira force-pushed the ssncf/ai-budget-enforcement branch from 9891e0b to 24ec993 Compare July 2, 2026 11:31
@ssncferreira
ssncferreira force-pushed the ssncf/ai-budget-enforcement branch from 24ec993 to 15f3e9a Compare July 2, 2026 14:51
@ssncferreira
ssncferreira force-pushed the ssncf/ai-spend-on-token-usage branch from 13eb001 to dbb6f44 Compare July 2, 2026 14:51
@ssncferreira
ssncferreira force-pushed the ssncf/ai-budget-enforcement branch from 15f3e9a to 6708bd9 Compare July 2, 2026 14:58
@ssncferreira
ssncferreira force-pushed the ssncf/ai-spend-on-token-usage branch from dbb6f44 to 04e366f Compare July 2, 2026 14:58

ssncferreira commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • Jul 2, 3:29 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jul 2, 3:44 PM UTC: Graphite rebased this pull request as part of a merge.
  • Jul 2, 3:53 PM UTC: @ssncferreira merged this pull request with Graphite.

@ssncferreira
ssncferreira changed the base branch from ssncf/ai-spend-on-token-usage to graphite-base/26915 July 2, 2026 15:30
@ssncferreira
ssncferreira changed the base branch from graphite-base/26915 to main July 2, 2026 15:42
@ssncferreira
ssncferreira force-pushed the ssncf/ai-budget-enforcement branch from a76e58c to e753f18 Compare July 2, 2026 15:43
@ssncferreira
ssncferreira merged commit 1989db0 into main Jul 2, 2026
29 of 30 checks passed
@ssncferreira
ssncferreira deleted the ssncf/ai-budget-enforcement branch July 2, 2026 15:53
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.