Skip to content

feat(sdk): expose box network tunnels#994

Merged
DorianZheng merged 2 commits into
boxlite-ai:mainfrom
G4614:codex/c-go-box-tunnel
Jul 22, 2026
Merged

feat(sdk): expose box network tunnels#994
DorianZheng merged 2 commits into
boxlite-ai:mainfrom
G4614:codex/c-go-box-tunnel

Conversation

@G4614

@G4614 G4614 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Expose one-shot box network tunnels through the C and Go SDKs while preserving direct local descriptors.

Test plan:

  • cargo check -p boxlite-c on Linux debug host
  • cargo test -p boxlite-c --lib on Linux debug host (59 passed, 1 ignored)
  • go test -tags boxlite_dev ./... (blocked by existing unresolved auto-pause/resume/delete Cgo symbols on latest main)

@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

Adds box-scoped network tunnel APIs to the C, Go, Python, and Node SDKs. Tunnels expose URI or file-descriptor endpoints, support one-shot socket connections, and are validated through unit, integration, and HTTP/WebSocket/binary end-to-end tests.

Changes

Network tunnel SDK stack

Layer / File(s) Summary
C tunnel ABI and implementation
sdks/c/Cargo.toml, sdks/c/include/boxlite.h, sdks/c/src/*
Adds network and tunnel handles, endpoint inspection, FD connection bridging, lifecycle APIs, Tokio networking features, and pointer-safety tests.
Go tunnel wrappers
sdks/go/constants.go, sdks/go/tunnel.go
Adds guest addressing plus Unix cgo wrappers for network creation, endpoint retrieval, tunnel connection, context checks, and cleanup.
Python async and sync APIs
sdks/python/boxlite/*, sdks/python/src/*
Adds native network bindings, SimpleBox and sync-API tunnel wrappers, endpoint and socket conversion, startup guards, port validation, and exports.
Node bindings and SimpleBox integration
sdks/node/Cargo.toml, sdks/node/lib/*, sdks/node/src/*, sdks/node/tests/tunnel.test.ts
Adds N-API network contracts, one-shot tunnel consumption, FD-backed sockets, SimpleBox.network and SimpleBox.tunnel, public exports, and unit tests.
Tunnel E2E validation
scripts/test/e2e/*, sdks/python/tests/*
Adds in-box HTTP/WebSocket and binary fixtures plus Python and Node tests for endpoint forms, readiness, streaming, half-close behavior, restart, cancellation, isolation, and cleanup.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SDKClient
  participant NetworkHandle
  participant BoxTunnel
  participant GuestService
  SDKClient->>NetworkHandle: tunnel(port)
  NetworkHandle->>BoxTunnel: create one-shot tunnel
  SDKClient->>BoxTunnel: endpoint() or connect()
  BoxTunnel->>GuestService: bridge socket to guest port
  GuestService-->>SDKClient: tunneled response
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description is too sparse and does not follow the required Summary/Changes/How to verify template. Rewrite it using the template headings, adding a 1-2 sentence Summary, a Changes bullet list, and a clear verification command.
✅ Passed checks (3 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title is concise and accurately summarizes the main change: exposing box network tunnels.
✨ 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 marked this pull request as ready for review July 17, 2026 05:26
@G4614
G4614 requested a review from a team as a code owner July 17, 2026 05:26
@boxlite-agent

boxlite-agent Bot commented Jul 17, 2026

Copy link
Copy Markdown

📦 BoxLite review — couldn't complete

claude exited 1

stdout:
{"type":"result","subtype":"success","is_error":true,"api_error_status":403,"duration_ms":603,"duration_api_ms":0,"num_turns":1,"result":"Your organization has disabled Claude subscription access for Claude Code · Use an Anthropic API key instead, or ask your admin to enable access","stop_reason":"stop_sequence","session_id":"e3d67c7e-8751-463e-b9d4-8a290edf0e3e","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","uuid":"4c73cf09-85e5-4d9e-ab9b-6af6f700b659"}

stderr:
<empty>

powered by BoxLite

@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 sdks/go/tunnel.go
Comment thread sdks/go/constants.go Outdated
@G4614
G4614 force-pushed the codex/c-go-box-tunnel branch from 0eb7595 to a66ff19 Compare July 17, 2026 05:48
@G4614
G4614 changed the base branch from stack/box-endpoint-url-fd to main July 17, 2026 05:50
@G4614
G4614 force-pushed the codex/c-go-box-tunnel branch from a66ff19 to 690c471 Compare July 21, 2026 13:51

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

🧹 Nitpick comments (4)
scripts/test/e2e/sdks/node/e2e_tunnel.ts (1)

14-45: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider bounding endpoint()/connect() with a timeout.

Only the HTTP response has an explicit 5s timer; if tunnel.endpoint() or tunnel.connect() hangs, waitForHttp's 30s deadline check is never reached and the test relies on the outer subprocess timeout=180 in test_node_tunnel.py to fail — with a less actionable error.

♻️ Suggested guard
 async function getOverTunnel(box: SimpleBox): Promise<string> {
   const tunnel = await box.network.tunnel(GUEST_PORT)
-  const endpoint = await tunnel.endpoint()
+  const endpoint = await Promise.race([
+    tunnel.endpoint(),
+    delay(5_000).then(() => { throw new Error('endpoint() timed out') }),
+  ])
   if (typeof endpoint !== 'string') {
     throw new Error('expected REST tunnel endpoint URL for the cloud box')
   }
-  const socket = await tunnel.connect()
+  const socket = await Promise.race([
+    tunnel.connect(),
+    delay(5_000).then(() => { throw new Error('connect() timed out') }),
+  ])
🤖 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 `@scripts/test/e2e/sdks/node/e2e_tunnel.ts` around lines 14 - 45, Add an
explicit timeout around the awaited tunnel.endpoint() and tunnel.connect() calls
in getOverTunnel, using the same 5-second request timeout or an appropriate
shared timeout, and reject with a clear timeout error. Ensure these operations
cannot hang indefinitely before the existing HTTP response timer and preserve
the current successful tunnel flow.
sdks/c/src/tests.rs (1)

190-200: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add null-pointer validation tests for the tunnel functions too.

Only boxlite_box_network gets a null-pointer test here; boxlite_network_tunnel, boxlite_tunnel_endpoint, and boxlite_tunnel_connect each have equivalent null-guard branches but no matching coverage.

🤖 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 `@sdks/c/src/tests.rs` around lines 190 - 200, Add equivalent null-pointer
validation tests alongside test_box_network_null_pointer_validation for
boxlite_network_tunnel, boxlite_tunnel_endpoint, and boxlite_tunnel_connect. For
each function, pass null pointers as applicable, assert
BoxliteErrorCode::InvalidArgument, verify the FFIError message is non-null, and
free the error with boxlite_error_free.
sdks/node/src/network.rs (1)

45-47: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Panics via .expect() instead of returning a recoverable error, unlike the equivalent C ABI path.

sdks/c/src/network.rs's boxlite_network_tunnel treats a GUEST_IP parse failure as a graceful BoxliteError::Internal return; this napi binding instead panics via .expect(...). In practice GUEST_IP is a fixed constant so this can't realistically fail, but for consistency with the C layer's defensive handling of the same computation, consider returning Err(...) instead of panicking.

🤖 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 `@sdks/node/src/network.rs` around lines 45 - 47, The network tunnel path
currently panics when parsing the guest socket address; make it return a
recoverable error instead. Update the target address construction in the
relevant napi network function to propagate the parse failure as the binding’s
established internal error type, matching the graceful handling in
boxlite_network_tunnel while preserving successful parsing behavior.
sdks/c/src/network.rs (1)

53-252: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Newly-added public network/tunnel functions lack docstrings. All of the new pub unsafe extern "C" fn in the C ABI, and the new napi methods tunnel()/endpoint() in the Node binding, are missing doc comments, while sibling structs/enums and the existing connect_fd() do have them.

  • sdks/c/src/network.rs#L53-L252: add /// doc comments to boxlite_box_network, boxlite_network_free, boxlite_network_tunnel, boxlite_tunnel_free, boxlite_tunnel_endpoint, and boxlite_tunnel_connect.
  • sdks/node/src/network.rs#L40-L68: add /// doc comments to JsNetworkHandle::tunnel and JsBoxTunnel::endpoint.

As per coding guidelines, sdks/**/*.{js,ts,jsx,tsx,py,java,go,rs,rb,php,cs,cpp,c,h,swift,kt}: "Write comprehensive docstrings for all public functions and classes".

