fix(update): block autoupdate setup when running as root and add doctor install subcommand (swamp-club#368)#1398
Conversation
…or install subcommand (swamp-club#368) The official install.sh with sudo produces a root-owned binary that the unprivileged autoupdate scheduler cannot replace. This adds two defenses: 1. `swamp update --setup-auto` now refuses when running as root (POSIX), since the scheduler will later run as the normal user. 2. New `swamp doctor install` subcommand reports installation health: binary path/owner, writability, version, autoupdate status, and last update outcome. Works outside a swamp repo. Exits 1 when unhealthy. The existing inline permission warning now references `swamp doctor install` for diagnostics. Co-authored-by: Blake Irvin <blakeirvin@me.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
CLI UX Review
Blocking
None.
Suggestions
-
Missing remediation advice for scheduler-not-installed (
src/presentation/renderers/doctor_install.ts)
ThehasProblemcheck marks the install UNHEALTHY whenautoupdate.enabled && !schedulerInstalled, and theScheduler: not installedline turns red — but the advisory block at the bottom only fires for thewritable === "fail"case. A user who sees UNHEALTHY with no bottom advisory (because writability is fine but the scheduler is missing) has to infer the fix themselves. A line like⚠ Autoupdate is enabled but the scheduler is not installed. Run \swamp update --setup-auto` to install it.` would close the loop. -
No recovery hint when last check errored but binary is now writable (
src/presentation/renderers/doctor_install.ts)
WhenlastEntry.outcome === "error"is the sole reason for UNHEALTHY (writable is pass, scheduler is installed), the command exits 1 with UNHEALTHY and shows the error text inline, but gives no "what next" hint. A briefRun \swamp update` to retry.` below the Last error line would help. -
Final status wording differs from sibling doctor commands (
src/presentation/renderers/doctor_install.ts)
doctor_extensionsends withOVERALL: PASS/OVERALL: FAIL;doctor_installends withHEALTHY/UNHEALTHY. Both live underswamp doctor, so users who run both will see different conventions. Either is fine on its own — worth aligning if a future pass standardises the doctor suite.
Verdict
PASS — the new swamp doctor install command is well-scoped, help text is clear, both log and JSON modes are implemented, the root-check error in update --setup-auto is actionable, and exit codes are correct. The suggestions above are advisory and do not block merge.
There was a problem hiding this comment.
Code Review
Clean, well-structured PR. The domain logic, presentation layer, CLI wiring, and root guard all follow established codebase patterns.
Blocking Issues
None.
Suggestions
-
Write-probe race window (
src/cli/commands/doctor_install.ts:50-56): TheprobeBinaryWritabledep writes tobinaryPath + ".swamp-write-test"— the same path used byrunSetupAutoinupdate.ts:145. If a user somehow runs both commands concurrently, they'd race on the same probe file. This is extremely unlikely in practice, but a randomized suffix (e.g..swamp-write-test.${pid}) would close it. -
Username in owner when uid is null (
src/domain/update/install_health.ts:103-106): Whenstat.uidis null (Windows),usernamefalls through as the current user's name. The renderer skips the owner line foruid === null, so this is unused — but settingusernametonullexplicitly when uid is unknown would make the data model more honest.
Both are minor — neither affects correctness or mergeability.
Overall: license headers present on all new files, both log and json output modes supported, 12 tests cover the key branches, no libswamp boundary violations, no fire-and-forget promises, root guard properly uses UserError with rethrow pattern. Looks good to ship.
There was a problem hiding this comment.
Adversarial Review
Critical / High
None found.
Medium
-
Duplicated health-status logic across renderers —
src/presentation/renderers/doctor_install.ts:91-93andsrc/presentation/renderers/doctor_install.ts:127-130: ThehasProblemdetermination is computed independently in bothLogDoctorInstallRendererandJsonDoctorInstallRendererwith identical logic. If one is updated without the other, the JSON and log modes will disagree on whether an install is healthy.Breaking example: A future contributor adds a new unhealthy condition (e.g.
cadence === "weekly" && lastEntry older than 14 days) to the log renderer but forgets the JSON renderer.swamp doctor installreports UNHEALTHY and exits 1, butswamp doctor install --jsonreports"overallStatus": "healthy"and exits 0.Suggested fix: Extract the health determination into a shared pure function (e.g.
function isHealthy(report: InstallHealthReport): booleanin the domain layer or alongside the renderer factory) and call it from both renderers.
Low
-
probeBinaryWritablereturns wrong result if remove fails —src/cli/commands/doctor_install.ts:51-57: IfDeno.writeTextFilesucceeds butDeno.removethrows (e.g. some exotic filesystem race), the catch block returnsfalseeven though the directory is writable, and the probe file is leaked. This matches the existing pattern inupdate.ts:145-148and is extremely unlikely in practice, so not actionable on its own. -
No test for "different non-root user" ownership branch —
src/domain/update/install_health.ts:93-98: Theelsebranch (binary owned by a non-root, non-current user) is untested. The logic is straightforward but a test would pin theuid:${stat.uid}message format. -
TOCTOU between probe and scheduled autoupdate — The writability probe is a point-in-time check; permissions can change before the scheduler runs. This is inherent to the diagnostic nature of the tool and not a real bug.
Verdict
PASS — Clean dependency injection, proper error boundary in the root-check (update.ts), correct cross-platform fallbacks, thorough test coverage. The duplicated health-status logic (Medium #1) is worth extracting but doesn't block.
Summary
swamp update --setup-autowhen running as root (POSIX) — the scheduler runs as the normal user and cannot replace a root-owned binaryswamp doctor installsubcommand that reports installation health: binary path/owner, writability, version, autoupdate status, last update outcomeswamp doctor installFixes swamp-club#368.
What it looks like
Healthy install:
Unhealthy install (root-owned binary):
Follow-ups
Test plan
shutdown_handlers_testunrelated)deno check,deno lint,deno fmt— all cleanswamp doctor installandswamp doctor install --jsonboth produce correct output--repo-dirneeded)swamp doctor --helplistsinstallsubcommand