From f1748b681d48559adb80e6a35cb7050db33b85a5 Mon Sep 17 00:00:00 2001 From: claude-hdb Date: Tue, 14 Jul 2026 02:24:20 +0000 Subject: [PATCH] fix(doctor): four checks that lied, and none of them about a real fault MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run the doctor on a healthy host and it reported three problems, all of them its own: · dns.mode=none was flagged as leftover rehearsal dirt. It is now the SHIPPED setting — it is what stops a box enumerating its siblings. Its absence is the fault; its presence was being "fixed" away. · the nft check used 'sudo -n', which fails without cached credentials, so it reported the box-to-box rule MISSING on a host where 'sudo nft list' plainly shows it. · the kernel's bridge view — the one fact that settles the isolation question — was skipped with "'bridge' not installed". It is installed; it lives in /usr/sbin, which is not on a normal user's PATH. · and the DNS probe looked only for the drill's own box names, so it said "no box to probe with" while two boxes sat there RUNNING. A diagnostic that cries wolf is worse than no diagnostic: it costs the same trust as a real failure and teaches you to ignore it. All four now report what is actually true. Co-Authored-By: Claude Fable 5 --- drill/doctor.sh | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drill/doctor.sh b/drill/doctor.sh index 0879743..bdba491 100755 --- a/drill/doctor.sh +++ b/drill/doctor.sh @@ -41,12 +41,15 @@ timeout 10 incus list >/dev/null 2>&1 || { head_ "Network — claudenet" if incus network show claudenet >/dev/null 2>&1; then + # dns.mode=none is SHIPPED — it is what stops a box enumerating its siblings + # through the gateway's dnsmasq. Its ABSENCE is the problem, not its presence. dns="$(incus network get claudenet dns.mode 2>/dev/null)" - if [ -z "$dns" ] || [ "$dns" = managed ]; then - ok "dns.mode = ${dns:-}" + if [ "$dns" = none ]; then + ok "dns.mode = none — a box cannot enumerate its siblings by name" else - no "dns.mode = $dns ← the drill's phase D left this behind. Boxes minted now get NO working DNS." - [ "$FIX" = 1 ] && { incus network unset claudenet dns.mode && inf "reverted: dns.mode unset"; } + no "dns.mode = ${dns:-} — a box can RESOLVE its siblings' names and addresses" + inf "fix: re-run ~/.local/share/claudebox/host/setup-host.sh" + [ "$FIX" = 1 ] && { incus network set claudenet dns.mode=none && inf "set: dns.mode=none"; } fi inf "ipv4.address = $(incus network get claudenet ipv4.address 2>/dev/null)" # Incus reports the network as "Created" whether or not anything is actually @@ -79,7 +82,7 @@ else fi head_ "Firewall — the box-to-box drop" -if nft list table bridge claudebox >/dev/null 2>&1 || sudo -n nft list table bridge claudebox >/dev/null 2>&1; then +if sudo nft list table bridge claudebox >/dev/null 2>&1; then ok "nft bridge table 'claudebox' is present — boxes cannot reach each other" else no "the box-to-box drop is MISSING — boxes can reach each other" @@ -132,8 +135,12 @@ fi # tap — and then boxes reach each other while every config says they cannot. # Ask the kernel. head_ "Bridge ports — the KERNEL's view (config is a claim; this is the fact)" -if command -v bridge >/dev/null 2>&1; then - ports="$(sudo bridge -d link show 2>/dev/null | grep -A1 'master claudenet')" +BRIDGE="" +for c in bridge /usr/sbin/bridge /sbin/bridge; do + sudo "$c" -V >/dev/null 2>&1 && { BRIDGE="$c"; break; } +done +if [ -n "$BRIDGE" ]; then + ports="$(sudo "$BRIDGE" -d link show 2>/dev/null | grep -A1 'master claudenet')" if [ -z "$ports" ]; then inf "no instance is attached to claudenet right now (mint a box to check the taps)" else @@ -148,7 +155,7 @@ if command -v bridge >/dev/null 2>&1; then fi fi else - inf "'bridge' (iproute2) not installed — cannot read the kernel's view" + inf "'bridge' (iproute2) not found — cannot read the kernel's view" fi head_ "Instances" @@ -194,10 +201,9 @@ if [ "$PIN" = 1 ]; then fi head_ "Can a box actually resolve DNS?" -probe="" -for b in drill archive peer clone; do - incus config show "$b" >/dev/null 2>&1 && { probe="$b"; break; } -done +# Any box will do — the drill's names are not the only boxes on a host. +probe="$(incus list "user.claudebox=1" --format csv --columns ns 2>/dev/null \ + | awk -F, '$2 == "RUNNING" { print $1; exit }')" if [ -n "$probe" ] && [ "$FIX" != 1 ]; then inf "probing inside '$probe' — this separates DNS from routing, which is the whole question:" inf "its resolv.conf: $(timeout 20 incus exec "$probe" -- sh -c 'grep -m2 nameserver /etc/resolv.conf' 2>/dev/null | tr '\n' ' ')"