Skip to content

Refactor split ports - #140

Merged
diegosouzapw merged 4 commits into
cheaper-inference:mainfrom
npmSteven:refactor-split-ports
Feb 27, 2026
Merged

diegosouzapw merged 4 commits into
cheaper-inference:mainfrom
npmSteven:refactor-split-ports

Conversation

@npmSteven

@npmSteven npmSteven commented Feb 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR adds split-port runtime support so OmniRoute can serve the dashboard and OpenAI-compatible API on different ports, while keeping backward compatibility with existing PORT-based setups.

Why

Some deployments need separate listeners for UI and API traffic (network policy, reverse proxies, container routing). Previously, both were tightly coupled to a single port.
What Changed

  • Added centralized runtime port resolution in src/lib/runtime/ports.ts:
    • PORT remains canonical/base
    • API_PORT optionally overrides API listener
    • DASHBOARD_PORT optionally overrides Next/dashboard listener
    • OMNIROUTE_PORT preserves canonical port in wrapped runtimes
  • Added API bridge server in src/lib/apiBridgeServer.ts and initialized it in src/instrumentation.ts:
    • Starts only when API_PORT !== DASHBOARD_PORT
    • Proxies OpenAI-compatible routes to dashboard listener
    • Returns 404 for non-API routes on API port
  • Updated startup execution flow:
    • New scripts: scripts/run-next.mjs, scripts/run-standalone.mjs
    • package.json dev/start now use wrapper script
    • Docker runtime now starts via run-standalone.mjs
    • Healthchecks in Dockerfile and compose files now target resolved dashboard port
  • Updated settings and UI consumers:
    • GET /api/settings now returns runtimePorts, apiPort, and dashboardPort
    • CLI tools page and onboarding now derive displayed endpoint from runtime API port
    • CLI tool config matching improved with normalized base URL and omnirouteManaged marker
  • Updated OAuth/cloud/runtime defaults:
    • OAuth server fallback now resolves to effective API port
    • Cloud sync internal base URL now follows dashboard port
  • Docs and env updates:
    • .env.example and README now document split-port mode and behavior
      Backward Compatibility
  • Existing single-port setups continue to work with just PORT.
  • Split-port mode is opt-in via API_PORT and/or DASHBOARD_PORT.

- Updated .env.example to include optional split ports for API and dashboard.
- Modified docker-compose files to dynamically use the configured ports.
- Introduced a new script (run-standalone.mjs) for running the server with environment-specific ports.
- Implemented an API bridge server to handle OpenAI-compatible routes when using split ports.
- Updated README and CLI tool documentation to reflect changes in port usage and configuration.
- Enhanced various components to utilize the new port configuration, ensuring backward compatibility.
- Added API_HOST variable to .env.example for enhanced host configuration.
- Updated package.json scripts to utilize a new run-next.mjs script for development and production.
- Introduced run-next.mjs to manage Next.js server execution with dynamic port handling.
- Enhanced route.ts files to normalize API base URLs and improve configuration checks.
- Updated apiBridgeServer.ts to use API_HOST for server initialization.
- Expanded global TypeScript definitions to include API_HOST.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @npmSteven, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the application's port handling mechanism, enabling users to run the API and dashboard on separate network ports. This change provides greater control over network exposure, allowing for API-only deployments and more secure configurations. It involves introducing an API bridge for intelligent request routing and updating various parts of the codebase to support this new, more flexible port architecture.

