Skip to content

fix(sdk): make boxlite_options_add_port carry the full PortSpec#741

Merged
DorianZheng merged 5 commits into
mainfrom
fix/c-sdk-add-port-full-portspec
Jun 11, 2026
Merged

fix(sdk): make boxlite_options_add_port carry the full PortSpec#741
DorianZheng merged 5 commits into
mainfrom
fix/c-sdk-add-port-full-portspec

Conversation

@DorianZheng

@DorianZheng DorianZheng commented Jun 11, 2026

Copy link
Copy Markdown
Member

The C function took (guest_port, host_port) as two ints — reversed relative to core PortSpec, the Python/Node/Go SDKs, the CLI -p flag, Docker's host:guest convention, and add_volume in the same header — and could not express protocol or host_ip, capping every binding built on the C ABI below what the core model carries.

C SDK:

  • New signature: (opts, host_port, guest_port, protocol, host_ip), host-first, field-for-field the core PortSpec order. Old 3-arg form removed outright; the arity change turns stale callers into compile errors instead of silently flipped forwarding.
  • Ports are uint16_t; returns BoxliteErrorCode instead of void (InvalidArgument on NULL opts, guest_port 0, or non-UTF-8 host_ip).
  • New BoxlitePortProtocol enum, following the BoxliteRegistryTransport pattern. host_ip NULL or "" = all interfaces.
  • The C layer stores protocol/host_ip verbatim (same as Node/Python); capability enforcement for values the runtime cannot honor yet stays a core concern.

Go SDK:

  • WithPortSpec merged into WithPort(spec PortSpec); the two-int form is gone (Go has no overloading), matching the WithNetwork/WithSecret struct-option style. Named fields replace positional host/guest.
  • Zero-value Protocol now normalizes to TCP in toCSpec, matching the core default; UDP and HostIP are still rejected until the runtime honors them, with errors that blame the runtime, not the bridge.
  • Bridge passes protocol and host_ip through the new FFI signature and checks the returned error code.

Also corrects the stale "None/0 => dynamically assigned" comment on core PortSpec.host_port (actual behavior mirrors guest_port) and the same false claim in the Go docs, and updates both apps/runner call sites.

Summary by CodeRabbit

  • Breaking Changes

    • C SDK port API updated: host-first parameter order, ports are 16-bit, protocol parameter added, host IP allowed, and calls return error codes.
  • New Features

    • Port forwarding accepts explicit protocol and optional host bind address.
    • Go SDK options now accept a full port spec object when configuring ports.
  • Documentation

    • Migration guide with before/after C examples; port host-port semantics clarified.
  • Tests

    • New and revised tests cover port validation and error handling.

The C function took (guest_port, host_port) as two ints — reversed
relative to core PortSpec, the Python/Node/Go SDKs, the CLI -p flag,
Docker's host:guest convention, and add_volume in the same header —
and could not express protocol or host_ip, capping every binding
built on the C ABI below what the core model carries.

C SDK:
- New signature: (opts, host_port, guest_port, protocol, host_ip),
  host-first, field-for-field the core PortSpec order. Old 3-arg form
  removed outright; the arity change turns stale callers into compile
  errors instead of silently flipped forwarding.
- Ports are uint16_t; returns BoxliteErrorCode instead of void
  (InvalidArgument on NULL opts, guest_port 0, or non-UTF-8 host_ip).
- New BoxlitePortProtocol enum, following the BoxliteRegistryTransport
  pattern. host_ip NULL or "" = all interfaces.
- The C layer stores protocol/host_ip verbatim (same as Node/Python);
  capability enforcement for values the runtime cannot honor yet
  stays a core concern.

Go SDK:
- WithPortSpec merged into WithPort(spec PortSpec); the two-int form
  is gone (Go has no overloading), matching the WithNetwork/WithSecret
  struct-option style. Named fields replace positional host/guest.
- Zero-value Protocol now normalizes to TCP in toCSpec, matching the
  core default; UDP and HostIP are still rejected until the runtime
  honors them, with errors that blame the runtime, not the bridge.
