Skip to content

feat(preview): adapt browser HTTP over runner tunnels#993

Merged
DorianZheng merged 12 commits into
boxlite-ai:mainfrom
G4614:codex/box-endpoint-url-fd
Jul 23, 2026
Merged

feat(preview): adapt browser HTTP over runner tunnels#993
DorianZheng merged 12 commits into
boxlite-ai:mainfrom
G4614:codex/box-endpoint-url-fd

Conversation

@G4614

@G4614 G4614 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Route browser HTTP previews through the edge proxy over the runner CONNECT tunnel merged in PR #995.

Test plan:

  • cd apps/common-go && go test -race ./pkg/proxy
  • cd apps/proxy && go test -race ./pkg/proxy
  • cd apps/api && yarn jest --passWithNoTests=true --runInBand src/box/services/box.service.spec.ts --testNamePattern='^BoxService preview URLs'
  • cd apps/api && yarn prettier --check src/box/dto/port-preview-url.dto.ts src/box/services/box.service.ts src/box/services/box.service.spec.ts
  • Pre-push full matrix: local libkrunfw/e2fsprogs native build environment failed.

Summary by CodeRabbit

  • New Features
    • Preview URLs now use DNS-safe encoded box identifiers for service ports and network tunnels.
    • Proxy routing improves guest-port handling and supports robust bidirectional stream proxying.
    • Box activity refresh is more consistently applied during active proxy sessions.
  • Bug Fixes
    • Non-terminal port preview URLs are now generated instead of being rejected.
    • Forwarded header handling is tightened so untrusted client values are not propagated.
    • Box creation defaults to public when omitted (and respects an explicit false); legacy preview formats remain supported.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Preview URL and network tunnel flow

