perf(api,desktop,host-service): cut api traffic that no one needed

Merged
SA
1mo ago
+160-402

What & why

The Vercel bill is on track for ~$5.3k this month, and the api project is 93% of it. A sample of 5,000 production requests showed a lot of that traffic isn't needed:

ChangeShare of api requestsWhat was wrong
proxy.ts matcher narrowedinvocations were 2× requestsIt ran on every request only to add CORS headers. It now covers only paths a browser fetches (/api/trpc, /api/auth/* except jwks and oauth2/token, /api/desktop/version, /api/proxy/linear-image). Webhooks, host-service, the CLI, the SDK and MCP skip it.
Token refresh loop (host-service)~9%Once a refresh failed, refreshNeeded stayed true, so a host with a dead login retried the rejected refresh token on every api call, forever. Every sampled oauth2/token response with a recorded status was 400 or 429. A token rejected with 400 or 401 is no longer sent until the config holds a new one; network errors, 429s and 5xx still retry.
/api/auth/jwks CDN-cacheable~8%Cache-Control: public, s-maxage=300, stale-while-revalidate=3600. The relay's Workers isolates keep refetching the key set, and each fetch ran a function. We never rotate the RS256 key, so 5 minutes of staleness is safe. Depends on the proxy.ts change: before it, the proxy ran on jwks too.
getByBranches gated~6%It polled every 30s from every desktop, even with no cloud workspace rows to show. It now runs only when there are refs. The 30s poll stays for those users; moving it to a realtime nudge is a separate discussion.
Automations badge removed~6%The sidebar header polled automation.latestRuns + automation.list every 2 min and on every window focus, in every window. The badge is gone; clicking Automations goes straight to the page on a paid plan and otherwise fetches the list once to decide on the paywall. The getting-started card stops its 60s poll, and useFailedAutomations moves next to the automations page, its only user. The seen-failures store is deleted and automation-failures-v1 is added to DEAD_KEYS.

Separately (not in this PR): the Sazabi log drains were switched to 5% head sampling. They had been costing ~$775/month.

How I tested it

  • Checked the new proxy.ts matcher with Next's unstable_doesMiddlewareMatch against 17 real paths (tRPC batches, get-session, token, accept-invitation, oauth2/consent proxied; jwks, oauth2/token, webhooks, process-delivery, /mcp, /api/v2/agent, /.well-known skipped). All matched as intended.
  • New ConfigFileSessionTokenSource tests: a rejected refresh token is sent once across three attempts, a re-login gets through, and a 503 still retries. Against the old code the first test fails (3 sends).
  • bun run lint, typecheck for desktop, api and host-service, 375 desktop tests in the affected areas, host-service auth tests, and check:i18n (removes only the three badge strings).
  • Not yet clicked through in the app. Worth checking on the preview deployment:
    • The web app and the desktop renderer still reach the api: sign-in, tRPC, and accepting an invitation.
    • The Automations nav item on a free and a paid org.
    • curl -I .../api/auth/jwks shows x-vercel-cache: HIT on the second request.

Checklist

  • PR title follows conventional commits (type(scope): subject)
  • bun run lint and bun run typecheck pass (CI fails on lint warnings too)
  • "Allow edits from maintainers" is checked on fork PRs

Summary by cubic

Cuts avoidable api traffic driving a ~$5.3k/month Vercel bill, with the api project at 93% of it.

API and host-service

  • The proxy.ts matcher now covers only browser-fetched paths; it previously ran on every request to add CORS headers, billing each as two invocations.
  • /api/auth/jwks is now CDN-cacheable for 5 minutes instead of being refetched constantly by the relay's Workers isolates.
  • host-service stops resending a refresh token the api rejected (400/401) until the config holds a new one; network errors, 429s and 5xx still retry.

Desktop

  • getByBranches polls only when there are refs to show, not every 30s from every desktop regardless.
  • Removed the sidebar automations failure badge that polled latestRuns + list every 2 minutes and on every window focus; the paywall now fetches the list once on click, the getting-started card stops its 60s poll, and the seen-failures store is deleted.

Summary by CodeRabbit

  • Bug Fixes
    • Expired sessions now prompt you to sign in again when the server rejects a refresh token, while temporary server errors can still be retried.
    • Automation navigation handles access checks and unavailable automation lists more reliably.
  • Changes
    • Failed-run count badges and failure-count labels are no longer shown in the Automations sidebar.
    • Latest automation run information refreshes periodically.
    • Successful authentication key requests are cached to help responses load efficiently.