- Bridge passes protocol and host_ip through the new FFI signature
  and checks the returned error code.

Also corrects the stale "None/0 => dynamically assigned" comment on
core PortSpec.host_port (actual behavior mirrors guest_port) and the
same false claim in the Go docs, and updates both apps/runner call
sites.
@DorianZheng DorianZheng requested a review from a team as a code owner June 11, 2026 16:11
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 28c5f3b4-2ba0-4a57-9455-b6b33f2784af

📥 Commits

Reviewing files that changed from the base of the PR and between f9c9a17 and ca84678.

📒 Files selected for processing (1)
  • sdks/go/options.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdks/go/options.go

📝 Walkthrough

Walkthrough

This PR redesigns the port-forwarding API across C, Rust FFI, Go SDK, and runner clients. The C API gains a protocol enum, host IP binding support, and error reporting; Rust FFI implements the new contract with validation; Go SDK refactors WithPort to accept full PortSpec structs and handles protocol normalization; runner clients update their call sites accordingly; and PortSpec runtime semantics are clarified.

Changes

Port-Forwarding API and Integration

Layer / File(s) Summary
C API contract: PortProtocol enum and boxlite_options_add_port
sdks/c/include/boxlite.h, sdks/c/README.md
BoxlitePortProtocol enum (TCP/UDP) is introduced; boxlite_options_add_port changes from void (guest_port, host_port) to error-returning (host_port, guest_port, protocol, host_ip). Migration guide documents the breaking change with before/after examples.
Rust FFI bridge and validation
sdks/c/src/options.rs, sdks/c/src/tests.rs
BoxlitePortProtocol enum and boxlite_options_add_port FFI are implemented in Rust. options_add_port validates inputs (null handle, zero guest_port), parses and validates host_ip UTF-8, maps protocol, and conditionally omits host_port when zero. Three test cases cover full spec storage, default handling, and rejection of invalid inputs.
Go SDK port option redesign
sdks/go/options.go, sdks/go/boxlite_test.go
WithPort now accepts full PortSpec instead of (host, guest int). PortSpec.toCSpec() normalizes PortProtocolUnknown to TCP and rejects UDP/non-empty HostIP. buildCOptions() expands FFI call to pass protocol, optional host IP, and checks return codes. Tests verify protocol normalization and API shape changes.
Runner client port calls
apps/runner/pkg/boxlite/client.go, apps/runner/pkg/boxlite/stubs.go
Toolbox port-forwarding calls in Client.Create and Resize are updated from boxlite.WithPort(host, guest) to boxlite.WithPort(boxlite.PortSpec{Host: ..., Guest: ...}).
PortSpec runtime semantics documentation
src/boxlite/src/runtime/options.rs
PortSpec.host_port documentation is updated: None now means bind uses the same port as guest_port.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • boxlite-ai/boxlite#734: Both PRs update runner toolbox port-forwarding wiring to boxlite.WithPort, changing argument/PortSpec usage for the same code path.

Suggested reviewers

  • law-chain-hot

Poem

🐰 I swapped the old ports for a tidy spec,
Host and guest now sit in one neat deck.
TCP hops forward, UDP knocks politely,
Error codes chime when inputs act unsightly.
Hop, hop — the FFI's looking less hectic.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: updating boxlite_options_add_port to handle the full PortSpec across C and Go SDKs, which is the central focus of this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/c-sdk-add-port-full-portspec

Comment @coderabbitai help to get the list of available commands and usage tips.

G4614
G4614 previously approved these changes Jun 11, 2026

@G4614 G4614 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@G4614 G4614 enabled auto-merge June 11, 2026 16:13
law-chain-hot
law-chain-hot previously approved these changes Jun 11, 2026

@law-chain-hot law-chain-hot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

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

