Skip to content

feat(rest): integrate box network tunnel backend#992

Open
G4614 wants to merge 14 commits into
boxlite-ai:mainfrom
G4614:codex/rust-network-rest-standalone
Open

feat(rest): integrate box network tunnel backend#992
G4614 wants to merge 14 commits into
boxlite-ai:mainfrom
G4614:codex/rust-network-rest-standalone

Conversation

@G4614

@G4614 G4614 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Integrate the REST network tunnel backend on top of the Rust core tunnel handle from PR991.

This PR also consolidates the tunnel endpoint work from PR993:

  • tunnel() eagerly establishes both local and REST tunnel transports.
  • endpoint() returns BoxEndpoint::Fd for local boxes and BoxEndpoint::Url for REST boxes.
  • open_stream() consumes the already-prepared connection; connect() remains a deprecated compatibility alias.

PR993 remains draft because its local endpoint work is now included here.

Test plan:

  • cargo fmt --all -- --check
  • git diff --check
  • Focused Rust tunnel tests are blocked locally because the dependency cache lacks aws-lc-sys; run in CI after PR991 lands.
  • Run Linux/debug-machine REST e2e after PR991 lands.

Dependency: this draft is intentionally stacked on PR991 and is not expected to compile independently before PR991 is merged.

Summary by CodeRabbit

  • New Features
    • Added per-box network tunnel description via OpenAPI, returning a public service URL.
    • Implemented HTTP CONNECT-based tunnel setup by port (with optional bearer auth).
    • Tunnels now expose a concrete service endpoint that can be backed by a URL or local FD.
  • Improvements
    • REST-backed tunneling is enabled for cloned boxes, with endpoint/stream prepared upfront for immediate use.
    • Local tunnel targets are validated and remote/local stream preparation happens eagerly.
  • Bug Fixes
    • Clear error when a local tunnel cannot provide a public endpoint (missing FD).
    • Enhanced REST error mapping for structured JSON failures.

@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

The PR adds a box-scoped network tunnel API, changes BoxTunnel to use prepared URL or file-descriptor endpoints, and enables REST-backed authenticated HTTP CONNECT streams for REST-created boxes.

Changes

Network tunnel support

Layer / File(s) Summary
Network tunnel API contracts
openapi/box.openapi.yaml
Defines the Network tag, tunnel operation, required port parameter, and BoxServiceEndpoint response schema.
Prepared tunnel model
src/boxlite/src/litebox/network.rs, src/boxlite/src/litebox/box_impl.rs, src/boxlite/src/litebox/mod.rs, src/boxlite/src/runtime/backend.rs
Adds URL and file-descriptor endpoints, consumes prepared streams through open_stream, updates local tunnel creation, exports BoxEndpoint, and revises related tests and documentation.
REST HTTP CONNECT integration
src/boxlite/src/rest/client.rs, src/boxlite/src/rest/litebox.rs, src/boxlite/src/rest/runtime.rs, src/boxlite/Cargo.toml
Adds tunnel description and authenticated HTTP CONNECT methods, wires REST network backends for regular and cloned boxes, adds TLS transport dependencies, and tests CONNECT stream establishment.

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

Sequence Diagram(s)

sequenceDiagram
  participant RestBox
  participant ApiClient
  participant BoxTunnel
  participant UnixStream
  RestBox->>ApiClient: describe_box_tunnel(box_id, port)
  ApiClient-->>RestBox: Return public URL
  RestBox->>ApiClient: connect_box_network_tunnel(box_id, port)
  ApiClient->>UnixStream: Upgrade HTTP CONNECT
  UnixStream-->>RestBox: Return prepared stream
  RestBox->>BoxTunnel: Combine URL and stream
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 69.23% 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 is concise and clearly summarizes the main change: integrating the box network tunnel backend.
Description check ✅ Passed The description covers the summary, changes, verification steps, and rollout dependency, with only minor template-heading differences.
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 force-pushed the codex/rust-network-rest-standalone branch from 5cecb35 to 6b39f16 Compare July 15, 2026 13:17
@cla-assistant

