Skip to content

feat(gateway): add configurable port for Remote Access Gateway - #82

Open
justemu wants to merge 2 commits into
ai4s-research:masterfrom
justemu:fix/gateway-configurable-port
Open

feat(gateway): add configurable port for Remote Access Gateway#82
justemu wants to merge 2 commits into
ai4s-research:masterfrom
justemu:fix/gateway-configurable-port

Conversation

@justemu

@justemu justemu commented Aug 6, 2026

Copy link
Copy Markdown

Summary

Adds a configurable port for the Remote Access Gateway, allowing users to specify a custom port instead of relying on the hardcoded PREFERRED_PORT = 4098 with automatic fallback to a random ephemeral port.

Closes #80.

Changes

  • gateway.rs: Add port: Option<u16> to Persisted struct; bind_listener tries configured port first, then PREFERRED_PORT (4098), then random; set_gateway_config accepts optional port parameter; GatewayStatus includes configuredPort
  • tauri.ts: Update GatewayStatus interface and setGatewayConfig to support port parameter
  • RemoteAccessCard.tsx: Add port input field with apply/reset controls in Settings UI
  • i18n: Add port UI keys to all 7 shipped locales (de/en/es/fr/ja/ko/zh-Hans)

Behavior

  • No port configured: identical to before (4098, then ephemeral on conflict)
  • Port configured: binds exactly that port; returns a clear error if occupied (no silent fallback)
  • Port reset: clears the configured port, reverts to 4098-then-ephemeral behavior
  • Mode/token changes: in-place update without rebind, even when gateway is on an ephemeral port (avoids unnecessary URL changes)

Port binding semantics

A configured port must bind exactly or return an error. The previous implementation silently fell back to PREFERRED_PORT or an ephemeral port when the configured port was occupied, while configuredPort still reported the requested value — a lie that would break firewall rules and reverse-proxy configurations.

The updated_in_place comparison uses p.port.is_none_or(|port| r.port == port) to avoid unnecessary rebinds when no port is configured and the gateway is running on an ephemeral port (because PREFERRED_PORT was occupied at startup).

Integer validation

  • Rust: Option<u16> type guarantees valid range; port.filter(|n| n > 0) rejects port 0
  • TypeScript: Number.isInteger(n) && n >= 1 && n <= 65535 validates before sending to Tauri

Locale parity

All 7 shipped locales (de/en/es/fr/ja/ko/zh-Hans) include the port UI keys: port, portHint, portInvalid, portApply, portReset.

Tests (Rust)

  • configured_port_roundtrip_persistence — port survives write → read
  • configured_port_absent_means_none — no port line → None
  • configured_port_occupied_returns_error — occupied configured port fails (no fallback)
  • unconfigured_port_falls_back_to_ephemeral — 4098 occupied → ephemeral
  • configured_port_zero_is_treated_as_none — port 0 filtered to None

Backward Compatibility

Existing config files without a port line continue to work unchanged. The port parameter is optional in set_gateway_config.

@noahbenjamin1994 noahbenjamin1994 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for adding the configuration surface, but this needs changes before merge: a configured port must bind exactly or return a clear error rather than silently falling back while configuredPort still reports the requested value; when 4098 is occupied and the unconfigured gateway is running on an ephemeral port, the current r.port comparison forces an unnecessary rebind on ordinary mode changes and changes the URL again; and adding keys only to the English locale fails the repository's strict locale-parity test. Please fix those semantics, validate integer input, and add Rust/UI coverage for persistence, occupied configured ports, ephemeral-port in-place updates, reset behavior, and all shipped locales.

justemu added a commit to justemu/open-science-1 that referenced this pull request Aug 8, 2026
…alidation, tests

Address review feedback on ai4s-research#82:

- bind_listener: configured port must bind exactly or return a clear error
  (no silent fallback while configuredPort still reports the requested value)
- updated_in_place: skip port comparison when no port is configured to avoid
  unnecessary rebind when PREFERRED_PORT is occupied and gateway is on an
  ephemeral port
- Locale parity: add port i18n keys to all 7 shipped locales (de/es/fr/ja/ko/zh-Hans)
- Integer validation: use Number.isInteger() instead of Number.isNaN() in TS
- Tests: 5 new Rust tests covering persistence roundtrip, absent port, occupied
  configured port error, ephemeral fallback, and port-0-as-none
- Add 'port' field to gateway.txt persisted config
- bind_listener tries configured port first, then PREFERRED_PORT (4098), then random
- set_gateway_config accepts optional port parameter
- GatewayStatus includes configuredPort for UI display
- Settings UI adds port input with apply/reset controls
- Closes ai4s-research#80

The hardcoded PREFERRED_PORT = 4098 with automatic fallback to a random
ephemeral port causes issues on multi-user servers and makes reverse-proxy
configuration unreliable. This change allows administrators to configure
a specific port via the Settings UI, while preserving backward-compatible
behavior when no port is configured.
…alidation, tests

Address review feedback on ai4s-research#82:

- bind_listener: configured port must bind exactly or return a clear error
  (no silent fallback while configuredPort still reports the requested value)
- updated_in_place: skip port comparison when no port is configured to avoid
  unnecessary rebind when PREFERRED_PORT is occupied and gateway is on an
  ephemeral port
- Locale parity: add port i18n keys to all 7 shipped locales (de/es/fr/ja/ko/zh-Hans)
- Integer validation: use Number.isInteger() instead of Number.isNaN() in TS
- Tests: 5 new Rust tests covering persistence roundtrip, absent port, occupied
  configured port error, ephemeral fallback, and port-0-as-none
@justemu
justemu force-pushed the fix/gateway-configurable-port branch from c70853e to 8d7d659 Compare August 12, 2026 16:35
@justemu

justemu commented Aug 12, 2026

Copy link
Copy Markdown
Author

Hi @noahbenjamin1994, thanks for the thorough review!

We've addressed all your feedback in the follow-up commit (c70853e) and rebased onto the latest master to resolve merge conflicts. Here's a summary of what the fix commit covers:

Configured port binds exactlybind_listener now tries the configured port first; if occupied, it returns a clear error instead of silently falling back while configuredPort still reports the requested value.

Ephemeral port rebind fixupdated_in_place skips the port comparison when no port is configured, avoiding unnecessary rebinds when PREFERRED_PORT is occupied and the gateway is running on an ephemeral port.

Locale parity — Port i18n keys (port, portHint, portInvalid, portApply, portReset) added to all 7 shipped locales (de/en/es/fr/ja/ko/zh-Hans).

Integer validation — TypeScript uses Number.isInteger() instead of Number.isNaN() for port input validation.

Tests — 5 new Rust tests: configured_port_roundtrip_persistence, configured_port_absent_means_none, configured_port_occupied_returns_error, unconfigured_port_falls_back_to_ephemeral, configured_port_zero_is_treated_as_none.

The branch has been rebased onto the latest master (was 47 commits behind) to resolve merge conflicts with the recent ACP feature additions. Could you please re-review when you get a chance? Thanks!

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.

[Enhancement] Add configurable port for the Remote Access Gateway

2 participants