Highlights

  • Flexible Port Configuration: Introduced dedicated environment variables (API_PORT, DASHBOARD_PORT) to allow separate ports for the API and the dashboard, enhancing deployment flexibility. The PORT variable now serves as a canonical base port, with API_PORT and DASHBOARD_PORT overriding it for specific services.
  • API Bridge Server: Implemented a new API bridge server that listens on the API_PORT and proxies only OpenAI-compatible requests to the DASHBOARD_PORT, ensuring that API-only clients can connect without exposing the full dashboard.
  • Runtime Port Management: Refactored the application's startup scripts (package.json, Dockerfile, bin/omniroute.mjs) to dynamically parse and manage PORT, API_PORT, and DASHBOARD_PORT from environment variables, improving consistency across different runtime environments (development, production, Docker).
  • Dynamic UI Updates: Updated dashboard components (CLIToolsPageClient.tsx, onboarding/page.tsx) to dynamically determine and display the correct API endpoint based on the configured API_PORT, ensuring accurate information for users.
  • Enhanced CLI Tool Detection: Improved the logic for detecting OmniRoute configuration in external CLI tools by checking for sk_omniroute API keys and dynamically configured localhost or 127.0.0.1 API endpoints, making detection more robust.
  • Documentation and Examples: Updated the README.md with clear instructions and examples for using the new split-port mode, including how to configure PORT, DASHBOARD_PORT, and NEXT_PUBLIC_BASE_URL.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • .env.example
    • Added comments for canonical/base port and optional split ports (API_PORT, API_HOST, DASHBOARD_PORT).
  • Dockerfile
    • Copied run-standalone.mjs into the image.
    • Updated the healthcheck command to dynamically use DASHBOARD_PORT or PORT.
    • Changed the default command (CMD) to execute run-standalone.mjs instead of server.js.
  • README.md
    • Updated the description for the omniroute --port command to specify it sets the API port.
    • Modified the 'After starting' section to show separate URLs for Dashboard and API based on dashboard-port and api-port.
    • Added a new section detailing the optional split-port mode with PORT and DASHBOARD_PORT environment variables.
    • Clarified that when ports are split, the API port serves only OpenAI-compatible routes.
    • Updated the development setup command to include DASHBOARD_PORT and set NEXT_PUBLIC_BASE_URL to the dashboard port.
    • Revised troubleshooting advice for 'Dashboard/API ports are wrong' to explain PORT, API_PORT, DASHBOARD_PORT, and NEXT_PUBLIC_BASE_URL.
  • bin/omniroute.mjs
    • Updated the help message to reflect custom API port usage and separate dashboard/API URLs.
    • Introduced a parsePort utility function for robust port parsing.
    • Modified port parsing logic to support PORT, API_PORT, and DASHBOARD_PORT environment variables.
    • Simplified error message for server not found.
    • Updated environment variables passed to the child process to include OMNIROUTE_PORT, PORT (for dashboard), DASHBOARD_PORT, and API_PORT.
    • Adjusted server ready detection condition for better readability.
    • Modified the onReady function to display separate dashboard and API URLs and open the dashboard URL in the browser.
  • docker-compose.prod.yml
    • Updated the healthcheck command to dynamically use DASHBOARD_PORT or PORT.
  • docker-compose.yml
    • Updated the healthcheck command to dynamically use DASHBOARD_PORT or PORT.
  • package.json
    • Modified the dev script to use node scripts/run-next.mjs dev.
    • Modified the start script to use node scripts/run-next.mjs start.
  • playwright.config.ts
    • Added dashboardPort and dashboardBaseUrl variables, dynamically resolving the dashboard port.
    • Updated baseURL in use configuration to use dashboardBaseUrl.
    • Updated webServer.url to use dashboardBaseUrl.
  • scripts/run-next.mjs
    • Added a new script to wrap Next.js dev and start commands, handling PORT, API_PORT, and DASHBOARD_PORT environment variables.
  • scripts/run-standalone.mjs
    • Added a new script to run the standalone server.js with proper PORT, API_PORT, and DASHBOARD_PORT environment variables.
  • src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx
    • Added apiBaseUrl state variable.
    • Updated fetchCloudSettings to retrieve apiPort from settings and construct apiBaseUrl.
    • Modified getApiBaseUrl to prioritize apiBaseUrl if available.
  • src/app/(dashboard)/dashboard/onboarding/page.tsx
    • Added apiEndpoint state variable, initialized to a default value.
    • Introduced resolveApiEndpoint function to dynamically construct the API endpoint based on apiPort.
    • Updated checkSetup to call resolveApiEndpoint with settings?.apiPort.
    • Changed the displayed API endpoint in the UI to use the dynamic apiEndpoint state.
  • src/app/api/cli-tools/guide-settings/[toolId]/route.ts
    • Normalized baseUrl by trimming and removing trailing slashes.
    • Added omnirouteManaged: true to the routerModel.
    • Introduced normalizeApiBase helper function for consistent URL comparison.
    • Enhanced the logic for finding existing OmniRoute models to include omnirouteManaged flag, normalized apiBase comparison, and checks for sk_omniroute in API keys.
  • src/app/api/cli-tools/status/route.ts
    • Imported getRuntimePorts and extracted apiPort.
    • Updated checkToolConfigStatus to include sk_omniroute, localhost:${apiPort}, and 127.0.0.1:${apiPort} in the configuration string check for better detection.
  • src/app/api/settings/route.ts
    • Imported getRuntimePorts.
    • Included runtimePorts, apiPort, and dashboardPort in the JSON response for GET requests.
  • src/instrumentation.ts
    • Imported and initialized initApiBridgeServer.
  • src/lib/apiBridgeServer.ts
    • Added a new module implementing an HTTP proxy server that listens on API_PORT.
    • Defined OPENAI_COMPAT_PATHS regex patterns to identify OpenAI-compatible routes.
    • Implemented isOpenAiCompatiblePath to check if a request path matches OpenAI patterns.
    • Implemented proxyRequest to forward requests to the DASHBOARD_PORT.
    • Added initApiBridgeServer to start the API bridge if apiPort and dashboardPort are different, handling port in use errors.
  • src/lib/oauth/config/index.ts
    • Added getDefaultApiServer function to dynamically determine the default API server URL based on OMNIROUTE_PORT and API_PORT.
    • Updated getServerCredentials to use getDefaultApiServer for the server property.
  • src/lib/runtime/ports.ts
    • Added a new utility module to parse and provide port, apiPort, and dashboardPort from environment variables.
    • Defined parsePort for safe integer parsing with fallbacks.
    • Exported RuntimePorts type.
    • Implemented getRuntimePorts to resolve all port configurations, considering OMNIROUTE_PORT for wrapped runtimes.
  • src/shared/services/cloudSyncScheduler.ts
    • Imported getRuntimePorts and extracted dashboardPort.
    • Updated INTERNAL_BASE_URL to use the dynamically resolved dashboardPort.
  • src/types/global.d.ts
    • Added new environment variables (API_PORT, API_HOST, DASHBOARD_PORT, OMNIROUTE_PORT) to the NodeJS.ProcessEnv interface.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the application to support splitting the API and dashboard servers onto different ports, introducing wrapper scripts, an API bridge for proxying requests, and a centralized module for port resolution. A critical security vulnerability was identified where the API bridge defaults to listening on all network interfaces (0.0.0.0) and proxies to unauthenticated routes, which could allow unauthorized remote access to the AI proxy. Furthermore, the review highlighted several areas for improvement: duplicated logic for port parsing, a hardcoded port number in an API route, inconsistent command-line argument parsing for ports, and a duplicated Docker healthcheck command. Minor issues like information leakage of port configurations were also noted.

