⚠️ Note: This project has been archived

gsudo is no longer maintained. It solved a usability problem — an agent with no tty cannot answer a sudo prompt — not a security problem.

What I do now: privileged commands are run by the human in a real terminal, or delegated to a dedicated worker inside an isolated environment with Always-Ask approval. The tool is no longer installed on my daily machine.

If you bookmarked this article: the old link redirects here; the content is kept for historical reference.

In One Sentence

gsudo solves the usability problem of “an agent has no TTY, so sudo cannot prompt”; it is usable on trusted single-user dev machines (where the agent handles no untrusted content, or is isolated from the human), but it is not a security boundary.

Decision Table

ScenarioWhat to do
Not applicable · agent runs as the same user and reads web pages / issues / third-party codeDo not give it a privilege channel; run the command in a human terminal, or delegate to an isolated worker
Applicable · agent is isolated from the human account (separate user / container / VM)gsudo is usable, but the real boundary is the isolation, not the dialog
Applicable · one-off VM destroyed after the taskCheapest safe form; throwaway beats any dialog
Not applicable · multi-tenant / shared hosts, long-running untrusted code, root compromise unacceptableroot-owned allowlist, precise sudoers rules, isolated VM

“Isolated from the human” means a permission boundary, not a session boundary: a separate OS user, a container, or a VM. Different terminals or different agent instances under the same user do not count.

Why It Is Not a Security Boundary

  • Structural fact: the password passes through a component running as your user. Same-user processes can read pipes and environments, and can poison wrappers. Switching to Tkinter, zenity, or pkexec only changes the UI or the authentication entry point; it does not change who owns the password path.
  • Threat model: the main risk is same-user code execution — a prompt-injected agent, a malicious dependency, an editor extension. It does not require full account takeover; conversely, once the account is compromised, gsudo’s marginal contribution is root persistence, not a new entry point.
  • Approval layers: harness allow/deny and the sudo password prompt are two layers, and neither replaces the other. If a human can run sudo in a real terminal, gsudo adds nothing.
  • Tk 9.0: broken rendering is a usability issue (observed under Tk 9.0 + WSLg, where ttk.Button text was invisible); it does not change the security conclusion. Switching to zenity/GTK fixes only the UI.

What I Do Instead

The reason for retiring it is not that the dialog was poorly built. It answered the wrong question: it treated “the agent has no tty” as the core problem and left “who may escalate, and under what constraints” untouched.

Same-user GUI dialogs have two more side effects:

  • A dialog cannot tell “a human is reading” from “a human is clicking on reflex”; the more prompts you approve, the less each one means.
  • Every added confirmation makes people believe there is one more security boundary, which relaxes the pressure to isolate.

Choose by environment, highest priority first:

NeedApproachSource of the boundary (cost)
Occasional package installs or config changesAgent prints the command; the human runs sudo in a real terminalHuman tty + human judgement (lowest)
Frequent escalation with automationDedicated privileged worker: separate user / container / VM, approval set to Always-AskIsolation + per-request approval (medium)
One-off experimentationTemporary VM / container, destroyed afterwardsDisposal, no need to trust individual behaviour (medium)
Strong audit requirementsroot-owned allowlist scripts, precise sudoers rulesPolicy files the same user cannot rewrite (high)

One-line rule: first ask whether the privilege can be locked into a box you throw away, not whether a human can click one more confirmation.

Key Evidence

  1. No TTY: tty → not a tty; sudo -n true → sudo: interactive authentication is required. This is the only legitimate reason for gsudo to exist.
  2. Same-user readability of the password path: with ptrace_scope=1, /proc/<pid>/mem was denied, but /proc/<pid>/fd/4 yielded the plaintext password (measured 2026-10-05; not re-run during later review — the original record stands). The mechanism holds, and it is the direct basis for “not a security boundary”.
  3. Fixed password path: the source passes the password via subprocess.run(["sudo", "-S", ...], input=password + "\n"); the sudo-timestamp probe added later only reduces repeated typing and does not change the path.

