# How the Omarchy Shell Plugin System Integrates with Quickshell: A Technical Deep Dive

> Discover how the Omarchy shell plugin system integrates with Quickshell via a QML registry. Learn about manifest validation and dynamic injection for third-party extensions.

- Repository: [Omacom/omarchy](https://github.com/omacom/omarchy)
- Tags: deep-dive
- Published: 2026-09-11

---

**The Omarchy shell plugin system integrates with Quickshell through a centralized QML registry that discovers third-party extensions, validates their manifests, and dynamically injects them into the compositor via JSON-driven configuration callbacks.**

The Omarchy desktop environment extends Quickshell’s QML-based compositor with a robust plugin architecture that enables dynamic loading of bar widgets, panels, and services. This system bridges static UI definitions with user-controlled extensions through a sophisticated validation and registration mechanism centered in `shell/services/PluginRegistry.qml`.

## Core Architecture: The PluginRegistry

At startup, **Quickshell loads `shell.qml`**, which instantiates the `PluginRegistry` singleton as a top-level property. This registry serves as the authoritative source for all plugin state within the Omarchy shell.

The `PluginRegistry` maintains two critical scan targets:

- **`pluginsDir`** – User-installed extensions located at `~/.config/omarchy/plugins`
- **`firstPartyDir`** – Bundled plugins shipped with the Omarchy distribution

The registry populates an internal map called `installedPlugins` that tracks every discovered extension. When the scan completes, it emits the **`scanFinished`** signal, followed by **`pluginsChanged`** whenever the plugin set mutates. UI components throughout the shell bind to these signals to react to installation, removal, or configuration updates.

## Manifest Validation and Security

Before any plugin enters the runtime, the registry executes **`validateManifest()`** against its [`manifest.json`](https://github.com/omacom/omarchy/blob/main/manifest.json) file. This function enforces a strict schema requiring fields such as `schemaVersion`, `id`, `name`, `version`, `kinds`, and `entryPoints`.

The validation layer distinguishes between **first-party** and **third-party** origins. First-party plugins receive trusted capability stamps automatically, while third-party clones inherit restricted capability sets. This security model prevents unauthorized access to sensitive shell APIs while allowing community extensions to function within sandboxed boundaries.

## Configuration Mediation via shell.json

The registry does not perform direct file I/O. Instead, `shell.qml` injects two callbacks during initialization:

- **`shellConfigProvider`** – Returns the canonical [`shell.json`](https://github.com/omacom/omarchy/blob/main/shell.json) configuration object
- **`shellConfigMutator`** – Receives a mutable copy of the configuration, applies updates (such as enabling a plugin or repositioning a bar widget), and persists changes back to disk

Enabled state derivation depends on plugin type:

- **Bar widgets** require their `kinds` array to contain `"bar"` and their ID must match the selected bar specified in `config.bar.id`
- **Panels, services, and overlays** must appear in the top-level `plugins[]` array and must not be listed in `disabledPlugins[]`

## Runtime Quickshell Integration

The Omarchy shell plugin system binds to Quickshell’s runtime through several mechanisms:

**Quickshell.env Integration** – The registry accesses `Quickshell.env` to resolve the `HOME` environment variable, constructing absolute paths to user plugin directories without hardcoding filesystem assumptions.

**Dynamic Component Instantiation** – Once validated and enabled, plugins expose QML entry points (e.g., `Plugin.qml`) that the shell instantiates as child components of bar containers or overlay layers.

**Signal-Driven UI Updates** – Components such as bar widgets listen to `pluginsChanged` signals to reload their configuration without requiring manual shell restarts.

## Plugin API Surface and Bar Widgets

Third-party extensions interact with the system through read-only APIs to prevent circular dependencies. The **`PluginRegistryApi.qml`** module exposes a limited interface that allows plugins to query their own enabled state and resolve entry-point URLs without holding a reference to the host registry.

For bar widget-specific functionality, **`PluginBarWidgetRegistryApi.qml`** provides specialized methods to query placement sections and update widget-specific properties through the registry’s mutator functions.

```json
// Example: A valid plugin manifest (manifest.json)
{
  "schemaVersion": 1,
  "id": "example.weather",
  "name": "Weather Panel",
  "version": "1.0.0",
  "kinds": ["panel"],
  "entryPoints": {
    "panel": "Plugin.qml"
  }
}

```

```qml
// Example: Enabling a bar widget programmatically
import QtQuick
import Omarchy.PluginRegistry

Component.onCompleted: {
    // Enable widget "example.clock" and anchor to right section
    PluginRegistry.setEnabled("example.clock", true, {section: "right"})
}

```

```qml
// Example: Querying state via the read-only API
import QtQuick
import Omarchy.PluginRegistryApi as Registry

QtObject {
    required property string pluginId: "example.clock"
    property var api: Registry.PluginRegistryApi { pluginId: pluginId }

    Component.onCompleted: {
        if (api.enabled) {
            console.log("Clock widget is active on bar")
        }
    }
}

```

## Summary

- **`shell/services/PluginRegistry.qml`** serves as the central authority for plugin discovery, validation, and lifecycle management within the Omarchy shell.
- **Manifest validation** occurs through `validateManifest()`, enforcing security boundaries between first-party and third-party code.
- **Configuration state** flows through [`shell.json`](https://github.com/omacom/omarchy/blob/main/shell.json), mediated by provider and mutator callbacks wired in `shell.qml`.
- **Quickshell integration** leverages environment variables, QML component instantiation, and signal emissions to keep the UI synchronized with plugin state changes.
- **Read-only APIs** in `PluginRegistryApi.qml` and `PluginBarWidgetRegistryApi.qml` allow safe introspection without exposing internal registry mutations to extension code.

## Frequently Asked Questions

### How does Omarchy validate plugin security?

Omarchy validates plugins through the `validateManifest()` function in `shell/services/PluginRegistry.qml`, which ensures every [`manifest.json`](https://github.com/omacom/omarchy/blob/main/manifest.json) contains required fields like `schemaVersion`, `id`, and `entryPoints`. The system assigns capability stamps based on origin, granting first-party plugins broader access while restricting third-party extensions to sandboxed APIs.

### What is the difference between first-party and third-party plugins in Omarchy?

First-party plugins reside in the bundled `firstPartyDir` and receive automatic capability stamps indicating trusted status. Third-party plugins installed in `~/.config/omarchy/plugins` undergo the same validation but inherit restricted capability sets that limit their access to sensitive shell internals unless explicitly granted.

### How do bar widgets register themselves with the Quickshell compositor?

Bar widgets register by specifying `"bar"` in their manifest’s `kinds` array and matching the `config.bar.id` specified in [`shell.json`](https://github.com/omacom/omarchy/blob/main/shell.json). The `PluginBarWidgetRegistryApi.qml` exposes methods to query placement and update properties, while the shell instantiates the widget’s QML entry point as a child of the bar container when enabled.

### Can plugins modify the shell configuration dynamically?

Yes, plugins can request configuration changes through the `shellConfigMutator` callback provided to the registry by `shell.qml`. Rather than writing directly to disk, plugins invoke registry methods that create mutable copies of [`shell.json`](https://github.com/omacom/omarchy/blob/main/shell.json), apply updates (such as toggling enabled state or repositioning widgets), and persist the changes through the sanctioned mutation pipeline.