Skip to content

Make every rate-limit env var optional and fail open on deleted rules - #8818

Merged
azooz2003-bit merged 2 commits into
mainfrom
feat-ratelimit-env-optional
Jul 24, 2026
Merged

azooz2003-bit merged 2 commits into
mainfrom
feat-ratelimit-env-optional

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jul 24, 2026 •

Copy link
Copy Markdown
Collaborator

Extends the fail-open pattern from #8714, #8773, and #8771 to every remaining rate-limit consumer, per the no-rate-limits ruling.

Why now: the two relay/iroh env vars were just unset in Vercel prod; this deploy applies them. The remaining three ids (CMUX_ANALYTICS_RATE_LIMIT_ID, CMUX_CLIENT_CONFIG_RATE_LIMIT_ID, CMUX_FEEDBACK_RATE_LIMIT_ID) could not be unset before this change: env.ts hard-required them (deploys would fail validation) and the routes 503'd without them — client-config gates every app boot.

Changes: all rate-limit ids optional in env.ts; unset id = no rate limiting; deleted rule (not-found) = warn + fail open; genuine check failures keep failing closed; enterprise/feedback config resolvers no longer treat a missing id as 'not configured'. Also fixes a pre-existing red test on main (stale iroh-required assertion).

Verification: bun test 870 pass / 0 fail (was 6 fail against old assertions + 1 pre-existing red on main); bun run typecheck clean.

After merge + deploy, all remaining *_RATE_LIMIT_ID env vars can be deleted with zero firewall calls made anywhere.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Make all rate-limit env vars optional and fail open when a Vercel firewall rule is deleted. This prevents outages when limits are removed and keeps production deploys passing without these vars.

  • Refactors

    • env.ts: CMUX_FEEDBACK_RATE_LIMIT_ID, CMUX_CLIENT_CONFIG_RATE_LIMIT_ID, and CMUX_ANALYTICS_RATE_LIMIT_ID are optional; removed Vercel production validation.
    • analytics/events and client-config: unset id skips limiting; not-found warns and continues; errors still 503; blocked requests return 429.
    • feedback, waitlist, enterprise/contact: guard on optional id; not-found warns and continues; missing id no longer marks endpoint “not configured.”
    • Tests updated for fail-open behavior and resolved client-config env expectations.
  • Migration

    • No action required.
    • You may delete all *_RATE_LIMIT_ID env vars; no firewall calls will be made.

Written for commit 1aaa31c. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Feedback, waitlist, analytics, client configuration, and enterprise contact requests now “fail open” when rate-limiting rules are unset or not found.
    • Rate-limiting behavior remains enforced for real limiter errors; rate-limited/blocked outcomes still return appropriate 429 responses.
  • Configuration
    • Rate-limit environment validation has been relaxed for selected settings, allowing deployments to pass when those optional IDs are omitted.
  • Tests
    • Updated test cases to reflect the new fail-open behavior across affected routes.

Aziz's ruling: no rate limits at all, and nothing may break when the rule ids
are unset or their Vercel firewall rules are deleted. This extends the
8714/8773/8771 fail-open pattern to the remaining consumers:

- env.ts: CMUX_FEEDBACK/CLIENT_CONFIG/ANALYTICS_RATE_LIMIT_ID become optional
  (client-config and analytics previously hard-failed production deploy env
  validation when unset; feedback failed every deploy).
- analytics/events + client-config routes: unset id skips limiting instead of
  503ing; a not-found rule warns and fails open; genuine check failures still
  503.
- waitlist + feedback routes: guard the limiter on the optional id and fail
  open on not-found instead of 503ing the endpoint.
- enterprise/contact + feedback config resolvers no longer treat a missing
  rate-limit id as 'endpoint not configured'.
- push and vault routes already guarded/failed open; unchanged.