🤖 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 `@sdks/c/src/network.rs` around lines 53 - 252, Add comprehensive Rust doc
comments to the public C ABI functions boxlite_box_network,
boxlite_network_free, boxlite_network_tunnel, boxlite_tunnel_free,
boxlite_tunnel_endpoint, and boxlite_tunnel_connect in sdks/c/src/network.rs
(lines 53-252), and to JsNetworkHandle::tunnel and JsBoxTunnel::endpoint in
sdks/node/src/network.rs (lines 40-68), documenting their purpose, parameters,
outputs, and relevant safety/ownership behavior.

Source: Coding guidelines

🤖 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 `@sdks/c/include/boxlite.h`:
- Around line 546-568: Add comprehensive API documentation comments for
boxlite_box_network, boxlite_network_free, boxlite_network_tunnel,
boxlite_tunnel_free, boxlite_tunnel_endpoint, and boxlite_tunnel_connect.
Document each function’s purpose, parameters, return status, output values,
error handling, and ownership/lifetime rules for returned network, tunnel, URI,
and file-descriptor resources.

In `@sdks/c/src/network.rs`:
- Around line 20-31: The UnixStream bridging logic in connection_fd is
duplicated across SDK crates; extract the pair/copy_bidirectional/FD-export flow
into a shared helper in the core boxlite crate, then update
sdks/c/src/network.rs lines 20-31 and sdks/node/src/network.rs lines 12-24 to
call it instead of maintaining local implementations. Preserve the existing
error propagation and returned OwnedFd behavior at both sites.
- Around line 152-213: Initialize *out_type to a defined sentinel immediately
after validating the output pointers and alongside the existing *out_uri and
*out_fd initialization in boxlite_tunnel_endpoint. Preserve the
endpoint-specific assignments on success while ensuring the error path leaves
all out-parameters deterministically initialized.

