From 00bbde2373f138fbecc5be4572cd87dc4584d418 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 11 Mar 2026 00:09:20 -0700 Subject: [PATCH] fix: detect correct VM user on NixOS (skip nixbld users) Two bugs fixed: 1. Post-rebuild agent wait loop hardcoded `incus` commands instead of using the runtime abstraction. On Lima (macOS), this always failed, causing a 120s timeout on every create. 2. detect_vm_username/detect_vm_uid matched NixOS nixbld* users (UID 30001+ with home /var/empty) instead of the actual user. Now filters to users with home under /home/. Co-Authored-By: Claude Opus 4.6 --- src/runtime/incus.rs | 6 +++--- src/sandbox/provision.rs | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/runtime/incus.rs b/src/runtime/incus.rs index df043a0..2d4e8ff 100644 --- a/src/runtime/incus.rs +++ b/src/runtime/incus.rs @@ -72,13 +72,13 @@ impl IncusRuntime { Ok(()) } /// Detect the UID of the first non-root user in the VM. + /// Filters to users with home under /home/ to exclude NixOS nixbld* users + /// (UID 30001+ with home /var/empty). async fn detect_vm_uid(vm: &str) -> Option { - // Find first user with UID >= 1000 (standard non-system user) - // Use bash -lc to get login shell PATH (NixOS needs /run/current-system/sw/bin/) let result = run_cmd( "incus", &["exec", vm, "--", "bash", "-lc", - "awk -F: '$3 >= 1000 && $3 < 65534 { print $3; exit }' /etc/passwd"], + "awk -F: '$3 >= 1000 && $3 < 65534 && $6 ~ /^\\/home\\// { print $3; exit }' /etc/passwd"], ).await.ok()?; let uid = result.stdout.trim().to_string(); if uid.is_empty() { None } else { Some(uid) } diff --git a/src/sandbox/provision.rs b/src/sandbox/provision.rs index 3a81aef..922cb8f 100644 --- a/src/sandbox/provision.rs +++ b/src/sandbox/provision.rs @@ -323,20 +323,14 @@ async fn provision_nixos( if rebuild_ok { // Wait for the VM agent to come back after system activation. - // nixos-rebuild switch restarts incus-agent as part of the new config. + // On Incus, nixos-rebuild restarts incus-agent; on Lima, SSH may drop briefly. println!("Waiting for VM agent to restart after system activation..."); - // Small delay to let the old agent fully stop tokio::time::sleep(std::time::Duration::from_secs(5)).await; - let vm = format!("devbox-{name}"); let max_attempts = 40; // 40 * 3s = 120s let mut agent_ready = false; for i in 0..max_attempts { - let check = crate::runtime::cmd::run_cmd( - "incus", - &["exec", &vm, "--", "echo", "ready"], - ) - .await; + let check = runtime.exec_cmd(name, &["echo", "ready"], false).await; if let Ok(r) = check { if r.exit_code == 0 && r.stdout.trim() == "ready" { println!("NixOS rebuild complete. VM agent is ready."); @@ -1355,11 +1349,14 @@ fn whoami() -> String { /// find the first user with UID >= 1000 from /etc/passwd. /// On Lima, exec_cmd runs as the Lima user, so `whoami` works. /// Falls back to the host username if detection fails. +/// +/// Filters: UID 1000-65533, home under /home/ (excludes NixOS nixbld* users +/// which have UID 30001+ but home /var/empty). async fn detect_vm_username(runtime: &dyn Runtime, name: &str) -> String { let result = runtime .exec_cmd( name, - &["bash", "-lc", "awk -F: '$3 >= 1000 && $3 < 65534 { print $1; exit }' /etc/passwd"], + &["bash", "-lc", "awk -F: '$3 >= 1000 && $3 < 65534 && $6 ~ /^\\/home\\// { print $1; exit }' /etc/passwd"], false, ) .await;