# How Quickshell Plugins Are Sandboxed in Omarchy: Filesystem Security Explained

> Discover how Omarchy sandboxes Quickshell plugins with a robust filesystem security model. Learn about the PluginRegistry and path validation techniques.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: internals
- Published: 2026-08-22

---

**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`](https://github.com/basecamp/omarchy/blob/main/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`](https://github.com/basecamp/omarchy/blob/main/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:

```json
{
  "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:

```json
{
  "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:

```qml
// 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`](https://github.com/basecamp/omarchy/blob/main/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`](https://github.com/basecamp/omarchy/blob/main/shell/plugins/README.md) for first-party examples and [`manual/32-shell-plugins.md`](https://github.com/basecamp/omarchy/blob/main/manual/32-shell-plugins.md) for installation security guarantees.

## Summary

- Omarchy implements **filesystem-based sandboxing** rather than process isolation for Quickshell plugins.
- The `PluginRegistry.qml` service enforces security through `isSafeEntryPoint()`, `validateManifest()`, and `entryPointUrl()` functions.
- All entry points in [`manifest.json`](https://github.com/basecamp/omarchy/blob/main/manifest.json) must 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`](https://github.com/basecamp/omarchy/blob/main/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`](https://github.com/basecamp/omarchy/blob/main/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.