In `@sdks/go/tunnel.go`:
- Around line 67-85: Update Network.Tunnel and Tunnel.Connect so context
cancellation is honored while the native cgo calls are in flight, not only
before invocation. Avoid leaving the caller blocked on synchronous bridge calls
when the guest network hangs; use the existing native/API mechanisms to return
promptly on ctx cancellation while preserving current validation and error
propagation.

In `@sdks/python/tests/test_tunnel.py`:
- Line 89: Remove the module-wide pytestmark integration marker from
test_tunnel.py, then apply the integration marker only to tests that require a
real box or external service. Keep pure unit tests such as
test_endpoint_returns_local_file_descriptor, test_connect_consumes_tunnel_once,
test_tunnel_requires_a_started_box, and test_sync_tunnel_rejects_invalid_ports
unmarked so they remain in the non-integration test lane.

---

Nitpick comments:
In `@scripts/test/e2e/sdks/node/e2e_tunnel.ts`:
- Around line 14-45: Add an explicit timeout around the awaited
tunnel.endpoint() and tunnel.connect() calls in getOverTunnel, using the same
5-second request timeout or an appropriate shared timeout, and reject with a
clear timeout error. Ensure these operations cannot hang indefinitely before the
existing HTTP response timer and preserve the current successful tunnel flow.

In `@sdks/c/src/network.rs`:
- Around line 53-252: Add comprehensive Rust doc comments to the public C ABI
functions boxlite_box_network, boxlite_network_free, boxlite_network_tunnel,
boxlite_tunnel_free, boxlite_tunnel_endpoint, and boxlite_tunnel_connect in
sdks/c/src/network.rs (lines 53-252), and to JsNetworkHandle::tunnel and
JsBoxTunnel::endpoint in sdks/node/src/network.rs (lines 40-68), documenting
their purpose, parameters, outputs, and relevant safety/ownership behavior.

In `@sdks/c/src/tests.rs`:
- Around line 190-200: Add equivalent null-pointer validation tests alongside
test_box_network_null_pointer_validation for boxlite_network_tunnel,
boxlite_tunnel_endpoint, and boxlite_tunnel_connect. For each function, pass
null pointers as applicable, assert BoxliteErrorCode::InvalidArgument, verify
the FFIError message is non-null, and free the error with boxlite_error_free.

In `@sdks/node/src/network.rs`:
- Around line 45-47: The network tunnel path currently panics when parsing the
guest socket address; make it return a recoverable error instead. Update the
target address construction in the relevant napi network function to propagate
the parse failure as the binding’s established internal error type, matching the
graceful handling in boxlite_network_tunnel while preserving successful parsing
behavior.
🪄 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: 964a9057-fd30-4380-b8ed-fbc73632747a

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa46fb and 690c471.

📒 Files selected for processing (31)
  • scripts/test/e2e/cases/test_node_tunnel.py
  • scripts/test/e2e/cases/test_sdk_tunnel.py
  • scripts/test/e2e/sdks/node/e2e_tunnel.ts
  • sdks/c/Cargo.toml
  • sdks/c/include/boxlite.h
  • sdks/c/src/lib.rs
  • sdks/c/src/network.rs
  • sdks/c/src/tests.rs
  • sdks/go/constants.go
  • sdks/go/tunnel.go
  • sdks/node/Cargo.toml
  • sdks/node/lib/index.ts
  • sdks/node/lib/native-contracts.ts
  • sdks/node/lib/simplebox.ts
  • sdks/node/src/box_handle.rs
  • sdks/node/src/lib.rs
  • sdks/node/src/network.rs
  • sdks/node/tests/tunnel.test.ts
  • sdks/python/Cargo.toml
  • sdks/python/boxlite/__init__.py
  • sdks/python/boxlite/simplebox.py
  • sdks/python/boxlite/sync_api/__init__.py
  • sdks/python/boxlite/sync_api/_box.py
  • sdks/python/boxlite/sync_api/_network.py
  • sdks/python/boxlite/sync_api/_simplebox.py
  • sdks/python/pytest.ini
  • sdks/python/src/box_handle.rs
  • sdks/python/src/lib.rs
  • sdks/python/src/network.rs
  • sdks/python/tests/test_dev_tunnel_e2e.py
  • sdks/python/tests/test_tunnel.py

