How the Vorssaint-utils Uninstaller Identifies and Removes Leftover Application Files
The Vorssaint-utils uninstaller executes a two-phase removal process that first detaches system daemons and resets macOS TCC permissions via shell commands, then performs identity-based scanning of Library folders using bundle ID verification, path safety checks, and exclusivity validation to identify and trash leftover files.
The Vorssaint-utils repository provides a comprehensive macOS application removal system designed to eliminate all traces of an installed program. The uninstaller in Vorssaint-utils coordinates shell-based system teardown with Swift-based file system analysis to locate and remove orphaned preferences, caches, and container data that standard deletion misses. This approach targets both root-level system components and user-specific Library directories while implementing rigorous safety checks to prevent collateral damage.
Phase One: Detaching System Services and Cleaning Core Components
The uninstallation process begins with Tools/uninstall.sh, a shell script that orchestrates the immediate removal of running processes and system-wide registrations. According to the Vorssaint-utils source code, this script first terminates the application process using pkill -x Vorssaint (lines 16-19), then invokes the bundled binary with the --uninstall flag to deregister auxiliary services.
The Uninstaller.runAndExit() method in Sources/Vorssaint/Support/Uninstaller.swift (lines 14-50) handles the daemon teardown by calling FanControlService.restoreAndUnregisterForRemoval() to unregister the fan-helper daemon, followed by SMAppService.mainApp.unregister() to eliminate the login item. The script subsequently resets macOS privacy permissions using tccutil reset All "$BUNDLE" (line 55), clearing Accessibility and Screen Recording allowances from the TCC database.
Final system cleanup includes removing the app bundle via rm -rf, deleting preference plists, saved state, HTTP storage, ByHost preferences, and a secret keychain entry (lines 59-76). If the application previously modified sleep settings, the script checks pmset -g and restores normal sleep behavior (lines 92-99).
Phase Two: Scanning for Leftover Application Files
Once system components are detached, the uninstaller transitions to file system scanning via AppUninstaller in Sources/Vorssaint/Services/Uninstall/AppUninstaller.swift. The process starts with select(appURL:) (lines 99-121), which validates the target bundle and extracts its identifier using UninstallerSupport.verifiedBundleID.
The scanner builds an identity object through UninstallerSupport.identity (lines 86-99 in UninstallerSupport.swift), which aggregates:
- The primary and legacy bundle IDs
- Normalized display name tokens via
normalizedToken - Team IDs and signed group IDs from code signatures
This identity drives the search through predefined SearchFolder locations including Application Support, Caches, Containers, and Library/Preferences. The collect method (around line 152) generates Leftover objects for files matching specific criteria.
File Matching and Confidence Classification
The UninstallerSupport.leftoverMatch enum (lines 30-35) categorizes discoveries into two confidence levels:
.exact: Files owned by the application's bundle ID or signed group.related: Files matching normalized name tokens but not directly owned
Each Leftover object records the file path, size, category, and confidence level, enabling the UI to present sorted results for user review before deletion.
Safety Mechanisms: Preventing Accidental Deletion
The Vorssaint-utils uninstaller implements multiple validation layers to prevent system damage. The UninstallerSupport.removalPathIsSafe function (lines 102-116 in UninstallerSupport.swift) verifies that candidate paths reside within the expected root directory and contain no symbolic links, mitigating directory traversal attacks.
For shared resources, UninstallerSupport.sharedDataIsExclusive (lines 31-41) checks whether folders contain data used by other installed applications through exclusiveBundleIDs validation. The uninstaller only marks shared folders for removal when they contain exclusively the target application's data, preventing collateral deletion of cross-app resources.
Programmatic Usage Examples
You can trigger the uninstallation process either through the command-line script or directly via the Swift API.
Running the Full Uninstaller from Command Line
# From the repository root
./Tools/uninstall.sh
The script performs the complete teardown sequence, printing progress symbols such as "▸ Quitting…", "UNINSTALL: fan helper daemon unregistered", and finally "✓ Vorssaint fully removed."
Using the Swift Uninstaller Class
import Vorssaint
// Called when the user selects "Advanced → Uninstall" inside the app
Uninstaller.runAndExit() // never returns; exits with EXIT_SUCCESS or EXIT_FAILURE
This method detaches the fan helper, restores sleep if needed, unregisters the login item, and then terminates the process.
Scanning for Leftovers Programmatically
import Vorssaint
let url = URL(fileURLWithPath: "/Applications/Editor.app")
AppUninstaller.shared.select(appURL: url)
// After a short async scan:
DispatchQueue.main.async {
let leftovers = AppUninstaller.shared.items
leftovers.forEach { leftover in
print("\(leftover.category): \(leftover.url.path) – size: \(leftover.size) – confidence: \(leftover.confidence)")
}
}
The items array holds Leftover objects that can be filtered, displayed, or sent to the Trash based on their confidence classification.
Summary
- The uninstaller uses a two-phase approach: shell-based system teardown followed by Swift-based file scanning
Tools/uninstall.shhandles process termination, daemon deregistration, TCC permission resets, and bundle deletionAppUninstaller.select(appURL:)initiates scanning by building an identity from bundle IDs, name tokens, and team IDsUninstallerSupport.removalPathIsSafeprevents symlink attacks by validating path containment and link absenceUninstallerSupport.sharedDataIsExclusiveensures shared folders aren't deleted if other apps depend on them- Leftover files receive confidence classifications (
.exactor.related) before UI presentation and user-confirmed trashing
Frequently Asked Questions
How does the uninstaller prevent deleting files used by other applications?
The UninstallerSupport.sharedDataIsExclusive function validates folders against exclusiveBundleIDs to ensure no other installed applications reference the same data. Additionally, the leftoverMatch logic distinguishes between files owned exclusively by the target bundle (.exact) and those merely related by name (.related), allowing the UI to highlight potential conflicts before removal.
What macOS permissions does the uninstaller reset during removal?
According to Tools/uninstall.sh (line 55), the uninstaller executes tccutil reset All "$BUNDLE" to clear all TCC (Transparency, Consent, and Control) database entries for the application. This specifically removes granted Accessibility and Screen Recording permissions, ensuring no residual privacy allowances remain after deletion.
Can I run the Vorssaint-utils uninstaller from the command line?
Yes. The repository includes Tools/uninstall.sh, which you can execute directly from the repository root using ./Tools/uninstall.sh. This script performs the complete uninstallation sequence including process termination, daemon deregistration, permission resets, and bundle deletion, printing progress indicators such as "▸ Quitting…" and "✓ Vorssaint fully removed" upon completion.
How does the uninstaller identify files that belong to the target application?
The system constructs an identity profile using UninstallerSupport.identity, which combines the verified bundle ID from UninstallerSupport.verifiedBundleID, normalized name tokens, and code-signing team IDs. The scanner then searches known Library folders and matches files where leftoverMatch returns .exact (bundle ID ownership) or .related (name token matches), creating Leftover objects that include file size and confidence metadata.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →