How the Omarchy Shell Plugin System Integrates with Quickshell: A Technical Deep Dive
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/pluginsfirstPartyDir– 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 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 canonicalshell.jsonconfiguration objectshellConfigMutator– 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
kindsarray to contain"bar"and their ID must match the selected bar specified inconfig.bar.id - Panels, services, and overlays must appear in the top-level
plugins[]array and must not be listed indisabledPlugins[]
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.
// 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"
}
}
// 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"})
}
// 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.qmlserves 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, mediated by provider and mutator callbacks wired inshell.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.qmlandPluginBarWidgetRegistryApi.qmlallow 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 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. 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, apply updates (such as toggling enabled state or repositioning widgets), and persist the changes through the sanctioned mutation pipeline.
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 →