Skip to content

[nanvixd] E: Enable HTTP mode on Windows#18

Open
esaurez wants to merge 1 commit into
devfrom
enhancement-windows-http-mode
Open

[nanvixd] E: Enable HTTP mode on Windows#18
esaurez wants to merge 1 commit into
devfrom
enhancement-windows-http-mode

Conversation

@esaurez

@esaurez esaurez commented May 15, 2026

Copy link
Copy Markdown
Owner

Summary

Enables HTTP mode for nanvixd on Windows and lifts the standalone gateway endpoint from a Unix-only Unix-domain socket to a single cross-platform endpoint (UDS on Unix, named pipe on Windows). Same gateway_bridge_task runs on both OSes; only the binding primitive differs. This is the structural change the Nanvix containerd shim needs to drive nanvixd end-to-end on Windows.

What changed

Enabling HTTP mode on Windows

  • nanvixd: drop the cfg(unix) gates around -http-addr parsing/validation in args.rs and around the HTTP-mode block in main.rs::async_main. Remove the cfg(windows) bail! block that previously refused the flag.
  • src/libs/nanvix/Cargo.toml: nanvix-http becomes an unconditional workspace dependency (no optional, no target.'cfg(unix)'.dependencies); feature lines drop the ? suffix on nanvix-http?/x.
  • src/libs/nanvix/src/lib.rs: the pub use nanvix_http as http; re-export is no longer cfg(unix)-gated.
  • nanvix-http::server: replace the eager tokio::signal::unix::signal(SignalKind::interrupt()) with a pinned cross-platform shutdown future — SIGINT on Unix, tokio::signal::ctrl_c() on Windows. Unix behaviour is preserved.

Cross-platform standalone gateway endpoint

  • nanvix-http::client::standalone::serve_new: replace the Unix-only UnixListener/gateway_bridge_task pair with a cross-platform GatewayEndpoint enum (Unix(UnixListener) on Unix, Pipe { server: NamedPipeServer } on Windows). The accept primitive differs per-OS; the bidirectional pump is shared via a generic run_bridge<R: AsyncRead, W: AsyncWrite> helper.
  • New bind_gateway_endpoint(&path) and default_gateway_path() per-OS helpers:
    • Unix default: /tmp/nvx-standalone-gw-<pid>.sock
    • Windows default: \\.\pipe\nanvix-standalone-gw-<pid>
  • The Unix socket is created with mode 0600 so only the owner can connect (closes a pre-existing world-accessible-socket hole — UnixListener::bind inherits the process umask, typically 0755/0775).
  • On Windows the named pipe is reclaimed automatically when the bridge drops its server handle; the gateway_sockaddr field on RunningVm is only used to populate the NEW response, and is therefore annotated #[cfg_attr(windows, allow(dead_code))].

New -gateway-sockaddr CLI flag

nanvixd accepts an optional -gateway-sockaddr <path> flag (with matching StandaloneConfig::gateway_sockaddr field and accessor) so callers can pre-allocate a predictable per-VM endpoint path. When unset, nanvix-http falls back to the per-process default above, preserving existing nanvix-bench / nanvix-terminal behaviour without any flag.

NewResponse.gateway_sockaddr keeps its wire name; the doc comment is updated to describe the cross-platform semantics (UDS path on Unix, named pipe path on Windows).

Documentation

doc/run-linux.md gets a short note that the shim manages both -console-file and -gateway-sockaddr itself; any value passed in extra_args is stripped with a warning.

Why this matters

The Nanvix containerd shim drives nanvixd exclusively over HTTP (NEW / KILL / READY requests). Without this PR:

  • nanvixd refuses to start in HTTP mode on Windows; the shim's wait_for_server times out on every pod create.
  • The io_handler in uservm::standalone::handle_write_request always pushes guest stdout/stderr through output_tx expecting a consumer to drain output_rx. Without a consumer, every guest write returns -1, CPython raises BrokenPipeError at shutdown, and pods exit 120 about a minute after boot with no diagnostic trail.

The cross-platform gateway endpoint must ship in the same change as the gating removal — splitting them would land an intermediate state that breaks every networked workload on Windows.

Tests

cargo test -p nanvixd --lib args::tests   # 7 passed

New nanvixd::args::tests cover:

  • parses_http_mode_with_sockaddr-http-addr on every target.
  • parses_interactive_mode-- separator still works.
  • rejects_both_http_and_interactive / rejects_neither_http_nor_interactive — mutual-exclusion / required-mode rules.
  • parses_gateway_sockaddr_flag-gateway-sockaddr <path> is captured.
  • gateway_sockaddr_is_none_when_unset — default is None.
  • rejects_gateway_sockaddr_without_value — missing-value error message.

z.ps1 build, z.ps1 build -- format-check, z.ps1 build -- lint-check, and z.ps1 build -- spellcheck all pass on Windows.

End-to-end verification

Tested on Win Server 2025 with the Nanvix containerd shim and a CPython HTTP-server pod:

  • Shim spawns nanvixd.exe -http-addr <addr> -gateway-sockaddr \\.\pipe\nanvix-standalone-gw-<container-id> ….
  • wait_for_server() succeeds; shim POSTs NEW, then connects to the per-sandbox gateway pipe.
  • Pod boots, binds 0.0.0.0:9999, and serves Hello from Nanvix! to a probe inside the L2Bridge HCN compartment.
  • Application's print() output reaches crictl logs.
  • Cross-platform shutdown signal is exercised when containerd service-stops the shim and forwards Ctrl-C to nanvixd.

Notes for reviewers

  • This PR is part of a 4-PR series enabling the Windows shim path:
    1. This PR — HTTP mode on Windows + cross-platform standalone gateway endpoint + -gateway-sockaddr flag.
    2. [uservm] E: Named-pipe-aware stderr on Windows #19 — Named-pipe-aware get_stderr_writer on Windows (independent; lets -console-file accept \\.\pipe\…).
    3. [nanvix-http] B: serve_kill waits for VM exit #20serve_kill waits for VM exit before aborting the bridge (stacked on this PR; logically correct on both platforms; only user-visible on Windows once this PR lands).
    4. [nanvixd] E: Add -log-to-stdout flag #21nanvixd -log-to-stdout flag (independent; lets the shim capture nanvixd's own logrus output).
  • The "gateway" name is overloaded across deployment modes — multi-process has a distinct gateway service, while standalone's gateway is just a per-VM bridge to IKC channels. A follow-up could rename the standalone variant (e.g. to host_io) across both repos in one coordinated change.
  • Unix behaviour is preserved everywhere except for the new chmod 0600 on the gateway UDS, which closes a pre-existing world-accessible-socket hole. The only Unix-visible refactor is the cross-platform shutdown-signal future, which still routes SIGINT exactly like before.

Comment thread src/libs/nanvix-http/src/client/standalone.rs Outdated
Comment thread src/utils/nanvixd/src/main.rs Outdated
Comment thread src/utils/nanvixd/src/args.rs Outdated
return Err(anyhow::anyhow!("wrong usage"));
},
#[cfg(unix)]
#[cfg(any(unix, windows))]

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

WE are only supporting unix or windows, so I think this config is now redundant


#[test]
fn resolve_windows_sink_path_includes_pid() {
::std::env::remove_var("NANVIXD_GUEST_STDIO_DIR");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Update test based on the fix to the other comment

Comment thread src/libs/nanvix-http/src/client/standalone.rs Outdated
@esaurez esaurez force-pushed the enhancement-windows-http-mode branch from 5fe718c to f9252b4 Compare May 19, 2026 01:13
@esaurez esaurez force-pushed the enhancement-windows-http-mode branch from f9252b4 to 03203a8 Compare May 19, 2026 01:35
Comment thread src/libs/nanvix-http/src/client/standalone.rs
Comment thread src/libs/nanvix-http/src/client/standalone.rs Outdated
Comment thread src/libs/nanvix-http/src/client/standalone.rs Outdated
Comment thread src/libs/nanvix-http/src/message.rs Outdated
/// - Unix: Unix-domain socket path
/// - Windows: Named pipe path (e.g. `\\.\pipe\nanvix-standalone-gw-<pid>`)
///
/// In standalone mode this is always populated regardless of OS.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

This comment is probably unnecessary and confusing

/// Optional GDB server port for debugging the guest.
#[cfg(feature = "gdb")]
gdb_port: Option<u16>,
/// Optional path at which to expose the **gateway endpoint** — a

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Another comment that may need to be rewritten to not include information about how this was implemented, but focus on only on the aspects that matter for the code.

Comment thread src/utils/nanvixd/src/args.rs Outdated
{allow_host_networking} Enable host networking for the guest (disabled when \
omitted).{gdb_port_line}
omitted).
{gateway_sockaddr} <path> (Standalone) Expose the gateway endpoint at <path>. \

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

what is the gateway endpoint? It would be clear ohn what this does, and what it is useful for

* nanvixd accepts -http-addr <sockaddr> on Windows. Drops the
  cfg(unix) gates in args.rs and main.rs and the cfg(windows)
  bail block that previously refused the flag.
* nanvix-http becomes an unconditional workspace dependency in
  src/libs/nanvix/Cargo.toml. The pub re-export in nanvix/lib.rs
  is no longer cfg(unix)-gated.
* nanvix-http server uses a cross-platform shutdown signal future:
  SIGINT on Unix, tokio::signal::ctrl_c() on Windows.
* nanvix-http standalone serve_new exposes a cross-platform
  gateway endpoint -- Unix-domain socket on Unix, named pipe on
  Windows -- with a single gateway_bridge_task pumping bytes
  between the connected client and the guest's IKC channels on
  both OSes. The accept primitive differs (UnixListener vs
  tokio NamedPipeServer); the bridge body is generic over
  AsyncRead/AsyncWrite via a shared run_bridge helper.
* New -gateway-sockaddr CLI flag in nanvixd lets the caller pick
  the endpoint path explicitly. When unset, nanvix-http falls
  back to a per-process default
  (/tmp/nvx-standalone-gw-<pid>.sock on Unix,
  \\.\pipe\nanvix-standalone-gw-<pid> on Windows) so nanvix-bench
  and nanvix-terminal continue to work without the flag.
* NewResponse.gateway_sockaddr keeps its wire name; the doc
  comment is updated to describe the cross-platform semantics
  (UDS path on Unix, named pipe path on Windows).
* doc/run-linux.md: short note that the shim manages -console-file
  and -gateway-sockaddr.

The cross-platform gateway endpoint must ship with the HTTP-mode
unblock: io_handler in uservm::standalone::handle_write_request
always sends guest stdout/stderr through output_tx, expecting a
consumer to read output_rx. Without the gateway every guest
write returns -1, CPython exits 120, pods die ~60s after boot
with no diagnostic trail. The Windows shim cannot drive nanvixd
without HTTP, and HTTP-mode requires the gateway consumer; both
must land together to avoid an intermediate state that breaks
every networked workload.

Tests:
* New nanvixd args::tests cover -http-addr parsing on every
  target, mutual-exclusion / required-mode validation, and
  -gateway-sockaddr parsing (default None, missing-value error).

Verified end-to-end on Win Server 2025: containerd shim spawns
nanvixd, posts NEW, the guest application binds a TCP port and
serves traffic via the shim's gateway-pipe consumer.

Future rename suggestion (out of scope here): 'gateway' is
overloaded across deployment modes; a follow-up could rename
the standalone variant to 'host_io' across both repos.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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