Layer / File(s) Summary
API preview contracts and tunnel entrypoints
apps/api-client-go/api/openapi.yaml, apps/api/src/box/dto/*, apps/api/src/box/services/*
Preview examples and generated URLs use encoded box IDs; box creation and warm-pool assignment default public to true.
Shared proxy rewriting and stream primitives
apps/common-go/pkg/proxy/*
Proxy targets and reverse-proxy rewriting now control destination URLs, queries, hosts, forwarding headers, request headers, and transports.
Proxy target resolution and activity lifecycle
apps/proxy/pkg/proxy/*
Direct-preview IDs are decoded and validated, guest and terminal targets are selected, activity updates and cache handling are added, and CONNECT shutdown handling is wired in.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GetProxyTarget
  participant guestPortTransport
  participant Runner
  Client->>GetProxyTarget: Request preview host and path
  GetProxyTarget->>GetProxyTarget: Decode box ID and select target port
  GetProxyTarget->>guestPortTransport: Select guest-port transport
  guestPortTransport->>Runner: Establish HTTP CONNECT tunnel
  Runner-->>guestPortTransport: Return guest connection
  guestPortTransport-->>Client: Relay the guest request
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: routing browser HTTP previews through runner tunnels.
Description check ✅ Passed The description includes a clear summary and verification steps, with only minor template deviations like a missing explicit Changes section.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@G4614 G4614 closed this Jul 17, 2026
@G4614 G4614 reopened this Jul 22, 2026
@G4614
G4614 force-pushed the codex/box-endpoint-url-fd branch from 3b944f6 to 063d398 Compare July 22, 2026 10:51
@G4614
G4614 marked this pull request as ready for review July 22, 2026 10:53
@G4614
G4614 requested a review from a team as a code owner July 22, 2026 10:53
@boxlite-agent

boxlite-agent Bot commented Jul 22, 2026

Copy link
Copy Markdown

📦 BoxLite review — couldn't complete

claude exited 1

stdout:
{"is_error":true,"duration_api_ms":0,"num_turns":1,"stop_reason":"stop_sequence","session_id":"981faab9-0fee-4177-a68c-f5afb2b870e2","total_cost_usd":0,"usage":{"input_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{},"permission_denials":[],"terminal_reason":"api_error","fast_mode_state":"off","subtype":"success","api_error_status":403,"result":"Your organization has disabled Claude subscription access for Claude Code · Use an Anthropic API key instead, or ask your admin to enable access","type":"result","duration_ms":400,"uuid":"eef573e7-2264-41d5-add0-52bc90df8e69"}

stderr:
<empty>

powered by BoxLite

@G4614
G4614 enabled auto-merge July 22, 2026 10:53
@G4614 G4614 changed the title feat(rust-sdk): return URL or fd from tunnel endpoint feat(preview): support browser preview Jul 22, 2026

@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: 3

🧹 Nitpick comments (2)
apps/runner/pkg/api/controllers/proxy.go (1)

58-105: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding handler-level tests for ProxyRequest/BoxliteNetworkProxy.

The existing tests exercise the private helper functions (toolboxEscapedPath, parseGuestPortProxyPath, etc.) individually, but there's no httptest-driven test exercising the gin handlers themselves end-to-end (terminal dispatch, guest-port dispatch, 400 on bad port, 410 on legacy path). Given this dispatch logic was flagged as medium complexity, a couple of route-level tests would give more confidence in the wiring.

🤖 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 `@apps/runner/pkg/api/controllers/proxy.go` around lines 58 - 105, The
handler-level dispatch paths in ProxyRequest and BoxliteNetworkProxy lack
end-to-end coverage. Add httptest-driven Gin route tests covering terminal
dispatch, guest-port dispatch, invalid-port 400 responses, and legacy-path 410
responses, reusing existing test helpers and keeping private helper tests
unchanged.
apps/proxy/pkg/proxy/get_box_target.go (1)

232-232: 🚀 Performance & Scalability | 🔵 Trivial

Cache TTL for box-public status dropped from 1 hour to 3 seconds.

This trades a large increase in IsBoxPublic call volume (every unique box re-checked every 3s under load) for fresher public/private status. Likely intentional given the "defaults new boxes to public" change elsewhere in this stack, but worth confirming the API side (IsBoxPublic) can absorb the added QPS at scale.

🤖 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 `@apps/proxy/pkg/proxy/get_box_target.go` at line 232, The box-public cache TTL
in the cache update around boxPublicCache.Set was reduced to 3 seconds,
potentially increasing IsBoxPublic API load. Confirm the shorter TTL is
intentional and that IsBoxPublic can handle the added QPS; otherwise restore the
established 1-hour TTL or use an approved TTL balancing freshness and capacity.
🤖 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 `@apps/proxy/pkg/proxy/get_box_target_test.go`:
- Around line 1-2: Update the SPDX header in
apps/proxy/pkg/proxy/get_box_target_test.go lines 1-2 to use Apache-2.0 instead
of AGPL-3.0. Also update apps/common-go/pkg/proxy/proxy_test.go lines 1-2 to use
Apache-2.0; both sites require the same header correction.

In `@apps/proxy/pkg/proxy/get_box_target.go`:
- Around line 380-417: Move the shouldPoll ticker startup in updateLastActivity
before the fallible cache Has and UpdateLastActivity calls, while preserving the
existing polling interval, done-channel termination, and recursive
updateLastActivity invocation. Ensure the polling goroutine is scheduled
unconditionally for shouldPoll connections so initial cache or API errors do not
prevent later heartbeats.

In `@apps/runner/pkg/boxlite/guest_port_tunnel.go`:
- Around line 83-103: Update NewGuestPortTransport to configure an explicit
timeout for guest-port connections, including ResponseHeaderTimeout and, where
needed, a dial deadline for DialGuestPort, using the existing timeout
configuration or a sensible bounded duration. Ensure handleGuestPortProxy
requests cannot remain blocked indefinitely while preserving the current network
validation and error logging.

---

Nitpick comments:
In `@apps/proxy/pkg/proxy/get_box_target.go`:
- Line 232: The box-public cache TTL in the cache update around
boxPublicCache.Set was reduced to 3 seconds, potentially increasing IsBoxPublic
API load. Confirm the shorter TTL is intentional and that IsBoxPublic can handle
the added QPS; otherwise restore the established 1-hour TTL or use an approved
TTL balancing freshness and capacity.

In `@apps/runner/pkg/api/controllers/proxy.go`:
- Around line 58-105: The handler-level dispatch paths in ProxyRequest and
BoxliteNetworkProxy lack end-to-end coverage. Add httptest-driven Gin route
tests covering terminal dispatch, guest-port dispatch, invalid-port 400
responses, and legacy-path 410 responses, reusing existing test helpers and
keeping private helper tests unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ac1ce7e3-6bcc-4c37-82dd-a94c3dac2c3d

📥 Commits

Reviewing files that changed from the base of the PR and between 43ebd34 and 063d398.

📒 Files selected for processing (17)
  • apps/api-client-go/api/openapi.yaml
  • apps/api/src/box/dto/create-box.dto.ts
  • apps/api/src/box/dto/port-preview-url.dto.ts
  • apps/api/src/box/services/box.service.spec.ts
  • apps/api/src/box/services/box.service.ts
  • apps/common-go/pkg/proxy/proxy.go
  • apps/common-go/pkg/proxy/proxy_test.go
  • apps/proxy/pkg/proxy/get_box_target.go
  • apps/proxy/pkg/proxy/get_box_target_test.go
  • apps/proxy/pkg/proxy/proxy.go
  • apps/runner/pkg/api/controllers/proxy.go
  • apps/runner/pkg/api/controllers/proxy_integration_test.go
  • apps/runner/pkg/api/controllers/proxy_test.go
  • apps/runner/pkg/api/server.go
  • apps/runner/pkg/boxlite/client.go
  • apps/runner/pkg/boxlite/guest_port_tunnel.go
  • apps/runner/pkg/boxlite/guest_port_tunnel_test.go

Comment thread apps/proxy/pkg/proxy/get_box_target_test.go
Comment thread apps/proxy/pkg/proxy/get_box_target.go
Comment thread apps/runner/pkg/boxlite/guest_port_tunnel.go Outdated
@G4614
G4614 force-pushed the codex/box-endpoint-url-fd branch from 063d398 to b3070cb Compare July 22, 2026 11:27
@G4614 G4614 changed the title feat(preview): support browser preview feat(preview): adapt browser HTTP over runner tunnels Jul 22, 2026
@G4614
G4614 force-pushed the codex/box-endpoint-url-fd branch from b3070cb to 6fcfd86 Compare July 22, 2026 11:29

@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: 4

♻️ Duplicate comments (1)
apps/proxy/pkg/proxy/get_box_target.go (1)

403-440: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Transient error at connection start permanently disables activity polling.

updateLastActivity returns early on any cache Has / UpdateLastActivity error before reaching the if shouldPoll ticker block. Since it is invoked once per connection with shouldPoll=true, a single transient Redis/API blip means the heartbeat ticker is never scheduled for the rest of that (potentially long-lived) connection. Schedule the polling goroutine before the fallible calls.

🤖 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 `@apps/proxy/pkg/proxy/get_box_target.go` around lines 403 - 440, Move the
shouldPoll ticker goroutine setup in updateLastActivity before the fallible
cache Has and BoxAPI.UpdateLastActivity calls, so transient errors cannot
prevent polling from being scheduled. Preserve the existing ticker interval,
recursive updateLastActivity invocation, done-channel shutdown, and one-time
setup behavior.
🧹 Nitpick comments (1)
apps/proxy/pkg/proxy/get_box_target.go (1)

255-255: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Public-status cache TTL dropped 1h → 3s greatly increases IsBoxPublic call volume.

For frequently accessed public previews this is a ~1200x increase in cache misses hitting PreviewAPI.IsBoxPublic on the hot request path. If the goal is faster propagation of visibility changes, consider a middle-ground TTL (e.g. tens of seconds) or event-driven invalidation to bound API load.

🤖 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 `@apps/proxy/pkg/proxy/get_box_target.go` at line 255, Update the TTL used by
boxPublicCache.Set in the public-status caching flow to a middle-ground duration
of at least tens of seconds, rather than 3 seconds, while preserving the
existing cache behavior and key.
🤖 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 `@apps/common-go/pkg/proxy/proxy.go`:
- Around line 151-159: Update the header cleanup block before
req.SetXForwarded() to delete the inbound X-Forwarded-For header, ensuring
SetXForwarded() builds the upstream value without preserving client-supplied
entries.

In `@apps/proxy/pkg/proxy/proxy.go`:
- Around line 43-49: Make the activity-poll stop-hook registration race-safe
around stopActivityPoll and the cleanup at line 118: install an idempotent
cancellation controller in the Gin context before starting the detached poll,
then have GetProxyTarget register/use that controller rather than publishing the
stop function afterward. Ensure cleanup can cancel the poll whether it runs
before or after poll initialization, while preserving single execution of
cancellation.
- Around line 195-197: Update the HTTP server configuration in the proxy startup
flow around the Addr, Handler, and ReadHeaderTimeout fields to add
configuration-backed ReadTimeout and IdleTimeout values. Ensure the selected
ReadTimeout explicitly accommodates legitimate upload durations while still
bounding slow request bodies, and preserve the existing handler and
header-timeout behavior.

In `@apps/proxy/pkg/proxy/tunnel.go`:
- Around line 65-77: Update the tunnel relay in the handler around Hijack and
ProxyBidirectionalStream to wrap clientConn with
common_proxy.NewBufferedConn(clientConn, buffered.Reader) after writing and
flushing the CONNECT response, then pass that wrapped connection to
ProxyBidirectionalStream so buffered client bytes are preserved.

---

Duplicate comments:
In `@apps/proxy/pkg/proxy/get_box_target.go`:
- Around line 403-440: Move the shouldPoll ticker goroutine setup in
updateLastActivity before the fallible cache Has and BoxAPI.UpdateLastActivity
calls, so transient errors cannot prevent polling from being scheduled. Preserve
the existing ticker interval, recursive updateLastActivity invocation,
done-channel shutdown, and one-time setup behavior.

---

Nitpick comments:
In `@apps/proxy/pkg/proxy/get_box_target.go`:
- Line 255: Update the TTL used by boxPublicCache.Set in the public-status
caching flow to a middle-ground duration of at least tens of seconds, rather
than 3 seconds, while preserving the existing cache behavior and key.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a5002e2c-6175-46fb-896d-7472f67c5585

📥 Commits

Reviewing files that changed from the base of the PR and between 063d398 and 6fcfd86.

📒 Files selected for processing (21)
  • apps/api-client-go/api/openapi.yaml
  • apps/api/src/box/dto/create-box.dto.ts
  • apps/api/src/box/dto/port-preview-url.dto.ts
  • apps/api/src/box/services/box.service.spec.ts
  • apps/api/src/box/services/box.service.ts
  • apps/api/src/boxlite-rest/boxlite-proxy.controller.spec.ts
  • apps/api/src/boxlite-rest/boxlite-proxy.controller.ts
  • apps/api/src/boxlite-rest/boxlite-rest-routing.spec.ts
  • apps/common-go/pkg/proxy/proxy.go
  • apps/common-go/pkg/proxy/proxy_test.go
  • apps/common-go/pkg/proxy/tunnel_test.go
  • apps/infra/sst.config.ts
  • apps/proxy/pkg/proxy/get_box_target.go
  • apps/proxy/pkg/proxy/get_box_target_test.go
  • apps/proxy/pkg/proxy/proxy.go
  • apps/proxy/pkg/proxy/tunnel.go
  • apps/proxy/pkg/proxy/tunnel_test.go
  • apps/runner/pkg/api/controllers/network_tunnel.go
  • apps/runner/pkg/api/controllers/network_tunnel_test.go
  • apps/runner/pkg/api/server.go
  • apps/runner/pkg/boxlite/guest_port_tunnel.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/api-client-go/api/openapi.yaml
  • apps/common-go/pkg/proxy/proxy_test.go
  • apps/api/src/box/dto/create-box.dto.ts
  • apps/api/src/box/dto/port-preview-url.dto.ts
  • apps/api/src/box/services/box.service.ts

Comment thread apps/common-go/pkg/proxy/proxy.go
Comment thread apps/proxy/pkg/proxy/proxy.go
Comment thread apps/proxy/pkg/proxy/proxy.go
Comment thread apps/proxy/pkg/proxy/tunnel.go Outdated
@G4614
G4614 force-pushed the codex/box-endpoint-url-fd branch from 0e2d8b2 to 6cf185f Compare July 23, 2026 03:57

@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 `@apps/proxy/pkg/proxy/get_box_target.go`:
- Around line 112-119: Update the invalid target-port branch in GetProxyTarget
to wrap the ParseUint validation failure with
ctx.Error(common_errors.NewBadRequestError(...)) before returning, matching the
existing validation-error handling and ensuring malformed public-box Host
headers produce a 400 response.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0901e7bb-51f1-41eb-8ad3-25f03b47904a

📥 Commits

Reviewing files that changed from the base of the PR and between 0e2d8b2 and 6cf185f.

📒 Files selected for processing (9)
  • apps/api-client-go/api/openapi.yaml
  • apps/api/src/box/dto/port-preview-url.dto.ts
  • apps/api/src/box/services/box.service.spec.ts
  • apps/api/src/box/services/box.service.ts
  • apps/common-go/pkg/proxy/proxy.go
  • apps/common-go/pkg/proxy/proxy_test.go
  • apps/proxy/pkg/proxy/get_box_target.go
  • apps/proxy/pkg/proxy/get_box_target_test.go
  • apps/proxy/pkg/proxy/proxy.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/api/src/box/dto/port-preview-url.dto.ts
  • apps/common-go/pkg/proxy/proxy_test.go
  • apps/api-client-go/api/openapi.yaml
  • apps/proxy/pkg/proxy/get_box_target_test.go
  • apps/common-go/pkg/proxy/proxy.go
  • apps/api/src/box/services/box.service.ts

Comment thread apps/proxy/pkg/proxy/get_box_target.go
@DorianZheng
DorianZheng disabled auto-merge July 23, 2026 06:47
@DorianZheng
DorianZheng merged commit 015503d into boxlite-ai:main Jul 23, 2026
23 checks passed
@G4614
G4614 deleted the codex/box-endpoint-url-fd branch July 23, 2026 08:16
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