Comment thread src/lib/apiBridgeServer.ts Outdated
const { apiPort, dashboardPort } = getRuntimePorts();
if (apiPort === dashboardPort) return;

const host = process.env.API_HOST || "0.0.0.0";

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.

security-critical critical

The API bridge server defaults to listening on 0.0.0.0 (all network interfaces), making it accessible to anyone on the same network. This is particularly dangerous because the /api/v1/ routes it proxies to are explicitly excluded from authentication checks in the application's middleware (src/proxy.ts, line 40) and do not implement internal authentication (as evidenced by src/app/api/v1/route.ts). This combination allows unauthorized remote attackers to access the AI proxy, potentially leading to significant financial costs by abusing configured AI providers. It is highly recommended to change the default listening host to 127.0.0.1 to ensure the API bridge is only accessible locally unless explicitly configured otherwise by the user.

Suggested change
const host = process.env.API_HOST || "0.0.0.0";
const host = process.env.API_HOST || "127.0.0.1";

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.

@diegosouzapw I've swapped it to 127.0.0.1 for now but if you want it switched to 0.0.0.0 just let me know

Comment thread src/lib/oauth/config/index.ts Resolved
Comment thread Dockerfile Resolved
Comment thread bin/omniroute.mjs Resolved
Comment thread scripts/run-standalone.mjs Resolved
Comment thread src/lib/apiBridgeServer.ts Resolved
Comment thread src/lib/apiBridgeServer.ts Resolved
Comment thread src/lib/oauth/config/index.ts Resolved
Comment thread docker-compose.yml Resolved
Comment thread docker-compose.prod.yml Resolved
@kilo-code-bot

kilo-code-bot Bot commented Feb 26, 2026 •

Copy link
Copy Markdown

Code Review Summary

Status: 4 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 1

Note: The OAuth config issue (API_PORT vs DASHBOARD_PORT) has been FIXED in this PR - it now correctly uses dashboardPort.

Issue Details (click to expand)

WARNING

File Line Issue
src/lib/apiBridgeServer.ts 17 No timeout on proxy requests - connections could hang indefinitely, causing resource exhaustion
docker-compose.yml 35 Port mapping ${PORT:-20128}:20128 doesn't support split ports in Docker
docker-compose.prod.yml 42 Port mapping "20130:20128" doesn't support split ports in production

SUGGESTION