assert_eq!(zero_guest_code, BoxliteErrorCode::InvalidArgument);
assert_eq!(null_opts_code, BoxliteErrorCode::InvalidArgument);
let ports = unsafe { &(*opts).options.ports };
…inter'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
@DorianZheng DorianZheng dismissed stale reviews from law-chain-hot and G4614 via f9fe543 June 11, 2026 16:14

@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 (2)
sdks/c/README.md (1)

835-837: ⚡ Quick win

Show return-code handling in the migration “After” snippet.

The new API now returns BoxliteErrorCode; the snippet should show checking code != Ok so migrated callers don’t accidentally ignore failures.

🤖 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/README.md` around lines 835 - 837, The example call to
boxlite_options_add_port now returns a BoxliteErrorCode but the snippet doesn’t
check it; update the migration “After” snippet to capture the return value from
boxlite_options_add_port (e.g. BoxliteErrorCode code =
boxlite_options_add_port(opts, 8080, 80, BoxlitePortProtocolTcp, NULL);) and
then check if code != Ok and handle/log/return the error accordingly so callers
don’t ignore failures.
sdks/c/src/tests.rs (1)

483-510: ⚡ Quick win

Add a regression test for invalid UTF-8 host_ip.

This suite covers null handle and zero guest port, but the documented host_ip UTF-8 rejection path is still untested. Add a case with non-UTF-8 bytes and assert InvalidArgument plus no port insertion.

🤖 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 483 - 510, Add a new assertion in the test
add_port_rejects_zero_guest_port_and_null_options (or a new test) that exercises
the host_ip UTF-8 rejection: call boxlite_options_add_port with a valid opts,
valid host_port/guest_port, BoxlitePortProtocol::BoxlitePortProtocolTcp and a
pointer to a C string containing non-UTF-8 bytes (e.g., a byte slice with
invalid UTF-8 cast to *const c_char), assert the return is
BoxliteErrorCode::InvalidArgument, verify (&(*opts).options.ports) is still
empty, and free opts with boxlite_options_free; reference new_test_options,
boxlite_options_add_port, BoxliteErrorCode, and boxlite_options_free to locate
where to add the check.
🤖 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/c/README.md`:
- Around line 835-837: The example call to boxlite_options_add_port now returns
a BoxliteErrorCode but the snippet doesn’t check it; update the migration
“After” snippet to capture the return value from boxlite_options_add_port (e.g.
BoxliteErrorCode code = boxlite_options_add_port(opts, 8080, 80,
BoxlitePortProtocolTcp, NULL);) and then check if code != Ok and
handle/log/return the error accordingly so callers don’t ignore failures.

In `@sdks/c/src/tests.rs`:
- Around line 483-510: Add a new assertion in the test
add_port_rejects_zero_guest_port_and_null_options (or a new test) that exercises
the host_ip UTF-8 rejection: call boxlite_options_add_port with a valid opts,
valid host_port/guest_port, BoxlitePortProtocol::BoxlitePortProtocolTcp and a
pointer to a C string containing non-UTF-8 bytes (e.g., a byte slice with
invalid UTF-8 cast to *const c_char), assert the return is
BoxliteErrorCode::InvalidArgument, verify (&(*opts).options.ports) is still
empty, and free opts with boxlite_options_free; reference new_test_options,
boxlite_options_add_port, BoxliteErrorCode, and boxlite_options_free to locate
where to add the check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eb5d9b44-9c5e-4b17-87d1-5edb73362ddc

📥 Commits

Reviewing files that changed from the base of the PR and between e512bd2 and 5479848.

📒 Files selected for processing (9)
  • apps/runner/pkg/boxlite/client.go
  • apps/runner/pkg/boxlite/stubs.go
  • sdks/c/README.md
  • sdks/c/include/boxlite.h
  • sdks/c/src/options.rs
  • sdks/c/src/tests.rs
  • sdks/go/boxlite_test.go
  • sdks/go/options.go
  • src/boxlite/src/runtime/options.rs

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sdks/c/src/tests.rs (1)

453-480: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Test doesn't verify the default values it's named for.