Comment thread sdks/c/include/boxlite.h
Comment thread sdks/c/src/network.rs
Comment thread sdks/c/src/network.rs
Comment thread sdks/go/tunnel.go Outdated
Comment thread sdks/python/tests/test_tunnel.py Outdated
@G4614 G4614 changed the title feat(sdk): expose box tunnels through C and Go feat(sdk): expose box network tunnels Jul 21, 2026
@G4614
G4614 force-pushed the codex/c-go-box-tunnel branch from 405d870 to 1fbde98 Compare July 22, 2026 04:19

@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

🧹 Nitpick comments (4)
sdks/python/src/box_handle.rs (1)

44-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the public network getter contract.

Expand the docstring to state that network.tunnel(port) is asynchronous and lazy, rejects port 0, and exposes URI/FD endpoints with one-shot connections.

🤖 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 `@sdks/python/src/box_handle.rs` around lines 44 - 50, Expand the docstring for
the public network getter method network in PyBoxHandle to document that
network.tunnel(port) is asynchronous and lazy, rejects port 0, and exposes URI
and file-descriptor endpoints using one-shot connections.

Source: Coding guidelines

sdks/node/lib/native-contracts.ts (1)

257-266: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Complete the exported tunnel API documentation.

Document the non-zero port requirement, the endpoint union (string URI versus numeric FD), and that _connectFd() consumes the one-shot tunnel connection.

🤖 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 `@sdks/node/lib/native-contracts.ts` around lines 257 - 266, Update the
exported JsNetworkHandle.tunnel and JsBoxTunnel endpoint and _connectFd
documentation to state that ports must be non-zero, endpoint() returns either a
string URI or numeric file descriptor, and _connectFd() consumes the tunnel’s
one-shot connection. Keep the existing API signatures unchanged.

Source: Coding guidelines

sdks/python/tests/test_dev_tunnel_e2e.py (2)

294-368: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add docstrings to test functions and helpers.

None of the test_* functions or module-level helpers carry a docstring describing the scenario under test (e.g. one-shot tunnel reconnection, binary integrity with concurrent streams).

As per coding guidelines, "Write comprehensive docstrings for all public functions and classes" for sdks/**/*.py.

🤖 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 `@sdks/python/tests/test_dev_tunnel_e2e.py` around lines 294 - 368, The test
functions and module-level helpers in test_dev_tunnel_e2e.py lack scenario
documentation. Add concise docstrings to every test_* function and helper,
including the tunnel endpoint/one-shot reconnection and binary-integrity
concurrent-stream scenarios, describing the behavior each validates.

Source: Coding guidelines


82-156: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate duplicated background-server wrapper logic.

_serve_marker_http and _serve_binary_echo each define a near-identical LocalServer class and PID-capture/backgrounding shell command for the SimpleBox branch, differing only by the embedded code and log path. Extracting a shared _run_background_process(box, code, log_path) helper would remove the duplication.