File Line Issue
src/lib/apiBridgeServer.ts 47 Empty/whitespace line inside declare global block
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Issue
src/lib/apiBridgeServer.ts WebSocket/upgrade requests not handled - any WebSocket connections to the API port will fail
bin/omniroute.mjs vs src/lib/runtime/ports.ts Inconsistent port validation (parsed > 0 vs parsed < 1) - both are functionally correct but inconsistent
Files Reviewed (23 files)
  • .env.example - Configuration documentation
  • Dockerfile - Startup script changes
  • README.md - Updated documentation
  • bin/omniroute.mjs - CLI startup with port configuration
  • docker-compose.yml - Dev Docker config
  • docker-compose.prod.yml - Production Docker config
  • package.json - Script updates
  • playwright.config.ts - Test configuration
  • scripts/run-next.mjs - Next.js wrapper
  • scripts/run-standalone.mjs - Standalone runner
  • src/lib/apiBridgeServer.ts - New API bridge server
  • src/lib/oauth/config/index.ts - OAuth configuration (FIXED: now uses dashboardPort)
  • src/lib/runtime/ports.ts - Runtime port utilities
  • src/app/api/cli-tools/status/route.ts - CLI status with port support
  • src/app/api/settings/route.ts - Settings with runtime ports
  • src/app/(dashboard)/dashboard/cli-tools/CLIToolsPageClient.tsx - CLI tools page
  • src/app/(dashboard)/dashboard/onboarding/page.tsx - Onboarding page
  • src/app/api/cli-tools/guide-settings/[toolId]/route.ts - CLI guide settings
  • src/instrumentation.ts - API bridge server initialization
  • src/shared/services/cloudSyncScheduler.ts - Cloud sync with dashboard port
  • src/types/global.d.ts - Type definitions for new env vars

Review Note: This PR introduces a significant architectural change allowing API and dashboard to run on separate ports. The core implementation is solid, and the OAuth config bug has been correctly fixed. The identified warnings should be addressed for production readiness, particularly the missing timeout on proxy requests.

- Updated .env.example to include optional production ports for API and dashboard.
- Modified docker-compose files to utilize dynamic port configuration.
- Introduced runtime-env.mjs for centralized port resolution and environment variable management.
- Refactored run-next.mjs and run-standalone.mjs to leverage new runtime port handling.
- Enhanced route.ts and apiBridgeServer.ts to utilize dynamic ports for improved API integration.
- Updated OAuth configuration to reflect changes in port management.
diegosouzapw added a commit that referenced this pull request Feb 27, 2026
Adds split-port runtime support: API and dashboard can run on separate ports.
- PORT remains canonical/base
- API_PORT optionally overrides API listener
- DASHBOARD_PORT optionally overrides dashboard listener
- API bridge server proxies OpenAI-compatible routes with 30s timeout
- Centralized port resolution in src/lib/runtime/ports.ts
- Unit tests for runtime port resolution (14 tests)
- Healthcheck extracted into scripts/healthcheck.mjs
@diegosouzapw
diegosouzapw merged commit 21fd504 into cheaper-inference:main Feb 27, 2026
10 checks passed
@diegosouzapw

Copy link
Copy Markdown
Collaborator

✅ Merged to main with additional polish!

Improvements applied before merge:

  • Added 30s proxy timeout to prevent resource exhaustion
  • Extracted healthcheck into scripts/healthcheck.mjs (removes duplicated inline code from Dockerfile and compose files)
  • Added 14 unit tests for parsePort and resolveRuntimePorts
  • Fixed formatting in declare global block

Thank you for this excellent contribution, @npmSteven! 🎉

diegosouzapw added a commit that referenced this pull request Mar 7, 2026
Adds split-port runtime support: API and dashboard can run on separate ports.
- PORT remains canonical/base
- API_PORT optionally overrides API listener
- DASHBOARD_PORT optionally overrides dashboard listener
- API bridge server proxies OpenAI-compatible routes with 30s timeout
- Centralized port resolution in src/lib/runtime/ports.ts
- Unit tests for runtime port resolution (14 tests)
- Healthcheck extracted into scripts/healthcheck.mjs
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
… polish fixes

Adds split-port runtime support: API and dashboard can run on separate ports.
- PORT remains canonical/base
- API_PORT optionally overrides API listener
- DASHBOARD_PORT optionally overrides dashboard listener
- API bridge server proxies OpenAI-compatible routes with 30s timeout
- Centralized port resolution in src/lib/runtime/ports.ts
- Unit tests for runtime port resolution (14 tests)
- Healthcheck extracted into scripts/healthcheck.mjs
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.

2 participants