Skip to content

fix(streams): connect desktop directly to Fly.io streams server#1404

Merged
Kitenite merged 3 commits into
mainfrom
kitenite/prod-chat-issue
Feb 11, 2026
Merged

fix(streams): connect desktop directly to Fly.io streams server#1404
Kitenite merged 3 commits into
mainfrom
kitenite/prod-chat-issue

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Feb 11, 2026

Summary

  • Desktop chat was failing in production with ERR_CONTENT_DECODING_FAILED because the Vercel API proxy double-encoded response bodies
  • Remove the API proxy entirely and connect the desktop app directly to the Fly.io streams server (streams.superset.sh)
  • Add configurable CORS origins on the streams server for production, with support for Electron's null-origin requests

Changes

Removed — API proxy (apps/api)

  • Delete apps/api/src/app/api/streams/[...path]/route.ts
  • Remove STREAMS_URL from API env schema (no longer needed)

Updated — Desktop (apps/desktop)

  • Add STREAMS_URL env var with default https://streams.superset.sh
  • Update getConfig and ChatSessionManager to use STREAMS_URL directly instead of routing through the API proxy

Updated — Streams server (apps/streams)

  • Add CORS_ORIGINS env var for configurable allowed origins
  • Wire CORS origins through createServer with function-based origin check
  • Handle Electron file:// null-origin requests in CORS middleware
  • Set production CORS origins in fly.toml