The test name add_port_zero_host_and_empty_ip_mean_defaults promises validation of default semantics, but the test only checks that both calls return Ok (lines 477-478) without asserting the stored PortSpec field values.

According to the implementation (context snippet 1) and C header documentation (context snippet 3):

  • host_port=0 should map to None (meaning "use guest_port")
  • NULL or empty host_ip should map to None (meaning "all interfaces")

Add assertions after line 478 to verify:

let ports = unsafe { &(*opts).options.ports };
assert_eq!(ports.len(), 2);
// First call: host_port=0, host_ip=NULL
assert_eq!(ports[0].host_port, None, "host_port=0 should map to None");
assert_eq!(ports[0].guest_port, 80);
assert_eq!(ports[0].host_ip, None, "NULL host_ip should map to None");
// Second call: host_port=443, host_ip=""
assert_eq!(ports[1].host_port, Some(443));
assert_eq!(ports[1].guest_port, 443);
assert_eq!(ports[1].host_ip, None, "empty host_ip should map to None");

Without these checks, a regression in default handling would go undetected.

🤖 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 453 - 480, The test
add_port_zero_host_and_empty_ip_mean_defaults currently only checks return
codes; update it to inspect the stored PortSpec entries on opts to ensure
defaults are applied: after the two boxlite_options_add_port calls and before
boxlite_options_free, read &(*opts).options.ports, assert len==2, then verify
for the first PortSpec that host_port is None, guest_port==80, host_ip is None
(NULL case), and for the second PortSpec that host_port is Some(443),
guest_port==443, and host_ip is None (empty string case); reference the test
function name, opts, options.ports, and PortSpec fields when making these
assertions.
🤖 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.

Outside diff comments:
In `@sdks/c/src/tests.rs`:
- Around line 453-480: The test add_port_zero_host_and_empty_ip_mean_defaults
currently only checks return codes; update it to inspect the stored PortSpec
entries on opts to ensure defaults are applied: after the two
boxlite_options_add_port calls and before boxlite_options_free, read
&(*opts).options.ports, assert len==2, then verify for the first PortSpec that
host_port is None, guest_port==80, host_ip is None (NULL case), and for the
second PortSpec that host_port is Some(443), guest_port==443, and host_ip is
None (empty string case); reference the test function name, opts, options.ports,
and PortSpec fields when making these assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 03591a0f-e632-4f1f-be71-f402468f74f9

📥 Commits

Reviewing files that changed from the base of the PR and between 5479848 and f9c9a17.

📒 Files selected for processing (1)
  • sdks/c/src/tests.rs

cgo maps the `enum BoxlitePortProtocol` parameter to its underlying
integer type rather than the typedef's named type, so cPortProtocol
now returns plain uint32 with explicit constant conversions — the
same shape as cRegistryTransport. Also wraps the CodeQL-suggested
assert in the C SDK test helper to satisfy rustfmt.
@DorianZheng DorianZheng disabled auto-merge June 11, 2026 16:40
@DorianZheng DorianZheng enabled auto-merge June 11, 2026 16:40
@DorianZheng DorianZheng disabled auto-merge June 11, 2026 16:40
@DorianZheng DorianZheng merged commit ba7458d into main Jun 11, 2026
40 of 42 checks passed
@DorianZheng DorianZheng deleted the fix/c-sdk-add-port-full-portspec branch June 11, 2026 16:40
G4614 added a commit that referenced this pull request Jun 12, 2026
… PortSpec impl

merging upstream/main into the PR brought in #741's PortSpec-based
WithPort but the session's earlier stub (added to unblock the runner
build before #741 landed) remained in place, causing duplicate
declarations:
  ports redeclared in struct boxConfig
  WithPort redeclared in this block
  cannot use portMapping{} as PortSpec value in append

Remove the stub block: stub's ports field, portMapping struct, and
stub WithPort func. #741's PortSpec + WithPort + ports []PortSpec are
the canonical definitions.
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.

4 participants