How Quickshell Plugins Are Sandboxed in Omarchy: Filesystem Security Explained
Omarchy sandboxes Quickshell plugins using a filesystem-based security model enforced by the PluginRegistry service, which validates that all entry-point paths in manifest.json remain within the plugin's source directory through three layers of path validation.
Omarchy, the declarative desktop shell built on Quickshell by Basecamp, implements strict security boundaries for third-party extensions. Understanding how Quickshell plugins are sandboxed in Omarchy requires examining the PluginRegistry service, which treats every plugin as a self-contained directory and verifies that QML entry points cannot escape their designated filesystem boundaries.
The Filesystem Sandbox Architecture
Omarchy's security model treats each plugin as an isolated island with no access to external system files. Unlike process-based sandboxing that uses containers or virtual machines, Omarchy employs a purely filesystem-based sandbox that restricts plugin loading to resources residing under the plugin's own directory—either ~/.config/omarchy/plugins/<plugin-id>/ for third-party plugins or the built-in source tree for first-party plugins.
The sandbox logic resides in shell/services/PluginRegistry.qml, where the PluginRegistry singleton enforces security through a three-stage validation pipeline before any QML code executes.
Three-Layer Path Validation in PluginRegistry.qml
The sandboxing mechanism operates through consecutive validation steps that progressively verify path safety.
Layer 1: Rejecting Dangerous Path Syntax
The first defense occurs in the isSafeEntryPoint() function (lines 36-41 of shell/services/PluginRegistry.qml). This utility inspects raw entry-point strings from manifest.json and immediately invalidates any path that:
- Starts with an absolute root
/ - Contains parent directory references
.. - Is empty or null
This preemptive check ensures no plugin can specify absolute paths like /etc/passwd or relative traversal sequences like ../../secret.
Layer 2: Manifest Schema Validation
During the validateManifest() routine (lines 80-88), the registry iterates over the entryPoints object and subjects each value to isSafeEntryPoint(). If any entry point fails this validation, the entire manifest is rejected and the plugin is prevented from loading. This atomic validation ensures partial security breaches cannot occur—either all entry points are safe, or the plugin is discarded entirely.
Layer 3: Resolved Path Verification
Even after syntax validation, the entryPointUrl() function (lines 99-107) performs a final double-check by constructing the absolute file URL and verifying it begins with the plugin's source directory (expectedPrefix). This guards against edge cases where path concatenation might otherwise resolve outside the intended directory, ensuring the resolved string strictly remains within the plugin's containment boundary.
Safe vs. Unsafe Manifest Examples
The following manifest demonstrates a compliant plugin structure:
{
"schemaVersion": 1,
"id": "my.cool.plugin",
"name": "Cool Plugin",
"version": "1.0.0",
"kinds": ["panel"],
"entryPoints": {
"panel": "Panel.qml"
}
}
This passes validation because Panel.qml is a relative path without .. components, and the resolved URL <plugin-dir>/Panel.qml maintains the required directory prefix.
Conversely, this manifest triggers immediate rejection:
{
"schemaVersion": 1,
"id": "evil.plugin",
"name": "Evil Plugin",
"version": "0.1",
"kinds": ["panel"],
"entryPoints": {
"panel": "../../etc/passwd"
}
}
When isSafeEntryPoint() detects the .. traversal sequence, it returns false, causing validateManifest() to log a security warning and discard the plugin before any QML execution occurs.
Programmatic Validation
You can verify manifest safety programmatically using the registry singleton:
// Assume registry is the PluginRegistry singleton
var manifest = {
schemaVersion: 1,
id: "example.plugin",
name: "Example",
version: "0.1",
kinds: ["bar-widget"],
entryPoints: { "bar-widget": "BarWidget.qml" }
};
var result = registry.validateManifest(manifest, "/tmp/example.plugin/manifest.json");
// Returns the manifest object if valid, or null if sandbox violation detected
Source Documentation References
The manifest contract requiring relative paths is documented in shell/README.md (lines 46-52), which specifies that entryPoints must reference QML files within the plugin directory. Additional implementation details appear in shell/plugins/README.md for first-party examples and manual/32-shell-plugins.md for installation security guarantees.
Summary
- Omarchy implements filesystem-based sandboxing rather than process isolation for Quickshell plugins.
- The
PluginRegistry.qmlservice enforces security throughisSafeEntryPoint(),validateManifest(), andentryPointUrl()functions. - All entry points in
manifest.jsonmust use relative paths without..components or absolute prefixes. - Three validation layers ensure resolved URLs remain within the plugin's source directory (
~/.config/omarchy/plugins/<id>/or built-in paths). - Security violations result in immediate manifest rejection before QML code execution.
Frequently Asked Questions
How does Omarchy prevent plugins from accessing system files?
Omarchy's PluginRegistry validates that all entry-point paths in manifest.json are relative and contain no parent directory references (..). Additionally, the entryPointUrl() function verifies that resolved absolute paths begin with the plugin's source directory, preventing access to files like /etc/passwd or other system resources.
What happens if a plugin manifest contains an unsafe entry point?
If validateManifest() detects an unsafe path during the validation loop (lines 80-88 of shell/services/PluginRegistry.qml), it immediately rejects the entire manifest and logs a security warning. The plugin is not loaded, and none of its QML components execute, ensuring no code from the violating plugin runs in the Quickshell process.
Where is the plugin sandboxing logic located in the Omarchy source code?
The core sandboxing implementation resides in shell/services/PluginRegistry.qml, specifically in the isSafeEntryPoint() function (lines 36-41), the validateManifest() method (lines 80-88), and the entryPointUrl() helper (lines 99-107). The manifest schema requirements are documented in shell/README.md.
Can third-party plugins run arbitrary code outside their directory?
No. The filesystem sandbox strictly limits third-party plugins to resources within ~/.config/omarchy/plugins/<plugin-id>/ or equivalent installation directories. Because the PluginRegistry rejects any entry point that resolves outside this prefix, plugins cannot execute arbitrary host files regardless of their QML capabilities.
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 →