a73x

f6d1cb06

refactor: muxd reads MUX_KEY_FILE through xdg.pickKey

a73x   2026-08-27 08:15

Commit message
refactor: muxd reads MUX_KEY_FILE through xdg.pickKey

envKey existed to read "set but empty" as unset, which is the rule
xdg.pickKey already applies to both of its arguments. muxd keeps only
the tail that is its own — the default path, which no client reaches
for — and `--key ""` now falls through like the empty variable always
did, instead of arriving at Key.load as a path.

src/cli/main.zig
Old New
@@ -37,14 +37,7 @@ const usage =
37 /// intent sits higher. Split out so the order is testable without a 37 /// intent sits higher. Split out so the order is testable without a
38 /// daemon. 38 /// daemon.
39 fn pickKey(flag: ?[]const u8, env: ?[]const u8, default_if_present: ?[]const u8) ?[]const u8 { 39 fn pickKey(flag: ?[]const u8, env: ?[]const u8, default_if_present: ?[]const u8) ?[]const u8 {
40 return flag orelse env orelse default_if_present; 40 return xdg.pickKey(flag, env) orelse default_if_present;
41 }
42
43 /// MUX_KEY_FILE, with "set but empty" read as unset — an empty path
44 /// could only be a mistake, and Key.load would blame "".
45 fn envKey() ?[]const u8 {
46 const v = std.posix.getenv(xdg.key_env) orelse return null;
47 return if (v.len == 0) null else v;
48 } 41 }
49 42
50 const Cmd = enum { run, dump, stats, proxy, endpoint, version, help, keygen, start, stop, upgrade }; 43 const Cmd = enum { run, dump, stats, proxy, endpoint, version, help, keygen, start, stop, upgrade };
@@ -565,7 +558,7 @@ fn run(alloc: std.mem.Allocator, o: Opts, sock_path: []const u8) !u8 {
565 defer alloc.free(dflt); 558 defer alloc.free(dflt);
566 const dflt_if_present: ?[]const u8 = 559 const dflt_if_present: ?[]const u8 =
567 if (std.fs.cwd().access(dflt, .{})) |_| dflt else |_| null; 560 if (std.fs.cwd().access(dflt, .{})) |_| dflt else |_| null;
568 const key_path = pickKey(o.key, envKey(), dflt_if_present) orelse { 561 const key_path = pickKey(o.key, std.posix.getenv(xdg.key_env), dflt_if_present) orelse {
569 std.debug.print( 562 std.debug.print(
570 "muxd: no key: pass --key, set MUX_KEY_FILE, or run `muxd keygen` (default {s})\n", 563 "muxd: no key: pass --key, set MUX_KEY_FILE, or run `muxd keygen` (default {s})\n",
571 .{dflt}, 564 .{dflt},
@@ -1055,7 +1048,7 @@ fn announceKey(alloc: std.mem.Allocator) ?quic.Key {
1055 const dflt: ?[]const u8 = xdg.keyPath(alloc) catch null; 1048 const dflt: ?[]const u8 = xdg.keyPath(alloc) catch null;
1056 defer if (dflt) |p| alloc.free(p); 1049 defer if (dflt) |p| alloc.free(p);
1057 1050
1058 switch (announceKeyFrom(envKey(), dflt)) { 1051 switch (announceKeyFrom(xdg.pickKey(null, std.posix.getenv(xdg.key_env)), dflt)) {
1059 .key => |k| return k, 1052 .key => |k| return k,
1060 .no_path => std.debug.print( 1053 .no_path => std.debug.print(
1061 "muxd endpoint: no HOME to resolve a key path; staying on ssh\n", 1054 "muxd endpoint: no HOME to resolve a key path; staying on ssh\n",
@@ -1482,6 +1475,9 @@ test "pickKey: --key beats MUX_KEY_FILE beats the default path" {
1482 try std.testing.expectEqualStrings("/dflt", pickKey(null, null, "/dflt").?); 1475 try std.testing.expectEqualStrings("/dflt", pickKey(null, null, "/dflt").?);
1483 // Nothing named anywhere is the triad-message case, not a silent skip. 1476 // Nothing named anywhere is the triad-message case, not a silent skip.
1484 try std.testing.expect(pickKey(null, null, null) == null); 1477 try std.testing.expect(pickKey(null, null, null) == null);
1478 // Set but empty is unset, for the flag as well as the variable: an
1479 // empty path could only be a mistake, and Key.load would blame "".
1480 try std.testing.expectEqualStrings("/dflt", pickKey("", "", "/dflt").?);
1485 } 1481 }
1486 1482
1487 test "parseArgs: start takes run's flags" { 1483 test "parseArgs: start takes run's flags" {