The pkexec comparison and references remain in the pre-archive verification record and are not repeated here.

Original Design (Archive Record)

The following records the trade-offs made before 2026-10-25. It is kept for traceability, not as a current recommendation.

The Problem

When an AI runs in a terminal, the terminal itself is not launched as root. When the AI needs sudo xxx, sudo asks the terminal for a password — but the AI’s bash/subprocess environment has no interactive tty, so the password cannot be entered and the command simply fails.

That produced three failure modes:

  1. Retry loops — the agent sees “permission denied” and retries forever
  2. Hallucination — the agent treats error output as command results and continues with fabricated data
  3. Giving up — the agent concludes it lacks permission and abandons the task

gsudo’s answer at the time was to move the confirmation from the terminal into a GUI, sidestepping the missing tty. That fixed usability, not security.

Core Design

stdout/stderr separation: stdout carries raw command output (directly parseable by the agent), stderr carries the gsudo banner and status (for humans). gsudo whoami with capture_output=True yields root on stdout — no regex-parsing of wrapper text.

GUI instead of terminal interaction: sudo’s prompt goes to stderr, and the agent’s bash environment has no tty; even with a sudo -S pipe, stdin is awkward to control. So gsudo drew its own Tkinter window that does one thing: show the command and the reason, then take the password.

Cross-platform, but platform-adaptive:

LinuxmacOSWindows
Mechanismsudo -Ssudo -SStart-Process -Verb RunAs
InteractionPassword promptPassword promptConfirm button
System dialogNoneNoneUAC
Password✅✅❌ (UAC only needs a click)

Windows needs no password field — UAC is designed for intent confirmation, not identity verification.

No queueing, no lock: an early version used a global file lock to stop windows from stealing focus. The later judgement was that several agents needing privilege at once should all pop up, and the human decides the order; the lock only left later instances stuck in their terminals.

Key Decisions

Why not pkexec: the dialog is not customizable, so the command preview and the agent-generated reason cannot be shown; it is not cross-platform; environment variables are stripped. sudo -S passes the password over stdin and keeps the environment intact.

Why Tkinter: it ships with Python, so installing Python is enough; PyQt/wxPython need compilation or pip, and added dependencies raise the barrier. The goal was “copy the script and run it”.

Two windows on Windows: after gsudo’s confirmation, its window closes and the UAC dialog appears. UAC runs on the secure desktop (Winlogon Desktop), which ordinary programs cannot inject into or bypass. Accept the limit: keep the gsudo window light (command preview only) and let the system own the final confirmation.

Why v1.0 did not cache the password: v1.0 judged that sudo’s own timestamp mechanism (timestamp_timeout) was enough, so gsudo added no extra cache and re-prompted on every call. A later implementation probed the sudo timestamp first: when the cache is valid it shows a simplified confirmation (one click, no password), and a full password dialog only when it has expired. An intermediate “trust window” (five minutes of auto-approval after a checkbox) was withdrawn for trading intent for a countdown; see the trust-window retrospective.

Implementation Notes

  • PowerShell injection defence: commands are never assembled by string concatenation (a quote in the command would break the syntax); they are passed via EncodedCommand with base64 (UTF-16LE).
  • Enter / Esc: supported in both the password and result windows; Tkinter binds on a widget take precedence over root binds, so <Return> is bound on the password field to avoid clashing with the global binding.
  • Red flash: the status label started out red, so “running…” flashed red too; it was changed to grey while running and red only on error.

Verification Status

Last verified 2026-10-05 on Linux.

Passed: Linux GUI password dialog, Enter trigger, result window persistence; stdout/stderr separation; wrong-password detection and retry; syntax compilation.

Not verified: macOS (no device); Windows UAC (no desktop environment); alwaysOnTop compatibility across window managers; non-GUI fallback; concurrent multi-instance dialogs.