Also fixes a pre-existing red test on main (client-config-env expected
CMUX_IROH_RATE_LIMIT_ID to be required, stale since 8714/8771).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Vercel rate-limit identifiers are now optional across affected routes. Missing identifiers skip enforcement, while missing rate-limit rules fail open with warnings. Other limiter errors and blocked requests retain their existing responses, with environment and route tests updated accordingly.

Changes

Vercel rate-limit configuration and handling

Layer / File(s) Summary
Optional rate-limit environment contract
web/app/env.ts, web/tests/client-config-env.test.ts
Rate-limit environment variables are optional, and production validation tests now allow deployments without configured limiter IDs.
Route rate-limit handling
web/app/api/analytics/events/route.ts, web/app/api/client-config/route.ts, web/app/api/enterprise/contact/route.ts, web/app/api/feedback/route.ts, web/app/api/waitlist/route.ts
Affected routes skip rate limiting when no ID is configured and continue on not-found; blocked requests still return 429, while other limiter errors return 503.
Route behavior validation
web/tests/client-config-route.test.ts, web/tests/feedback-route.test.ts
Tests verify skipped limiting and successful fail-open behavior, including downstream PostHog requests and email delivery.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • manaflow-ai/cmux#8773: Updates related rate-limiting flows to skip unset limiter IDs and fail open when limiter rules are missing.

Suggested reviewers: lawrencecchen


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors, 1 inconclusive)

