Skip to the content.

Privilege Separation

Prism uses a two-phase install model to minimize the time spent running with elevated privileges. Normal operations (file copies, git config, directory creation) run first as the current user. Privileged operations (system package installs, global tool setup) run second, only after explicit user approval.


Two-Phase Install

Phase 1: Normal Operations (no sudo)

Phase 2: Privileged Operations (requires sudo)

The UI presents phase 2 steps for review before prompting for a password. The user sees exactly what will run with sudo and can approve or skip.


InstallationEngine — Sudo Session Management

The InstallationEngine owns sudo session lifecycle. It receives accessors via constructor injection for any I/O:

Session Creation

engine = InstallationEngine(sudo_accessor=accessor, ...)
session = engine.create_sudo_session()
# session.token = <32-byte cryptographically random URL-safe string>
# session.ttl_seconds = 900  (15 minutes)
# session.max_attempts = 3

Session Validation

is_valid = engine.validate_sudo_session(session)
# Returns False if:
#   - Session has expired (created_at + ttl_seconds < now)
#   - Session is locked (locked_until > now)

Attempt Tracking

# On successful password validation
session = engine.record_sudo_attempt(session, success=True)
# Resets attempt counter to 0

# On failed password validation
session = engine.record_sudo_attempt(session, success=False)
# Increments attempt counter
# After 3 failures: locks session for 30 seconds

SudoAccessor (I/O)

Handles the actual sudo validation via subprocess:

Password Validation

accessor = SudoAccessor()
is_valid = accessor.validate_password("user_password")

The password is passed via stdin to sudo -S -v:

The password never appears in:

Availability Check

if accessor.is_sudo_available():
    # sudo binary exists on this system

On systems without sudo (some containers, Windows without WSL), the privileged phase is skipped entirely.


Session Model

@dataclass
class SudoSession:
    token: str                         # Cryptographically random, memory-only
    created_at: datetime               # Session start time
    ttl_seconds: int = 900             # 15-minute default
    attempts: int = 0                  # Failed attempt counter
    max_attempts: int = 3              # Lock after this many failures
    locked_until: datetime | None      # None = not locked

Expiry

Sessions expire after ttl_seconds (default 15 minutes). After expiry, any operation requiring sudo will prompt for the password again. The TTL is intentionally short to limit the window of elevated access.

Lockout

After 3 consecutive failed password attempts, the session is locked for 30 seconds. During lockout:


Security Properties

Property Implementation
Password never in args sudo -S -v reads from stdin
Password never logged Accessor does not log the password string
Password never stored Passed directly to subprocess, not saved to disk or memory beyond the call
Token is memory-only Never written to disk, never sent over network
Token is cryptographic secrets.token_urlsafe(32) — 256 bits of entropy
Session is time-limited 15-minute TTL, checked on every operation
Brute force protection 3-attempt lockout with 30-second backoff

UI Flow

Figure 1: Two-Phase Install Flow

flowchart TB
    START["Install Started"] --> P1["Phase 1: Normal Install"]
    P1 --> WS["Created workspace"]
    P1 --> CF["Copied config files"]
    P1 --> GIT["Configured git"]
    P1 --> REPO["Cloned repositories"]
    REPO --> CHECK{"Privileged steps?"}
    CHECK -->|No| DONE["Installation Complete"]
    CHECK -->|Yes| P2["Phase 2: Privileged Operations"]
    P2 --> REVIEW["Review: docker-ce, kubectl, eslint"]
    REVIEW --> DECIDE{"User decision"}
    DECIDE -->|Approve & Install| SUDO["Enter sudo password"]
    SUDO --> EXEC["Execute privileged steps"]
    EXEC --> DONE
    DECIDE -->|Skip| PARTIAL["Partially Complete"]

    style START fill:#041f41,color:#fff
    style P1 fill:#0053e2,color:#fff
    style P2 fill:#ffc220,color:#000
    style DONE fill:#2a8703,color:#fff
    style PARTIAL fill:#f59e0b,color:#000
    style SUDO fill:#ea1100,color:#fff

If the user clicks Skip, phase 2 is not executed. The installation is marked as partially complete, and the skipped steps are logged so the user can run them manually later.


Platform Behavior

Platform Sudo Available Behavior
macOS Yes Full two-phase install
Linux Yes Full two-phase install
WSL2 Yes Full two-phase install
Windows (native) No (sudo not found) Phase 2 skipped, user advised to install manually
Docker containers Varies Depends on container setup

See Also