cla-assistant Bot commented Jul 16, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@G4614
G4614 force-pushed the codex/rust-network-rest-standalone branch from c9ad161 to bf8f74c Compare July 16, 2026 11:31
@G4614
G4614 marked this pull request as ready for review July 16, 2026 11:47
@G4614
G4614 requested a review from a team as a code owner July 16, 2026 11:47
@boxlite-agent

boxlite-agent Bot commented Jul 16, 2026

Copy link
Copy Markdown

📦 BoxLite review — couldn't complete

review model timed out after 36 minutes without a /publish callback
repo: boxlite-ai/boxlite
pr: 992
head: 22885218b069353ca203d7935a10ad57fc694476
box: pr-review-boxlite-992-mrohgo2g
last stage: model started
stage detail: claude
stage updated: 2026-07-17T05:14:22.581Z

powered by BoxLite

@G4614
G4614 enabled auto-merge July 16, 2026 11:47

@boxlite-agent boxlite-agent 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.

📦 BoxLite review — 2 issues

Comment thread src/boxlite/src/litebox/network.rs Outdated
Comment on lines +21 to +29
fn try_clone(&self) -> BoxliteResult<Self> {
match self {
Self::Url(url) => Ok(Self::Url(url.clone())),
Self::Fd(fd) => fd
.try_clone()
.map(Self::Fd)
.map_err(|error| BoxliteError::Network(format!("clone local tunnel fd: {error}"))),
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ endpoint() duplicates a live fd, not inert data
For a local box, BoxEndpoint::Fd wraps the actual connected tunnel socket; endpoint() dup(2)s it via try_clone while leaving the original fd in self.endpoint. A caller that calls endpoint() (e.g. to hand the fd to another language's socket layer, the documented into_fd FFI use case) and later also calls open_stream() gets two independent fds sharing one open file description — reads/writes on either steal bytes meant for the other, silently corrupting the tunnel. The Url variant has no such hazard since a String clone is inert; only Fd aliases a stateful, single-consumer resource. Prior code avoided this because endpoint() and connect() drained the same shared stream slot for local boxes rather than duplicating a live socket.

Comment thread src/boxlite/src/rest/litebox.rs Outdated
Comment on lines +387 to +405
#[async_trait]
impl BoxNetworkBackend for RestBox {
async fn tunnel(&self, target: SocketAddr) -> BoxliteResult<BoxTunnel> {
if target.ip().to_string() != crate::net::constants::GUEST_IP {
return Err(BoxliteError::Unsupported(
"REST box tunnels only support service ports on the guest IP".into(),
));
}

let port = target.port();
let box_id = self.box_id_str();
let endpoint = self.client.describe_box_tunnel(&box_id, port).await?;
let stream = self
.client
.connect_box_network_tunnel(&box_id, port)
.await?;
Ok(BoxTunnel::new(
Some(BoxEndpoint::Url(endpoint)),
Some(crate::net::BoxInternalTunnel::from_local(stream, target)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ tunnel() URL now depends on a successful live CONNECT
RestBox::tunnel unconditionally chains describe_box_tunnel (POST) then connect_box_network_tunnel (CONNECT). If the CONNECT leg fails (upstream box unreachable, 502 UpstreamUnavailableError) after the POST succeeded, tunnel() returns Err and the caller can no longer obtain just the public URL — a regression from the previous lazy design where fetching the endpoint and opening the stream were independent operations that could fail separately.

@G4614 G4614 Jul 17, 2026

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.

Superseded by the lazy tunnel refactor in 24b72ad. tunnel() now prepares only a reusable endpoint: remote boxes retain the public URL and defer HTTP CONNECT until open_stream(), while local boxes expose a stable Unix socket whose listener opens a fresh gvproxy tunnel for each client connection. This restores independent endpoint discovery and connection failure while keeping local and remote SDK lifecycle semantics aligned.

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

🤖 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 `@openapi/box.openapi.yaml`:
- Around line 873-874: Align the OpenAPI upstream failure responses, including
the additional occurrence, with the status and error type handled by the Rust
mapping in error.rs. Update the UpstreamUnavailableError contract and its
example so they use the same HTTP status and typed error classification
recognized by the Rust error mapping, preserving consistency in both layers.

In `@src/boxlite/src/rest/client.rs`:
- Around line 391-395: Update the rejected CONNECT branch in the REST client to
read the response body with the existing bounded-body mechanism and pass the
status and body through the shared error mapper instead of always constructing
BoxliteError::Network. Preserve the mapper’s established handling for HTTP
statuses and structured OpenAPI error responses, while retaining the current
success path for StatusCode::OK.
🪄 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: 9adf0df0-ee79-491e-b6f8-5c80f563255e

📥 Commits

Reviewing files that changed from the base of the PR and between 2411669 and 771cb67.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • openapi/box.openapi.yaml
  • src/boxlite/Cargo.toml
  • src/boxlite/src/litebox/box_impl.rs
  • src/boxlite/src/litebox/mod.rs
  • src/boxlite/src/litebox/network.rs
  • src/boxlite/src/rest/client.rs
  • src/boxlite/src/rest/litebox.rs
  • src/boxlite/src/rest/runtime.rs
  • src/boxlite/src/runtime/backend.rs

Comment thread openapi/box.openapi.yaml Outdated
Comment thread src/boxlite/src/rest/client.rs Outdated

@boxlite-agent boxlite-agent 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.

📦 BoxLite review — 2 issues

Comment thread src/boxlite/Cargo.toml Outdated
notify = "6.1"
proptest = "1.4"
toml = "0.8"
rustls = { version = "0.23", default-features = false, features = ["ring"] }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ dev-dep rustls{ring} conflicts with default aws-lc-rs
unified test build now compiles two rustls crypto providers (ring from this dev-dep, aws-lc-rs default via hyper-rustls); only the new connect_box_network_tunnel test installs a default provider, so any other TLS-using test that runs first in the same process (e.g. oci-client/reqwest tests) can hit rustls's 'no default CryptoProvider' error depending on test execution order.

Comment thread src/boxlite/src/litebox/network.rs Outdated
Comment on lines +54 to +78
pub async fn endpoint(&self) -> BoxliteResult<BoxEndpoint> {
self.endpoint
.lock()
.await
.as_ref()
.ok_or_else(|| {
BoxliteError::InvalidState("tunnel endpoint has already been consumed".into())
})?
.try_clone()
}

/// Consume the transport established by [`NetworkHandle::tunnel`].
pub async fn open_stream(&self) -> BoxliteResult<Box<dyn BoxConnection>> {
let fd = {
let mut endpoint = self.endpoint.lock().await;
match endpoint.as_ref() {
Some(BoxEndpoint::Fd(_)) => match endpoint.take() {
Some(BoxEndpoint::Fd(fd)) => Some(fd),
_ => unreachable!("endpoint variant changed while locked"),
},
_ => None,
}
};
if let Some(fd) = fd {
let stream = std::os::unix::net::UnixStream::from(fd);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 endpoint() dup's fd without consuming it
for a local (Fd) tunnel, calling endpoint() then open_stream() hands out two independent live fds to the same connected socket (dup via try_clone plus the original consumed by open_stream), unlike the Url path where stream/endpoint are cleanly separated; no in-repo caller does this today so unverified as reachable, but the API doesn't prevent it.

@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 `@src/boxlite/src/rest/client.rs`:
- Around line 329-339: Update the CONNECT authority construction near the host
extraction to bracket IPv6 literals before appending an explicit port, producing
[host]:port while preserving existing formatting for IPv4 and hostnames. Apply
this through the authority used to build the hyper::Uri.
🪄 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: 706d3c05-3269-4973-97dd-d181671ca9c1

📥 Commits

Reviewing files that changed from the base of the PR and between 2411669 and 0944d1c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • openapi/box.openapi.yaml
  • src/boxlite/Cargo.toml
  • src/boxlite/src/litebox/box_impl.rs
  • src/boxlite/src/litebox/mod.rs
  • src/boxlite/src/litebox/network.rs
  • src/boxlite/src/rest/client.rs
  • src/boxlite/src/rest/litebox.rs
  • src/boxlite/src/rest/runtime.rs
  • src/boxlite/src/runtime/backend.rs

Comment thread src/boxlite/src/rest/client.rs Outdated
@G4614
G4614 force-pushed the codex/rust-network-rest-standalone branch 2 times, most recently from 14531ef to 4e16d72 Compare July 17, 2026 03:36

@boxlite-agent boxlite-agent 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.

📦 BoxLite review — 1 issue

Comment thread src/boxlite/src/rest/client.rs Outdated
Comment on lines +359 to +365
use hyper_util::rt::TokioIo;
use tower::Service;

let mut connector = HttpsConnectorBuilder::new()
.with_native_roots()
.map_err(|e| BoxliteError::Config(format!("TLS roots unavailable: {e}")))?
.https_or_http()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ TLS connector rebuilt with blocking I/O per call
connect_box_network_tunnel() constructs a fresh HttpsConnectorBuilder and loads native root certs (blocking filesystem reads via rustls_native_certs) inline on every tunnel() call instead of caching it once on ApiClient, adding blocking I/O on the async task and setup latency each time a box tunnel is opened; not perf-tested due to missing toolchain.

@G4614
G4614 force-pushed the codex/rust-network-rest-standalone branch from 4e16d72 to 955de31 Compare July 17, 2026 03:40
@G4614
G4614 force-pushed the codex/rust-network-rest-standalone branch from 955de31 to 517cb3e Compare July 17, 2026 03:44

@boxlite-agent boxlite-agent 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.

📦 BoxLite review — 1 issues

Comment thread src/boxlite/src/rest/client.rs Outdated
Comment on lines +364 to +365
let mut connector = HttpsConnectorBuilder::new()
.with_native_roots()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 CONNECT tunnel panics: no default CryptoProvider installed
HttpsConnectorBuilder::with_native_roots() requires a process-wide rustls CryptoProvider; only the new unit test installs one (client.rs:690), so first real connect_box_network_tunnel call panics with 'no process-level CryptoProvider available'.

Suggested change
let mut connector = HttpsConnectorBuilder::new()
.with_native_roots()
let mut connector = HttpsConnectorBuilder::new()
.with_provider_and_native_roots(rustls::crypto::aws_lc_rs::default_provider().into())
.map_err(|e| BoxliteError::Config(format!("TLS roots unavailable: {e}")))?

@G4614
G4614 force-pushed the codex/rust-network-rest-standalone branch from 6776d87 to 24b72ad Compare July 17, 2026 03:57
@G4614
G4614 force-pushed the codex/rust-network-rest-standalone branch 2 times, most recently from a62f5cc to d469ccc Compare July 17, 2026 04:32
@G4614
G4614 force-pushed the codex/rust-network-rest-standalone branch from d469ccc to 62ceea7 Compare July 17, 2026 04:33

@boxlite-agent boxlite-agent 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.

📦 BoxLite review — 2 issues

Comment thread src/boxlite/Cargo.toml Outdated

@boxlite-agent boxlite-agent 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.

📦 BoxLite review — 1 issue

Comment thread src/boxlite/Cargo.toml Outdated

@boxlite-agent boxlite-agent 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.

📦 BoxLite review — 1 issue

Comment thread src/boxlite/src/litebox/box_impl.rs
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