Check name Status Explanation Resolution
Cmux Swift Logging ❌ Error The Swift diff adds a file-scoped private let hiddenMacsLog = Logger(...) in runtime code, but it is not nonisolated in a MainActor-affected file. Declare the logger as nonisolated private let (or move logging to an allowed debug/test path) and keep any sensitive values redacted.
Cmux Full Internationalization ❌ Error FAIL: ComputerBuildBadge.swift uses mobile.computers.buildLabelPrefix, but ios/cmux/Resources/Localizable.xcstrings has no matching entry. Add mobile.computers.buildLabelPrefix to Localizable.xcstrings with translated en/ja values, or switch to an existing localized key.
Cmux No Hacky Sleeps ❓ Inconclusive placeholder Need repo evidence before final verdict
✅ Passed checks (22 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed Swift diffs are limited to actors, SwiftUI views, and Sendable value types; no new protocol isolation, shared mutable Sendable refs, or UI-store background access was introduced.
Cmux Swift Blocking Runtime ✅ Passed No new Swift blocking primitives were introduced; the diff adds no waits/sleeps/locks, and remaining matches are pre-existing or test-only.
Cmux Browser Automation Off-Main ✅ Passed PR only changes env/routes/tests/workflows; it does not touch browser socket automation commands or policy coverage, so the off-main WebKit rule is not implicated.
Cmux Expensive Synchronous Load ✅ Passed PASS: the diff only touches web/tests/client-config-env.test.ts; no Swift files or main-actor/interactive agent-history loads are added or moved.
Cmux Cache Substitution Correctness ✅ Passed No fresh-authoritative-read was replaced by a cache/opportunistic value; changes only make rate-limit IDs optional and fail open on deleted firewall rules.
Cmux Algorithmic Complexity ✅ Passed Diff only adds optional rate-limit guards and fail-open branches; no new nested scans, repeated sorts, or per-target rescans in production code.
Cmux Swift Concurrency ✅ Passed Touched Swift files only add/adjust SwiftUI and async/await flow; no new DispatchQueue, Combine, completion-handler, or fire-and-forget Task patterns were introduced.
Cmux Swift @Concurrent ✅ Passed No Swift concurrency rule violations: the only new off-main helper is @concurrent loadImage, while other async changes remain @MainActor or actor-isolated.
Cmux Swift Package Boundaries ✅ Passed PASS: all Swift diffs are under SwiftPM package targets (CmuxMobileShell/CmuxMobileShellUI) plus tests; no app-target Swift boundary violation.
Cmux Swiftpm Lockfiles ✅ Passed PR diff only changes web app routes, env, and tests; no SwiftPM, Xcode, .gitignore, or workflow files were touched, so the lockfile rule is not triggered.
Cmux User-Facing Error Privacy ✅ Passed Changed API error bodies stay generic; the new rate-limit IDs appear only in server-side warnings, not user-visible responses.
Cmux Swiftui State Layout ✅ Passed No Swift files were changed in the actual diff, so the SwiftUI state/layout rule is not applicable.
Cmux Architecture Rethink ✅ Passed PR changes only web/ TypeScript routes/tests; no Swift code or Swift architecture patterns are introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR only changes web code and iOS mobile-shell views/models; no NSWindow/NSPanel/NSWindowController/WindowGroup additions or cmuxAuxiliaryWindowIdentifiers changes were found.
Cmux Source Artifacts ✅ Passed All changed paths are intentional source/docs/tests/assets; none are scratch, build, cache, or local tool artifacts.
Cmux No Test Or Debug Seam In Production Source ✅ Passed No new test/debug seams were added in production Sources; diff searches found no new #if DEBUG/test-only members or access-widening seams.
Cmux No Ambient Global State ✅ Passed No new ambient global state was introduced; added Swift changes are instance members or immutable helpers, and the PR removes prior global-ish hidden-computer state.
Title check ✅ Passed The title accurately summarizes the main change: optional rate-limit env vars and fail-open behavior for deleted rules.
Description check ✅ Passed The description covers the summary, rationale, and verification results, and is detailed enough despite omitting some template sections.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-ratelimit-env-optional

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extends the fail-open pattern to all remaining rate-limit consumers, makes every *_RATE_LIMIT_ID env var optional in env.ts, and removes the Vercel-production hard requirement on those vars — unblocking the ability to delete them from the Vercel environment without causing deploy failures or 503s on app boot.

  • env.ts: removes requireVercelProductionValue and relaxes CMUX_FEEDBACK_RATE_LIMIT_ID, CMUX_CLIENT_CONFIG_RATE_LIMIT_ID, and CMUX_ANALYTICS_RATE_LIMIT_ID to z.string().min(1).optional(), leaving isVercelProductionDeployment as dead code since nothing references it anymore.
  • Routes (analytics/events, client-config, feedback, waitlist, enterprise/contact): guard the checkRateLimit call on both VERCEL === "1" and a non-empty id; not-found errors now warn and fall through rather than 503; genuine check failures still return 503 — except in enterprise/contact, where both not-found and genuine errors already fell through before this PR (pre-existing, now more reachable).
  • Tests: updated to assert the new fail-open semantics and fix a pre-existing red assertion on main.

Confidence Score: 5/5

Safe to merge. The change consistently applies the intended fail-open pattern across all rate-limit consumers and removes env var requirements that were blocking deployments.

All five routes correctly guard on both the Vercel flag and a non-empty rate-limit ID before calling checkRateLimit, so no firewall calls are made when IDs are absent. The not-found path now warns and continues rather than returning 503. Genuine check errors still return 503 in four of five routes. The enterprise/contact fall-through on genuine errors is pre-existing and already flagged in a prior review.

web/app/api/enterprise/contact/route.ts — genuine rate-limit errors still fall through rather than returning 503, and not-found uses console.error instead of console.warn. Both are pre-existing and flagged in a prior review.

Important Files Changed

Filename Overview
web/app/env.ts All three rate-limit IDs made optional; requireVercelProductionValue removed but its only input, isVercelProductionDeployment, remains defined and unreferenced — dead code.
web/app/api/analytics/events/route.ts Fail-open pattern applied correctly: unset id skips limiting, not-found warns and continues, genuine errors still 503.
web/app/api/client-config/route.ts Fail-open pattern applied correctly; not-found downgraded to warn and falls through; genuine errors still 503.
web/app/api/enterprise/contact/route.ts Guard added for rateLimitId presence; pre-existing issue where genuine check errors also fail open (no 503 return) is now more routinely reachable since resolveEnterpriseConfig no longer requires rateLimitId to return non-null.
web/app/api/feedback/route.ts Fail-open pattern applied correctly; not-found warns and continues; genuine errors still 503; resolveFeedbackConfig no longer requires rateLimitId.
web/app/api/waitlist/route.ts Guard added for CMUX_FEEDBACK_RATE_LIMIT_ID presence; not-found warns and continues; genuine errors still 503.
web/tests/client-config-env.test.ts Tests inverted to assert production deployments succeed without any RATE_LIMIT_ID vars; assertions are consistent with new env.ts schema.
web/tests/client-config-route.test.ts Two tests updated from fail-closed to fail-open assertions; mock setup corrected to allow PostHog fetch to proceed; coverage is accurate.
web/tests/feedback-route.test.ts Missing-rule test updated from 503 to 200 with sendEmail called; fixes a stale red assertion from main.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming Request] --> B{VERCEL === '1'\nAND rateLimitId set?}
    B -- No --> F[Continue to handler]
    B -- Yes --> C[checkRateLimit]
    C --> D{Result}
    D -- rateLimited / blocked --> E[Return 429]
    D -- not-found --> G[console.warn\nfail open]
    G --> F
    D -- other error --> H{Route?}
    H -- analytics / client-config\nfeedback / waitlist --> I[Return 503]
    H -- enterprise/contact --> J[console.error\nfail open pre-existing]
    J --> F
    D -- ok --> F
    F --> K[Execute route logic]
