Skip to content

fix: support cross-platform status and down - #129

Merged
igorls merged 2 commits into
mainfrom
codex/fix-cross-platform-status-down
Jul 5, 2026
Merged

fix: support cross-platform status and down#129
igorls merged 2 commits into
mainfrom
codex/fix-cross-platform-status-down

Conversation

@igorls

@igorls igorls commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Route meshguard status through the userspace control socket before falling back to Linux netlink/kernel WireGuard status.
  • Route meshguard down through a new STOP control command before falling back to Linux RTNETLINK teardown.
  • Make Windows named-pipe polling nonblocking and add a short Unix control-socket response timeout.
  • Update CLI and platform docs to remove stale Linux-only caveats for userspace daemon status/down.

Validation

  • zig build test --summary all passed, 90/90 tests.
  • zig build --summary all passed, 7/7 steps.
  • git diff --check passed.
  • Windows smoke: temporary gossip-only daemon returned meshguard status, then stopped via meshguard down with daemon exit code 0.
  • Windows no-daemon smoke: meshguard status reports control socket unavailable.

Closes #125

Copilot AI review requested due to automatic review settings July 5, 2026 16:26

Copilot AI 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.

Pull request overview

This PR wires meshguard status and meshguard down through the cross-platform userspace control socket first (Unix socket / Windows named pipe), with Linux netlink/kernel fallbacks when the control socket is unavailable, and updates docs to reflect the new behavior across platforms.

Changes:

  • Add STOP control command and client-side Control.request() helper for Unix/Windows IPC.
  • Update CLI status/down commands to prefer control socket responses, falling back to Linux RTNETLINK/kernel status/teardown.
  • Refresh CLI + platform docs to remove stale “Linux-only” caveats for userspace-daemon status/down.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/services/control.zig Adds STOP, shared response formatter, Windows nonblocking named-pipe polling tweaks, and a cross-platform request() client helper with Unix timeout.
src/main.zig Routes status/down through Control.request() first; adds control-socket status printing; wires daemon stop flag.
README.md Removes Linux-only notes for status/down examples and checklist item.
docs/reference/cli.md Updates down/status docs to describe control-socket behavior + Linux fallback.
docs/guide/windows-support.md Updates Windows support matrix/docs to reflect named-pipe-backed status/down.
docs/guide/macos-support.md Notes control-socket-backed status/down for userspace daemon.
docs/guide/getting-started.md Updates getting-started steps to describe control-socket shutdown/status + Linux fallback.
docs/guide/freebsd-support.md Notes control-socket-backed status/down for userspace daemon.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/services/control.zig
Comment thread src/main.zig Outdated
Comment on lines +3539 to +3543
} else |_| {
if (comptime @import("builtin").os.tag != .linux) {
try stderr.writeStreamingAll(zio(), "meshguard is not running (control socket unavailable).\n");
std.process.exit(1);
};
}
Comment thread src/main.zig
Comment on lines +3562 to 3571
var response_buf: [4096]u8 = undefined;
if (lib.services.Control.request(allocator, "STATUS", &response_buf)) |n| {
try printControlStatus(allocator, stdout, response_buf[0..n]);
return;
} else |_| {
if (comptime @import("builtin").os.tag != .linux) {
try stderr.writeStreamingAll(zio(), "meshguard is not running (control socket unavailable).\n");
std.process.exit(1);
}
}
Comment thread src/services/control.zig Outdated
Comment on lines +269 to +279
{
return false;
}
if (err == @as(win.windows.Win32Error, @enumFromInt(536))) return false;
Comment thread src/main.zig
Comment on lines +3769 to +3772
var parsed = std.json.parseFromSlice(std.json.Value, allocator, response, .{}) catch {
try stdout.writeStreamingAll(zio(), "meshguard is running.\n");
try writeFormatted(stdout, " control response: {s}", .{response});
return;
Comment thread src/main.zig
Comment on lines +3778 to +3782
else => {
try stdout.writeStreamingAll(zio(), "meshguard is running.\n");
try writeFormatted(stdout, " control response: {s}", .{response});
return;
},

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 778cfb0ab8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/services/control.zig
Comment on lines +334 to +336
if (std.mem.eql(u8, cmd, "STOP")) {
if (self.stop_flag) |flag| {
flag.store(false, .release);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require authorization before honoring STOP

When the daemon is using the default Unix control socket, listenUnix chmods the socket to 0666, so any local user can connect to /run/meshguard/meshguard.sock. Adding this unauthenticated STOP handler lets an unprivileged local account terminate the meshguard daemon with meshguard down or a raw socket write, which is a local DoS against the VPN service; the stop path should be restricted to an authorized user/group or otherwise authenticated before flipping the daemon's running flag.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b10e60cdcf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/main.zig
// SWIM-driven handshake retransmit is only needed for the userspace WG data
// plane; kernel WireGuard retransmits its own, and gossip-only has none.
swim.retransmit_handshakes = !use_kernel_wg and !gossip_only;
control.setStopFlag(&swim.running);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Poll the control socket in kernel mode

For meshguard up --kernel, this wires STOP to swim.running, but the later kernel branch enters swim.run() and never calls control.poll(). In that mode the Unix socket can accept a meshguard down connection but no handler ever reads STOP, so the client times out and falls back to deleting mg0 while the daemon process keeps running instead of shutting down.

Useful? React with 👍 / 👎.

Comment thread src/services/control.zig
Comment on lines +576 to +577
if (win.ReadFile(handle, @ptrCast(out.ptr), @intCast(out.len), &bytes_read, null) == @as(win.BOOL, @enumFromInt(0))) {
return error.ReadFailed;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add a timeout to Windows pipe reads

This synchronous ReadFile has no timeout, so on Windows a meshguard status or meshguard down can hang indefinitely once it opens the pipe but the daemon does not reply. I checked windowsEventLoop: it only reaches control_socket.poll() after draining UDP, and the Windows UDP socket is created as a blocking socket in UdpSocket.bindAddr, so an idle Wintun daemon with no UDP traffic can leave this client blocked forever; the Unix request path avoids this with a 1s poll.

Useful? React with 👍 / 👎.

@igorls
igorls merged commit 6ada229 into main Jul 5, 2026
5 checks passed
@igorls
igorls deleted the codex/fix-cross-platform-status-down branch July 5, 2026 16:55
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.

Make status and down work through cross-platform control sockets

2 participants