As per coding guidelines, "Keep functions small and focused on a single responsibility" for sdks/python/**.

♻️ Suggested consolidation
async def _run_background_process(box, code: str, log_path: str):
    if isinstance(box, boxlite.SimpleBox):
        command = f"python3 -u -c {shlex.quote(code)} >{log_path} 2>&1 & echo $!"
        result = await box.exec("sh", "-c", command)
        pid = result.stdout.strip()

        class LocalServer:
            async def kill(self):
                await box.exec("kill", pid)

        return LocalServer()
    return await box.exec("python3", ["-u", "-c", code])
🤖 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 `@sdks/python/tests/test_dev_tunnel_e2e.py` around lines 82 - 156, Extract the
duplicated SimpleBox background-process and PID cleanup logic from
_serve_marker_http and _serve_binary_echo into a shared
_run_background_process(box, code, log_path) helper. Have both server functions
delegate to it with their respective code and log paths, while preserving the
existing non-SimpleBox execution and kill behavior.

Source: Coding guidelines

🤖 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 `@sdks/c/src/tests.rs`:
- Around line 194-196: Extend the test around boxlite_box_network to exercise
both ABI null-pointer guards independently: retain the null-handle case with a
valid output pointer, then add a separate call using a valid handle and null
out_network, asserting the expected InvalidArgument result and non-null error
message for each case.

---

Nitpick comments:
In `@sdks/node/lib/native-contracts.ts`:
- Around line 257-266: Update the exported JsNetworkHandle.tunnel and
JsBoxTunnel endpoint and _connectFd documentation to state that ports must be
non-zero, endpoint() returns either a string URI or numeric file descriptor, and
_connectFd() consumes the tunnel’s one-shot connection. Keep the existing API
signatures unchanged.

In `@sdks/python/src/box_handle.rs`:
- Around line 44-50: Expand the docstring for the public network getter method
network in PyBoxHandle to document that network.tunnel(port) is asynchronous and
lazy, rejects port 0, and exposes URI and file-descriptor endpoints using
one-shot connections.

In `@sdks/python/tests/test_dev_tunnel_e2e.py`:
- Around line 294-368: The test functions and module-level helpers in
test_dev_tunnel_e2e.py lack scenario documentation. Add concise docstrings to
every test_* function and helper, including the tunnel endpoint/one-shot
reconnection and binary-integrity concurrent-stream scenarios, describing the
behavior each validates.
- Around line 82-156: Extract the duplicated SimpleBox background-process and
PID cleanup logic from _serve_marker_http and _serve_binary_echo into a shared
_run_background_process(box, code, log_path) helper. Have both server functions
delegate to it with their respective code and log paths, while preserving the
existing non-SimpleBox execution and kill behavior.
🪄 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: a0e10bb7-937a-49c4-99e7-e047b390ed52

📥 Commits

Reviewing files that changed from the base of the PR and between 405d870 and 1fbde98.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (48)
  • openapi/box.openapi.yaml
  • scripts/build/build-runtime.sh
  • scripts/test/e2e/cases/test_node_tunnel.py
  • scripts/test/e2e/cases/test_sdk_tunnel.py
  • scripts/test/e2e/sdks/node/e2e_tunnel.ts
  • sdks/c/Cargo.toml
  • sdks/c/include/boxlite.h
  • sdks/c/src/lib.rs
  • sdks/c/src/network.rs
  • sdks/c/src/tests.rs
  • sdks/go/constants.go
  • sdks/go/tunnel.go
  • sdks/node/Cargo.toml
  • sdks/node/lib/index.ts
  • sdks/node/lib/native-contracts.ts
  • sdks/node/lib/simplebox.ts
  • sdks/node/src/box_handle.rs
  • sdks/node/src/lib.rs
  • sdks/node/src/network.rs
  • sdks/node/tests/tunnel.test.ts
  • sdks/python/Cargo.toml
  • sdks/python/boxlite/__init__.py
  • sdks/python/boxlite/simplebox.py
  • sdks/python/boxlite/sync_api/__init__.py
  • sdks/python/boxlite/sync_api/_box.py
  • sdks/python/boxlite/sync_api/_network.py
  • sdks/python/boxlite/sync_api/_simplebox.py
  • sdks/python/pytest.ini
  • sdks/python/src/box_handle.rs
  • sdks/python/src/lib.rs
  • sdks/python/src/network.rs
  • sdks/python/tests/test_dev_tunnel_e2e.py
  • sdks/python/tests/test_tunnel.py
  • 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/net/gvproxy/services.rs
  • src/boxlite/src/net/mod.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
  • src/boxlite/tests/gvproxy_backend.rs
  • src/cli/src/cli.rs
  • src/cli/src/commands/mod.rs
  • src/cli/src/commands/tunnel.rs
  • src/cli/src/main.rs
💤 Files with no reviewable changes (2)
  • src/boxlite/src/net/gvproxy/services.rs
  • src/boxlite/tests/gvproxy_backend.rs
🚧 Files skipped from review as they are similar to previous changes (26)
  • sdks/go/constants.go
  • sdks/python/boxlite/sync_api/init.py
  • sdks/node/Cargo.toml
  • sdks/python/pytest.ini
  • sdks/node/src/lib.rs
  • sdks/python/Cargo.toml
  • scripts/build/build-runtime.sh
  • sdks/python/boxlite/sync_api/_network.py
  • sdks/python/boxlite/init.py
  • sdks/python/src/lib.rs
  • sdks/node/tests/tunnel.test.ts
  • sdks/c/src/lib.rs
  • sdks/node/src/box_handle.rs
  • sdks/python/boxlite/sync_api/_simplebox.py
  • scripts/test/e2e/cases/test_sdk_tunnel.py
  • sdks/python/tests/test_tunnel.py
  • sdks/node/src/network.rs
  • sdks/python/boxlite/sync_api/_box.py
  • sdks/go/tunnel.go
  • sdks/python/src/network.rs
  • sdks/node/lib/index.ts
  • sdks/c/include/boxlite.h
  • sdks/python/boxlite/simplebox.py
  • scripts/test/e2e/sdks/node/e2e_tunnel.ts
  • sdks/c/src/network.rs
  • sdks/node/lib/simplebox.ts

Comment thread sdks/c/src/tests.rs Outdated

@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 `@scripts/test/e2e/cases/test_sdk_tunnel.py`:
- Around line 105-109: Update the WebSocket reads in the test flow around
frame_header and echoed to accumulate data across repeated loop.sock_recv calls
until the exact expected byte counts are reached. Ensure the header always
contains 2 bytes before reading its length byte, then collect the full echoed
frame length before comparing it with marker + b":" + payload.
🪄 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: ebe7a50a-6097-45a7-a38e-baf64acc73dc

📥 Commits

Reviewing files that changed from the base of the PR and between 1fbde98 and 58dc0e6.

📒 Files selected for processing (9)
  • scripts/test/e2e/cases/test_sdk_tunnel.py
  • scripts/test/e2e/fixtures/service_in_box_server.py
  • scripts/test/e2e/sdks/node/e2e_tunnel.ts
  • sdks/node/lib/native-contracts.ts
  • sdks/node/lib/simplebox.ts
  • sdks/python/boxlite/simplebox.py
  • sdks/python/boxlite/sync_api/_box.py
  • sdks/python/boxlite/sync_api/_network.py
  • sdks/python/boxlite/sync_api/_simplebox.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • sdks/python/boxlite/sync_api/_network.py
  • sdks/python/boxlite/sync_api/_simplebox.py
  • sdks/node/lib/native-contracts.ts
  • sdks/node/lib/simplebox.ts
  • sdks/python/boxlite/sync_api/_box.py
  • sdks/python/boxlite/simplebox.py

Comment thread scripts/test/e2e/cases/test_sdk_tunnel.py Outdated
@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@G4614
G4614 force-pushed the codex/c-go-box-tunnel branch from 58dc0e6 to 50b53d0 Compare July 22, 2026 08:22

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

🧹 Nitpick comments (4)
sdks/node/src/network.rs (1)

40-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the public N-API methods.

tunnel and endpoint are public SDK APIs but have no Rust docstrings. Document their one-shot semantics, endpoint variants, and error conditions.

Proposed documentation
+    /// Create a one-shot tunnel to `port` on the guest.
+    ///
+    /// Returns an error when `port` is zero or the tunnel cannot be created.
     #[napi]
     pub async fn tunnel(&self, port: u16) -> Result<JsBoxTunnel> {
...
+    /// Return this tunnel's URI or Unix file-descriptor endpoint.
+    ///
+    /// Returns an error after the tunnel has been consumed by `_connectFd`.
     #[napi]
     pub async fn endpoint(&self) -> Result<Either<String, i32>> {

As per coding guidelines: “Write comprehensive docstrings for all public functions and classes.”

🤖 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 `@sdks/node/src/network.rs` around lines 40 - 63, Document the public N-API
methods `JsBoxNetwork::tunnel` and `JsBoxTunnel::endpoint` with Rust doc
comments. Describe that `tunnel` creates a one-shot tunnel, rejects port 0, and
can return tunnel-creation errors; describe that `endpoint` returns either a
string endpoint or integer port and document its relevant errors and one-shot
behavior.

Source: Coding guidelines

sdks/python/tests/test_dev_tunnel_e2e.py (3)

55-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate retry/readiness-polling loop.

_wait_for_http_server and _wait_for_binary_server implement the same deadline/retry/fresh-tunnel pattern with only the readiness check differing. Consider extracting a shared "poll until ready" helper parameterized by an async predicate.

As per coding guidelines, "Keep functions small and focused on a single responsibility" and "Avoid deeply nested code structures; refactor into separate functions if needed."

♻️ Sketch of a shared polling helper
async def _poll_until_ready(box, port, check, *, timeout=20, interval=0.5):
    deadline = asyncio.get_running_loop().time() + timeout
    last_result = "no connection attempt completed"
    while True:
        tunnel = await box.network.tunnel(port)
        try:
            result = await check(tunnel)
            if result is not None:
                return result
        except (ConnectionError, OSError, RuntimeError, TimeoutError) as error:
            last_result = f"{type(error).__name__}: {error}"
        if asyncio.get_running_loop().time() >= deadline:
            raise AssertionError(f"server did not become ready; last result: {last_result}")
        await asyncio.sleep(interval)

Also applies to: 210-241

🤖 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 `@sdks/python/tests/test_dev_tunnel_e2e.py` around lines 55 - 73, Extract the
duplicated deadline, retry, error-tracking, sleep, and tunnel-refresh logic from
_wait_for_http_server and _wait_for_binary_server into a shared async polling
helper, parameterized by an async readiness check and preserving the existing
timeout, interval, exception handling, and result semantics. Update both
server-specific functions to supply only their protocol-specific checks and use
the helper’s returned ready result.

Source: Coding guidelines


322-346: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate cloud runtime/box bootstrap.

test_dev_cloud_tunnel_endpoint_and_one_shot_connects and test_dev_cloud_tunnel_binary_integrity repeat the same env-var lookup, Boxlite.rest(...) construction, and box-fetch/assert sequence. Extract a shared _dev_cloud_box() helper.

As per coding guidelines, "Keep functions small and focused on a single responsibility."

♻️ Proposed shared helper
async def _dev_cloud_box():
    from boxlite import ApiKeyCredential, Boxlite, BoxliteRestOptions

    api_key = _required_env("BOXLITE_API_KEY")
    box_id = _required_env("BOXLITE_E2E_BOX_ID")
    rest_url = os.getenv("BOXLITE_REST_URL", "https://dev.boxlite.ai/api")
    path_prefix = os.getenv("BOXLITE_REST_PATH_PREFIX")
    runtime = Boxlite.rest(
        BoxliteRestOptions(url=rest_url, credential=ApiKeyCredential(api_key), path_prefix=path_prefix)
    )
    box = await runtime.get(box_id)
    assert box is not None, f"dev box {box_id!r} was not found"
    return box

Also applies to: 350-367

🤖 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 `@sdks/python/tests/test_dev_tunnel_e2e.py` around lines 322 - 346, Extract the
duplicated cloud runtime and box-fetch setup from
test_dev_cloud_tunnel_endpoint_and_one_shot_connects and
test_dev_cloud_tunnel_binary_integrity into a shared async _dev_cloud_box()
helper. Move the environment lookups, Boxlite.rest construction, runtime.get
call, and missing-box assertion into that helper, then have both tests await it
while retaining their existing test-specific port and tunnel assertions.

Source: Coding guidelines


82-102: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate background-process launch/kill logic.

_serve_marker_http and _serve_binary_echo repeat the identical isinstance(box, boxlite.SimpleBox) branching and inline LocalServer class, differing only in the code/log path. Extract a shared _launch_background_process(box, code, log_path) helper.

As per coding guidelines, "Keep functions focused and cohesive - each function should do one thing well."

♻️ Proposed shared helper
async def _launch_background_process(box, code: str, log_path: str):
    if isinstance(box, boxlite.SimpleBox):
        command = f"python3 -u -c {shlex.quote(code)} >{log_path} 2>&1 & echo $!"
        result = await box.exec("sh", "-c", command)
        pid = result.stdout.strip()

        class LocalServer:
            async def kill(self):
                await box.exec("kill", pid)

        return LocalServer()
    return await box.exec("python3", ["-u", "-c", code])

Then _serve_marker_http/_serve_binary_echo just build code and delegate:

-    if isinstance(box, boxlite.SimpleBox):
-        command = (
-            f"python3 -u -c {shlex.quote(code)} >/tmp/tunnel-e2e.log 2>&1 & echo $!"
-        )
-        result = await box.exec("sh", "-c", command)
-        pid = result.stdout.strip()
-
-        class LocalServer:
-            async def kill(self):
-                await box.exec("kill", pid)
-
-        return LocalServer()
-    return await box.exec("python3", ["-u", "-c", code])
+    return await _launch_background_process(box, code, "/tmp/tunnel-e2e.log")

Also applies to: 145-155

🤖 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 `@sdks/python/tests/test_dev_tunnel_e2e.py` around lines 82 - 102, Extract the
duplicated SimpleBox background-process launch and inline LocalServer kill
behavior from _serve_marker_http and _serve_binary_echo into a shared
_launch_background_process(box, code, log_path) helper. Keep each serve function
responsible only for constructing its server code and delegating with its
appropriate log path, while preserving the existing non-SimpleBox execution path
and kill behavior.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@sdks/node/src/network.rs`:
- Around line 40-63: Document the public N-API methods `JsBoxNetwork::tunnel`
and `JsBoxTunnel::endpoint` with Rust doc comments. Describe that `tunnel`
creates a one-shot tunnel, rejects port 0, and can return tunnel-creation
errors; describe that `endpoint` returns either a string endpoint or integer
port and document its relevant errors and one-shot behavior.

In `@sdks/python/tests/test_dev_tunnel_e2e.py`:
- Around line 55-73: Extract the duplicated deadline, retry, error-tracking,
sleep, and tunnel-refresh logic from _wait_for_http_server and
_wait_for_binary_server into a shared async polling helper, parameterized by an
async readiness check and preserving the existing timeout, interval, exception
handling, and result semantics. Update both server-specific functions to supply
only their protocol-specific checks and use the helper’s returned ready result.
- Around line 322-346: Extract the duplicated cloud runtime and box-fetch setup
from test_dev_cloud_tunnel_endpoint_and_one_shot_connects and
test_dev_cloud_tunnel_binary_integrity into a shared async _dev_cloud_box()
helper. Move the environment lookups, Boxlite.rest construction, runtime.get
call, and missing-box assertion into that helper, then have both tests await it
while retaining their existing test-specific port and tunnel assertions.
- Around line 82-102: Extract the duplicated SimpleBox background-process launch
and inline LocalServer kill behavior from _serve_marker_http and
_serve_binary_echo into a shared _launch_background_process(box, code, log_path)
helper. Keep each serve function responsible only for constructing its server
code and delegating with its appropriate log path, while preserving the existing
non-SimpleBox execution path and kill behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c33c8dfa-6272-4177-ab2c-3f8163377647

📥 Commits

Reviewing files that changed from the base of the PR and between 58dc0e6 and 50b53d0.

📒 Files selected for processing (32)
  • scripts/test/e2e/cases/test_node_tunnel.py
  • scripts/test/e2e/cases/test_sdk_tunnel.py
  • scripts/test/e2e/fixtures/service_in_box_server.py
  • scripts/test/e2e/sdks/node/e2e_tunnel.ts
  • sdks/c/Cargo.toml
  • sdks/c/include/boxlite.h
  • sdks/c/src/lib.rs
  • sdks/c/src/network.rs
  • sdks/c/src/tests.rs
  • sdks/go/constants.go
  • sdks/go/tunnel.go
  • sdks/node/Cargo.toml
  • sdks/node/lib/index.ts
  • sdks/node/lib/native-contracts.ts
  • sdks/node/lib/simplebox.ts
  • sdks/node/src/box_handle.rs
  • sdks/node/src/lib.rs
  • sdks/node/src/network.rs
  • sdks/node/tests/tunnel.test.ts
  • sdks/python/Cargo.toml
  • sdks/python/boxlite/__init__.py
  • sdks/python/boxlite/simplebox.py
  • sdks/python/boxlite/sync_api/__init__.py
  • sdks/python/boxlite/sync_api/_box.py
  • sdks/python/boxlite/sync_api/_network.py
  • sdks/python/boxlite/sync_api/_simplebox.py
  • sdks/python/pytest.ini
  • sdks/python/src/box_handle.rs
  • sdks/python/src/lib.rs
  • sdks/python/src/network.rs
  • sdks/python/tests/test_dev_tunnel_e2e.py
  • sdks/python/tests/test_tunnel.py
🚧 Files skipped from review as they are similar to previous changes (29)
  • sdks/python/pytest.ini
  • sdks/go/constants.go
  • sdks/python/Cargo.toml
  • scripts/test/e2e/fixtures/service_in_box_server.py
  • sdks/python/boxlite/sync_api/init.py
  • sdks/node/Cargo.toml
  • sdks/c/src/lib.rs
  • sdks/node/src/box_handle.rs
  • sdks/c/src/tests.rs
  • sdks/node/src/lib.rs
  • sdks/python/boxlite/init.py
  • sdks/node/lib/native-contracts.ts
  • sdks/c/Cargo.toml
  • sdks/python/boxlite/sync_api/_network.py
  • sdks/python/boxlite/sync_api/_simplebox.py
  • sdks/node/lib/index.ts
  • scripts/test/e2e/cases/test_sdk_tunnel.py
  • sdks/python/src/box_handle.rs
  • sdks/c/include/boxlite.h
  • sdks/node/tests/tunnel.test.ts
  • sdks/python/boxlite/simplebox.py
  • scripts/test/e2e/sdks/node/e2e_tunnel.ts
  • sdks/python/tests/test_tunnel.py
  • sdks/go/tunnel.go
  • sdks/python/src/network.rs
  • sdks/python/src/lib.rs
  • sdks/python/boxlite/sync_api/_box.py
  • sdks/node/lib/simplebox.ts
  • sdks/c/src/network.rs

@G4614
G4614 force-pushed the codex/c-go-box-tunnel branch from 89bbea3 to f6ac4f2 Compare July 22, 2026 09:35
@G4614
G4614 enabled auto-merge July 22, 2026 10:07
@DorianZheng
DorianZheng disabled auto-merge July 22, 2026 10:21
@DorianZheng
DorianZheng merged commit 43ebd34 into boxlite-ai:main Jul 22, 2026
32 checks passed
@G4614
G4614 deleted the codex/c-go-box-tunnel branch July 23, 2026 08:16
DorianZheng added a commit that referenced this pull request Jul 23, 2026
## Problem
PR #988 split the guest's fused `Container.Init` into `Init` (create) +
`Start` (run init). The guest agent is baked into the versioned
guest-rootfs and reused per-box across restarts, so a box created before
#988 boots its old agent — which has no `Container.Start`. The post-#988
host calls it, tonic returns `Unimplemented`, and it surfaced as
`gRPC/tonic error … Unimplemented … (code=16)`, breaking restart/exec of
pre-#988 boxes.

## Fix
`ContainerInterface::start` now reads the gRPC status before `?`
flattens it and treats `Unimplemented` as "already started": on a
pre-#988 agent the fused `Init` (still called every boot) already ran
init, so `Start` is a redundant no-op. Real errors still propagate.

Also removes the spurious `boxlite/gvproxy` feature from `sdks/c`
(introduced in #994), which linked the shim-side `libgvproxy-sys` CGO
library into the C SDK — the SDK/CLI must not, per the existing
`net::tests` guard. Matches Python/Node/CLI, which expose the same
tunnel API with `rest` only.

## Test
An in-process tonic stub guest returning `Unimplemented` reproduces the
exact failure through `ContainerInterface::start`; the degrade turns it
green while a genuine `Start` error still surfaces. Full boxlite +
boxlite-shared unit suites pass (852 + 47).

Deferred: the durable fix (re-inject/recreate the guest so restarts run
the current agent, kata/Daytona-style) is not included here.

https://claude.ai/code/session_01Vu4qDgCnMjnLPpNsroxeAP


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Container startup now succeeds when the service reports that the
container start operation is unsupported, treating it as already
started.
  * Genuine container startup errors continue to be reported normally.
* **Compatibility**
* Updated SDK configuration to use the REST feature without the optional
networking feature.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: tester <[email protected]>
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