Loading

Reviews (2): Last reviewed commit: "Merge origin/main (resolve client-config..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/app/api/enterprise/contact/route.ts`:
- Line 54: Update the rate-limiter error handling in the route around the
VERCEL/config.rateLimitId branch so only a not-found limiter error is warned
about and allowed to continue. For any other limiter error, return an HTTP 503
response before sending the enterprise email, preserving the existing success
and not-found paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 14b6a26a-0ac5-4391-9884-2b8110f216b2

📥 Commits

Reviewing files that changed from the base of the PR and between 745534f and 5f6a6af.

📒 Files selected for processing (9)
  • web/app/api/analytics/events/route.ts
  • web/app/api/client-config/route.ts
  • web/app/api/enterprise/contact/route.ts
  • web/app/api/feedback/route.ts
  • web/app/api/waitlist/route.ts
  • web/app/env.ts
  • web/tests/client-config-env.test.ts
  • web/tests/client-config-route.test.ts
  • web/tests/feedback-route.test.ts

}

if (process.env.VERCEL === "1") {
if (process.env.VERCEL === "1" && config.rateLimitId) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Return a failure for non-not-found limiter errors.

Line 54 enters a branch whose current else if (error) only logs and then sends the enterprise email. This makes genuine limiter failures fail open too. Warn and continue only for not-found; return a 503 for other errors.

Proposed fix
 if (error === "not-found") {
-  console.error(
+  console.warn(
     "enterprise.contact.rate_limit_not_found",
     config.rateLimitId,
   );
 } else if (error) {
   console.error("enterprise.contact.rate_limit_error", error);
+  return jsonError("service_unavailable", 503);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (process.env.VERCEL === "1" && config.rateLimitId) {
if (error === "not-found") {
console.warn(
"enterprise.contact.rate_limit_not_found",
config.rateLimitId,
);
} else if (error) {
console.error("enterprise.contact.rate_limit_error", error);
return jsonError("service_unavailable", 503);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/app/api/enterprise/contact/route.ts` at line 54, Update the rate-limiter
error handling in the route around the VERCEL/config.rateLimitId branch so only
a not-found limiter error is warned about and allowed to continue. For any other
limiter error, return an HTTP 503 response before sending the enterprise email,
preserving the existing success and not-found paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@azooz2003-bit
azooz2003-bit merged commit b5fac31 into main Jul 24, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant