Skip to content

Windows support: dual-stack IPv4/IPv6 sockets, setup.bat, and just dev#1732

Merged
kixelated merged 3 commits into
mainfrom
claude/zealous-mcclintock-6a322b
Jun 14, 2026
Merged

Windows support: dual-stack IPv4/IPv6 sockets, setup.bat, and just dev#1732
kixelated merged 3 commits into
mainfrom
claude/zealous-mcclintock-6a322b

Conversation

@kixelated

@kixelated kixelated commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

What

Two related changes that get MoQ building, running, and developing on Windows.

1. Dual-stack IPv6 sockets (the bug)

Quinn and the relay's axum HTTP/HTTPS listeners bind a single [::] socket and rely on the OS to route both address families. Linux does, but Windows defaults IPV6_V6ONLY to on, so an IPv6 socket silently drops all IPv4 traffic. The result on Windows:

  • a relay on [::] is unreachable over 127.0.0.1 (both QUIC/UDP and the cert/WebSocket HTTP listener), and
  • a client on [::] cannot dial an IPv4 server (the existing pick_addr IPv4-mapping only works on a dual-stack socket).

Fix: clear IPV6_V6ONLY before binding, via socket2. The helpers live in a new pub mod bind:

  • bind::udp backs the quinn and noq client/server sockets (crate::bind::udp).
  • bind::tcp backs the relay and CLI HTTP/HTTPS listeners through axum_server::from_tcp.

2. Windows onboarding

  • setup.bat at the repo root: a winget-based installer for the Rust + JS toolchain, the MSVC C++ Build Tools, the ffmpeg CLI (demo media), and gh/uv. Idempotent.
  • doc/setup/windows.md (registered in the sidebar, linked from the setup index).
  • The demo free-port picker now guards lsof behind command -v and falls back to the start port when it is missing (Git Bash ships no lsof). This unblocks just dev.

Why

Out of the box on Windows: cargo build worked only after rustup update (deps need a recent stable), the relay/clients couldn't talk over IPv4, and just dev failed on lsof + the missing POSIX shell.

Verification (Windows 11, MSVC toolchain)

  • Core workspace builds; moq-native + moq-relay tests pass (the 16 auth::tests failures are pre-existing and reproduce on the base tree: a Windows temp-path-as-URL fixture issue, unrelated to this change).
  • cargo doc with -D warnings is clean on moq-native.
  • End-to-end over IPv4 (127.0.0.1): HTTP cert fetch and a QUIC publish/subscribe clock round-trip both succeed; the relay logs the client as ::ffff:127.0.0.1. Both were connection-refused before.
  • just dev from a Git Bash terminal runs the relay, publishes Big Buck Bunny via ffmpeg, and serves the web UI at localhost:5173 (playback confirmed).

Reviewer notes

  • Public API: adds the moq_native::bind module with bind::tcp and bind::udp (additive, non-breaking). Targets main per the branch rules (bug fix + additive).
  • cargo clippy on this machine flags ~20 pre-existing result_large_err lints in quinn.rs/tls.rs; they come from clippy 1.96 being newer than the Nix-pinned toolchain, not from this change. None are in the new code.
  • setup.bat was run on a real machine; the just dev chain was smoke-tested end to end.

(Written by Claude)

kixcord and others added 2 commits June 14, 2026 12:09
Quinn and the relay's axum HTTP/HTTPS listeners bind a single `[::]` socket and
rely on the OS to route both address families. Linux does, but Windows defaults
IPV6_V6ONLY to on, so an IPv6 socket silently drops all IPv4 traffic. A relay on
`[::]` is then unreachable over 127.0.0.1, and a client on `[::]` cannot dial an
IPv4 server (the existing pick_addr IPv4-mapping only works on a dual-stack socket).

Clear IPV6_V6ONLY before binding via socket2. A new bind_udp helper backs the
quinn and noq client/server sockets; a new public bind_tcp helper backs the relay
and CLI HTTP/HTTPS listeners via axum_server::from_tcp.

Verified end-to-end over IPv4 (127.0.0.1) on Windows: HTTP cert fetch and a QUIC
publish/subscribe round-trip both succeed (relay logs the client as
::ffff:127.0.0.1). Both were connection-refused before.

Public API: adds moq_native::bind_tcp (additive).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a winget-based setup.bat and a Windows setup page (registered in the
sidebar and linked from the setup index). The script installs the Rust + JS
toolchain, the MSVC C++ Build Tools, the ffmpeg CLI used by the demo, and gh/uv.

Guard the demo free-port picker so it falls back to the start port when lsof is
unavailable (Git Bash on Windows). With ffmpeg present and run from Git Bash,
`just dev` now works end-to-end on Windows: relay, ffmpeg publish, and the web
UI at localhost:5173.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds dual-stack IPv4/IPv6 socket binding utilities (bind_udp, bind_tcp) to moq-native using the socket2 crate. All UDP socket creation in quinn.rs and noq.rs, and all HTTP/HTTPS server listener setup in moq-cli and moq-relay, are migrated to these utilities. Separately, a setup.bat Windows installer script is added alongside a new doc/setup/windows.md guide covering winget-based toolchain installation, relay/CLI usage, and demo execution. The VitePress sidebar and setup index are updated to reference the new Windows page, and the demo/justfile port-probe loop is gated behind an lsof availability check.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: Windows support, dual-stack IPv4/IPv6 socket fixes, setup.bat automation, and just dev integration.
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.
Description check ✅ Passed The pull request description clearly outlines two related changes: dual-stack IPv6 socket support for Windows compatibility and Windows onboarding materials, directly matching the changeset.

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

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/zealous-mcclintock-6a322b

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 and usage tips.

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
setup.bat (1)

1-73: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use CRLF line endings for batch reliability on Windows.

This .bat file is currently LF-only; convert it to CRLF to avoid cmd parsing edge cases on Windows.

🤖 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 `@setup.bat` around lines 1 - 73, The setup.bat file is currently using LF
(Unix-style) line endings, which can cause parsing issues on Windows when the
script is executed. Convert all line endings throughout the entire setup.bat
file from LF to CRLF (Windows-style carriage return + line feed). This can be
done using a text editor's line ending conversion feature (most modern editors
like VS Code have this functionality) or by running a command-line tool designed
for this purpose. Ensure the entire file, from the opening `@echo` off to the
closing endlocal, uses CRLF line endings consistently.

Source: Linters/SAST tools

🤖 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 `@rs/moq-native/src/util.rs`:
- Around line 118-125: The bind_udp_ipv6_is_dual_stack test currently assumes
unconditional IPv6 and only_v6 socket option support, which creates CI flakiness
on platforms without these capabilities. Since bind_udp is intentionally
best-effort, modify the test to gracefully handle cases where IPv6 binding or
reading the only_v6 socket option is not supported. Use conditional logic (such
as skip or early return) to handle failures in the bind_udp call at line 122 or
the only_v6call at line 124, rather than panicking on unsupported platforms.

---

Outside diff comments:
In `@setup.bat`:
- Around line 1-73: The setup.bat file is currently using LF (Unix-style) line
endings, which can cause parsing issues on Windows when the script is executed.
Convert all line endings throughout the entire setup.bat file from LF to CRLF
(Windows-style carriage return + line feed). This can be done using a text
editor's line ending conversion feature (most modern editors like VS Code have
this functionality) or by running a command-line tool designed for this purpose.
Ensure the entire file, from the opening `@echo` off to the closing endlocal, uses
CRLF line endings consistently.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2bf90321-3a82-43fc-ba06-b6c686f6eabd

📥 Commits

Reviewing files that changed from the base of the PR and between e484505 and fd059fb.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • demo/justfile
  • doc/.vitepress/config.ts
  • doc/setup/index.md
  • doc/setup/windows.md
  • rs/moq-cli/src/web.rs
  • rs/moq-native/Cargo.toml
  • rs/moq-native/src/lib.rs
  • rs/moq-native/src/noq.rs
  • rs/moq-native/src/quinn.rs
  • rs/moq-native/src/util.rs
  • rs/moq-relay/src/web.rs
  • setup.bat

Comment thread rs/moq-native/src/util.rs Outdated
Comment on lines +118 to +125
#[test]
fn bind_udp_ipv6_is_dual_stack() {
// An IPv6 wildcard bind should come back dual-stack so IPv4 traffic
// reaches it. socket2 lets us read the option back to confirm.
let socket = bind_udp("[::]:0".parse().unwrap()).unwrap();
let socket = socket2::Socket::from(socket);
assert!(!socket.only_v6().unwrap(), "IPv6 socket should be dual-stack");
}

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Harden the IPv6 dual-stack test for platform capability variance.

bind_udp is intentionally best-effort, but Line [122] and Line [124] currently require unconditional IPv6 + only_v6 support, which can fail on valid environments and create CI flakiness.

Suggested adjustment
 #[test]
 fn bind_udp_ipv6_is_dual_stack() {
-	let socket = bind_udp("[::]:0".parse().unwrap()).unwrap();
+	let socket = match bind_udp("[::]:0".parse().unwrap()) {
+		Ok(socket) => socket,
+		Err(err)
+			if matches!(
+				err.kind(),
+				std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::Unsupported
+			) =>
+		{
+			return; // host/runner doesn't support this capability
+		}
+		Err(err) => panic!("unexpected bind failure: {err}"),
+	};
 	let socket = socket2::Socket::from(socket);
-	assert!(!socket.only_v6().unwrap(), "IPv6 socket should be dual-stack");
+	if let Ok(only_v6) = socket.only_v6() {
+		assert!(!only_v6, "IPv6 socket should be dual-stack");
+	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#[test]
fn bind_udp_ipv6_is_dual_stack() {
// An IPv6 wildcard bind should come back dual-stack so IPv4 traffic
// reaches it. socket2 lets us read the option back to confirm.
let socket = bind_udp("[::]:0".parse().unwrap()).unwrap();
let socket = socket2::Socket::from(socket);
assert!(!socket.only_v6().unwrap(), "IPv6 socket should be dual-stack");
}
#[test]
fn bind_udp_ipv6_is_dual_stack() {
// An IPv6 wildcard bind should come back dual-stack so IPv4 traffic
// reaches it. socket2 lets us read the option back to confirm.
let socket = match bind_udp("[::]:0".parse().unwrap()) {
Ok(socket) => socket,
Err(err)
if matches!(
err.kind(),
std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::Unsupported
) =>
{
return; // host/runner doesn't support this capability
}
Err(err) => panic!("unexpected bind failure: {err}"),
};
let socket = socket2::Socket::from(socket);
if let Ok(only_v6) = socket.only_v6() {
assert!(!only_v6, "IPv6 socket should be dual-stack");
}
}
🤖 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 `@rs/moq-native/src/util.rs` around lines 118 - 125, The
bind_udp_ipv6_is_dual_stack test currently assumes unconditional IPv6 and
only_v6 socket option support, which creates CI flakiness on platforms without
these capabilities. Since bind_udp is intentionally best-effort, modify the test
to gracefully handle cases where IPv6 binding or reading the only_v6 socket
option is not supported. Use conditional logic (such as skip or early return) to
handle failures in the bind_udp call at line 122 or the only_v6call at line 124,
rather than panicking on unsupported platforms.

Move the dual-stack socket helpers out of the internal `util` module into a new
`pub mod bind`, so callers read `moq_native::bind::tcp` / `bind::udp` and the
crate-internal sockets use `crate::bind::udp`. `udp` is now public alongside
`tcp`. Also drops the public-to-private doc links the flat `bind_tcp` re-export
carried.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kixelated
kixelated enabled auto-merge (squash) June 14, 2026 19:21
@kixelated
kixelated disabled auto-merge June 14, 2026 19:24
@kixelated
kixelated merged commit 39c1ef7 into main Jun 14, 2026
1 check failed
@kixelated
kixelated deleted the claude/zealous-mcclintock-6a322b branch June 14, 2026 19:24
@moq-bot moq-bot Bot mentioned this pull request Jun 14, 2026
kixelated added a commit that referenced this pull request Jun 14, 2026
PR #1732 added an autolink to http://localhost:4443/certificate.sha256 in
setup/windows.md, which VitePress can't reach at build time, failing the doc
build (and every PR's `just ci`). Generalize the existing localhost exemption
from a single :5173 literal to a regex covering any localhost URL.

Folded in here so this PR is green on its own; supersedes #1734.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@moq-bot moq-bot Bot mentioned this pull request Jun 16, 2026
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