Test Plan

  • Add DNS record: streams.superset.sh → Fly.io (cert already provisioned)
  • Deploy streams server and verify https://streams.superset.sh/health returns 200
  • Test desktop chat connects and streams messages in production
  • Test desktop chat still works in local dev (STREAMS_URL=http://localhost:8080)

Summary by CodeRabbit

  • New Features

    • Added configurable CORS origins support for the streams service, allowing connections from web and desktop applications.
  • Improvements

    • Refactored streaming architecture to use direct URLs instead of API proxying, improving performance and reducing latency.

The API proxy was causing ERR_CONTENT_DECODING_FAILED in production
because Node.js fetch auto-decompresses responses but forwards the
original Content-Encoding headers, causing browsers to double-decompress.

Instead of patching the proxy, remove it entirely and have the desktop
app connect directly to the streams server on Fly.io (streams.superset.sh).
This eliminates the Vercel middleman, reduces latency, and avoids
serverless timeout limits on long-lived SSE connections.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

This change migrates the streams service from being accessed through an API proxy to direct client access via a new NEXT_PUBLIC_STREAMS_URL environment variable. The API proxy route is removed, environment validation updated, desktop clients refactored to use the new variable, and CORS origins configuration added to the streams server.

Changes

Cohort / File(s) Summary
Environment Variable Naming
.env.example, .github/workflows/build-desktop.yml, apps/desktop/electron.vite.config.ts, apps/desktop/src/main/env.main.ts
Rename STREAMS_URL to NEXT_PUBLIC_STREAMS_URL and propagate through environment configuration layers, adding default value https://streams.superset.sh.
Desktop Streams Client Integration
apps/desktop/src/lib/trpc/routers/ai-chat/index.ts, apps/desktop/src/lib/trpc/routers/ai-chat/utils/session-manager/session-manager.ts
Update proxyUrl construction to use NEXT_PUBLIC_STREAMS_URL directly instead of composing it from API URL with /api/streams suffix.
Desktop HTML & Runtime Configuration
apps/desktop/src/renderer/index.html, apps/desktop/vite/helpers.ts
Update CSP connect-src directive and HTML environment variable substitution to include NEXT_PUBLIC_STREAMS_URL mapping.
API Proxy Removal
apps/api/src/app/api/streams/[...path]/route.ts, apps/api/src/env.ts
Remove the entire /api/streams/[...path] proxy route handler and delete STREAMS_URL field from server environment validation schema.
Deployment Workflow Updates
.github/workflows/deploy-preview.yml, .github/workflows/deploy-production.yml
Remove STREAMS_URL environment variable from API deployment steps in both preview and production workflows.
Streams Server CORS Enhancement
apps/streams/fly.toml, apps/streams/src/env.ts, apps/streams/src/index.ts, apps/streams/src/server.ts
Add configurable CORS_ORIGINS environment variable and implement conditional origin validation in CORS middleware, replacing wildcard origin with allowlist-based resolver when configured.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 No proxy hops, just streams so bright,
CORS gates wide open, everything just right!
Desktop calls direct, safe and clean,
The fastest chat service you've ever seen! 🚀

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main objective: connecting the desktop app directly to Fly.io's streams server instead of through an API proxy.
Description check ✅ Passed The description comprehensively covers all required sections with clear explanations of the problem, changes made across three main components, and a detailed test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kitenite/prod-chat-issue

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 and usage tips.

The streams URL needs to be in the renderer's Content Security Policy
connect-src directive, and replaced at build time via the HTML env
transform plugin. Renamed from STREAMS_URL to NEXT_PUBLIC_STREAMS_URL
to follow the convention for renderer-visible env vars.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 11, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app
  • ✅ Streams Fly.io app

Thank you for your contribution! 🎉

Remove STREAMS_URL from API deploy workflows (proxy deleted).
Add NEXT_PUBLIC_STREAMS_URL to desktop build workflow.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/streams/fly.toml`:
- Line 13: The CORS_ORIGINS entry currently includes an unnecessary Tauri
origin; update the CORS_ORIGINS value (the CORS_ORIGINS setting string) to
remove "tauri://localhost" so only valid Electron/production origins remain
(e.g., "https://app.superset.sh,https://admin.superset.sh"), ensuring you remove
any extra commas so the CSV string remains well-formed.

In `@apps/streams/src/index.ts`:
- Around line 23-25: After splitting and trimming env.CORS_ORIGINS into
corsOrigins, filter out empty strings so the resulting array contains only valid
origins; update the logic that builds corsOrigins in apps/streams/src/index.ts
(the corsOrigins variable) to remove entries where o === "" (e.g., via a
.filter) so server.ts's allowedOrigins.includes(origin) won't mistakenly match
empty-string or have an array with no real origins.

Comment thread apps/streams/fly.toml
STREAMS_INTERNAL_PORT = "8081"
STREAMS_INTERNAL_URL = "http://127.0.0.1:8081"
NODE_ENV = "production"
CORS_ORIGINS = "https://app.superset.sh,https://admin.superset.sh,tauri://localhost"
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for Tauri configuration files
echo "=== Searching for Tauri configuration ==="
fd -t f "tauri.conf" --max-results 10

# Check for Tauri in TOML files (excluding fly.toml)
echo "=== Tauri references in TOML files ==="
rg "tauri" --type=toml -g '!fly.toml' --max-count=20

# Check for Tauri in package.json files
echo "=== Tauri in package.json files ==="
rg "@tauri" --type=json -g 'package.json' --max-count=20

# Check desktop app structure
echo "=== Desktop app structure ==="
ls -la apps/desktop/ 2>/dev/null | head -20

# Check for Electron in desktop package.json
echo "=== Electron/Tauri in apps/desktop/package.json ==="
cat apps/desktop/package.json 2>/dev/null | grep -A 5 -E '"(electron|tauri)"'

Repository: superset-sh/superset

Length of output: 1681


Remove tauri://localhost from CORS_ORIGINS — this is an Electron-only app, not Tauri.

The desktop application uses Electron exclusively (electron-vite, electron-builder). Tauri is not present in the codebase. The tauri://localhost origin is unnecessary and should be removed from the configuration.

🤖 Prompt for AI Agents
In `@apps/streams/fly.toml` at line 13, The CORS_ORIGINS entry currently includes
an unnecessary Tauri origin; update the CORS_ORIGINS value (the CORS_ORIGINS
setting string) to remove "tauri://localhost" so only valid Electron/production
origins remain (e.g., "https://app.superset.sh,https://admin.superset.sh"),
ensuring you remove any extra commas so the CSV string remains well-formed.

Comment thread apps/streams/src/index.ts
Comment on lines +23 to +25
const corsOrigins = env.CORS_ORIGINS
? env.CORS_ORIGINS.split(",").map((o) => o.trim())
: undefined;
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.

⚠️ Potential issue | 🟡 Minor

Filter out empty strings after splitting CORS_ORIGINS.

A trailing comma (e.g., "https://a.com,https://b.com,") would produce an empty string element after split+trim. In server.ts, allowedOrigins.includes(origin) would then match origin === "", which is the deny sentinel — but more subtly, a bare "," would yield ["", ""], making the array truthy while containing no valid origins.

Proposed fix
 const corsOrigins = env.CORS_ORIGINS
-	? env.CORS_ORIGINS.split(",").map((o) => o.trim())
+	? env.CORS_ORIGINS.split(",").map((o) => o.trim()).filter(Boolean)
 	: undefined;
📝 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
const corsOrigins = env.CORS_ORIGINS
? env.CORS_ORIGINS.split(",").map((o) => o.trim())
: undefined;
const corsOrigins = env.CORS_ORIGINS
? env.CORS_ORIGINS.split(",").map((o) => o.trim()).filter(Boolean)
: undefined;
🤖 Prompt for AI Agents
In `@apps/streams/src/index.ts` around lines 23 - 25, After splitting and trimming
env.CORS_ORIGINS into corsOrigins, filter out empty strings so the resulting
array contains only valid origins; update the logic that builds corsOrigins in
apps/streams/src/index.ts (the corsOrigins variable) to remove entries where o
=== "" (e.g., via a .filter) so server.ts's allowedOrigins.includes(origin)
won't mistakenly match empty-string or have an array with no real origins.

@Kitenite Kitenite merged commit 5d3ef0c into main Feb 11, 2026
15 checks passed
@Kitenite Kitenite deleted the kitenite/prod-chat-issue branch February 11, 2026 22:28
@coderabbitai coderabbitai Bot mentioned this pull request Feb 18, 2026
4 tasks
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