Repository navigation
fix(install): ask for the root password before the work, not partway through - #394
Merged
Merged
Conversation
…through
tailscale's official installer goes through the distro package manager, so it
escalates on its own after refreshing package lists. That is tolerable when it
is the only thing running and miserable inside `moshcode update`, which walks a
plan: moshcode itself, then every installed engine, then every tool. On a box
with tailscale installed the prompt lands at step 17 of 18 — the operator sees a
long stream of downloads, looks away, and comes back to a run parked on a
password prompt, or to the escalation helper's own timeout having failed the
step. The work is unattended by design; the one interactive moment in it should
not be buried where nobody is looking.
So ask first. New primeEscalation() warms the credential before the plan starts,
and because the helper caches against the terminal that every installer
inherits, the escalation they do later finds it already there and never asks.
It stays quiet unless it has something to ask: nothing happens when no planned
step needs root, when a credential is already cached (or the rule is NOPASSWD),
when there is no terminal to type into, when the box has no escalation helper,
or when already root. `needsRoot: { except: ["darwin"] }` on tailscale is why
macOS is spared — there the script delegates to the App Store, which does its
own authorisation, so a password prompt would be for a step that never
escalates.
A declined or failed prompt is not fatal anywhere it is called. The installer
prompts for itself if it turns out to need to, which is exactly the old
behaviour — being unable to ask early is a missed convenience, not a reason to
refuse to install.
Wired into all three paths that hand off to an installer: `moshcode install`,
the pit's `/install`, and `moshcode update`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ThreatCrush Security Scan97 finding(s) HIGH/CRITICAL: 5 | MEDIUM: 41 | LOW: 51
…and 47 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
Merged
This was referenced Aug 14, 2026
Merged
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
tailscale's official installer goes through the distro package manager, so it escalates on its own — after it has already spent time refreshing package lists. Tolerable when it is the only thing running. Insidemoshcode updateit is not, because that walks a plan: moshcode itself, then every installed engine, then every tool.On a box with tailscale installed, that plan looks like this (real output from
planUpgrade):Step 17 of 18, after a self-update and fifteen downloads. The operator watches the first minute, looks away, and comes back to a run parked on a password prompt — or to the escalation helper's own timeout having failed the step. The work is unattended by design; the one interactive moment in it should not be buried where nobody is looking.
The fix
Ask before starting.
primeEscalation()warms the credential up front, and because the helper caches against the terminal that every installer inherits, the escalation those installers do later finds it already there and never asks.Wired into all three paths that hand off to an installer:
moshcode install <target>bin/moshcode.mjs/install <target>src/tui.mjsmoshcode update/upgradesrc/upgrade.mjs, once before the loopIt stays quiet unless it has something to ask
A prompt nobody needed is its own bug, so priming is skipped entirely when:
moshcode updatenever asksNOPASSWD(silent, not "asking for your password" followed by no prompt)sudo/doasPlatform-aware, because macOS does not escalate
needsRoot: { except: ["darwin"] }on tailscale. There its script delegates to the App Store, which does its own authorisation — so a blanket flag would ask every mac user for a password on behalf of a step that never escalates.needsRootHere()also acceptstrue(always) and an explicit platform list.Failure is never fatal
A declined prompt, a wrong password, an operator not in sudoers, a failed spawn — all return
{ primed: false, reason }and the run carries on. The installer prompts for itself if it turns out to need to, which is exactly the old behaviour. Being unable to ask early is a missed convenience, not a reason to refuse to install.Verification
node --test— 1735 pass, 0 fail (was 1717; +18 new).test/escalate-prime.test.mjs(13): the-vvalidate-only flag,doasgetting a real command since it has no-v, silence when cached, root, no-tty, no-escalator,MOSHCODE_ESCALATOR, declined, spawn failure, and the platform rules.test/upgrade-prime-sudo.test.mjs(5): the ordering contract, asserted on one interleaved event log —prime:tailscalemust be event 0, before anyrunCmd. Plus: a clean plan never asks, one prompt covers several privileged items, and a declined prompt does not cancel the run.Every input is injected, because none of these cases — no tty, no sudo, a cancelled prompt, being root — can be produced from inside a test process. The live prompt itself was not exercised: this dev box blocks the escalation helper from the agent's shell, so the stubs are the coverage there.
🤖 Generated with Claude Code