# How Omarchy Shell Plugins Function: Quickshell Plugin System Explained

> Discover how Omarchy shell plugins function as self-contained UI extensions within the Quickshell process. Learn about dynamic loading and IPC control.

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

---

**Omarchy shell plugins are self-contained UI extensions that run inside a single long-running Quickshell process, discovered via [`manifest.json`](https://github.com/basecamp/omarchy/blob/main/manifest.json) files, loaded dynamically at runtime, and controlled through IPC commands.**

The Omarchy desktop environment manages all visual elements—status bars, widgets, panels, overlays, and background services—through a unified plugin architecture. According to the `basecamp/omarchy` source code, this system enables seamless customization without requiring shell restarts or complex compilation steps.

## Plugin Discovery and Validation

The foundation of Omarchy shell plugins lies in **`shell/services/PluginRegistry.qml`**, which orchestrates the entire plugin lifecycle.

### Scanning Locations

At initialization, the `PluginRegistry` invokes a Bash script via its `rescan()` function to scan two distinct directories:

- **First-party plugins**: `shell/plugins` — bundled components shipped with Omarchy
- **Third-party plugins**: `~/.config/omarchy/plugins` — user-installed extensions

For each [`manifest.json`](https://github.com/basecamp/omarchy/blob/main/manifest.json) (or `*.manifest.json`) discovered, the registry performs four validation steps:

1. Parses the JSON structure
2. Validates against schema version 1 with required fields and safe entry-point paths
3. Stamps metadata with `__sourceDir` and `__isFirstParty` flags
4. Merges manifests into `installedPlugins`, reserving `omarchy.*` namespace for first-party plugins

## Manifest Schema and Structure

Every Omarchy shell plugin requires a [`manifest.json`](https://github.com/basecamp/omarchy/blob/main/manifest.json) at its root directory. The schema defines capabilities and entry points for the Quickshell runtime.

| Field | Purpose |
|-------|---------|
| `id` | Unique identifier; `omarchy.` prefix reserved for built-ins |
| `name` | Human-readable display title |
| `version` | Semantic version string |
| `kinds` | Plugin types: `bar-widget`, `bar`, `panel`, `overlay`, `menu`, `service` |
| `entryPoints` | Maps each kind to a relative QML file |
| `keepLoaded` | Optional; retains plugin in memory after dismissal |
| `barWidget` | Optional placement metadata and multiplicity rules |

Entry-point paths **must be relative** and cannot contain `..` or traverse outside the plugin directory—enforced by path validation in `PluginRegistry.qml`.

## Enabling and Disabling Omarchy Shell Plugins

Plugin activation is controlled through `~/.config/omarchy/shell.json`, interpreted by `PluginRegistry.setEnabled(id, true|false, placement?)`.

### Configuration Patterns

- **Bar widgets**: Enabled by inclusion in `bar.layout.<section>` arrays
- **Bar replacements**: Selected via `bar.id` field
- **General plugins**: Listed in top-level `plugins[]` array

First-party non-bar plugins are implicitly enabled unless explicitly listed in `disabledPlugins[]`. The registry handles special logic for widget placement, ensuring cloned plugins correctly supersede their sources.

## Runtime IPC Communication

External tools interact with running Omarchy shell plugins through an IPC layer that wraps `PluginRegistry` methods for atomic configuration changes.

### Core IPC Methods

| Command | Function |
|---------|----------|
| `listPlugins` | Returns JSON array of all discovered plugins |
| `enablePlugin <id> <placement>` | Activates plugin with optional bar placement |
| `putBarWidget <id> <placement>` | Adds widget if absent |
| `moveBarWidget <id> <placement>` | Relocates existing widget |
| `call <id> <method> <arg>` | Invokes method on loaded plugin |

These commands enable scripting, automation, and integration with external tools without shell restarts.

## Hot-Reload and Plugin Cloning

Omarchy shell plugins support development workflows through automatic reloading and safe customization via cloning.

### File System Monitoring

The `localPluginWatcher` in `PluginRegistry.qml` uses `inotifywait` to detect source modifications. Changes emit `localPluginChanged(pluginId)`, triggering hot-reload while preserving state for `keepLoaded` plugins.

### Cloning Mechanism

Cloning creates a personal copy of first-party plugins under `~/.config/omarchy/plugins/<username>.<id>/` with these characteristics:

- **Preserved identity**: Clone retains original IPC identity—existing shortcuts remain functional
- **Automatic mutual exclusion**: Enabling clone disables source plugin via `disabledPlugins[]`; conversely, enabling source removes clone from disabled list

This design allows users to modify built-in behavior without losing upstream compatibility.

## Entry-Point Loading and Injected Properties

When summoned via `omarchy-shell shell summon <id>`, the registry resolves the entry-point URL through `entryPointUrl(manifest, kind)`. The resulting `file://` path loads as a QML `Item` with injected context:

- `omarchyPath` — Absolute filesystem path to plugin directory
- `shell` — Reference to running shell instance
- `manifest` — Parsed manifest object
- `pluginRegistry` / `barWidgetRegistry` — Runtime registries for dynamic queries

These properties enable plugins to query system state, emit IPC calls, and self-configure.

## Practical Examples

### Minimal Bar Widget Manifest

```json
{
  "schemaVersion": 1,
  "id": "myorg.cool-clock",
  "name": "Cool Clock",
  "version": "1.0.0",
  "kinds": ["bar-widget"],
  "entryPoints": { "barWidget": "Widget.qml" },
  "barWidget": {
    "defaultSection": "center",
    "allowMultiple": false
  }
}

```

See `shell/services/PluginRegistry.qml` for complete schema validation rules.

### CLI Plugin Activation

```bash

# Enable plugin with left-side bar placement

omarchy enablePlugin myorg.cool-clock '{"section":"left"}'

```

This internally invokes `PluginRegistry.setEnabled("myorg.cool-clock", true, {"section":"left"})`.

### Panel Summoning with Payload

```bash

# Load weather panel and pass location data

omarchy-shell shell summon community.weather '{"location":"Seattle"}'

```

The `summon` method loads the plugin if needed and calls its `open(payloadJson)` entry point.

### Basic QML Widget Implementation

```qml
// Widget.qml
Item {
    width: 80; height: 30
    Text {
        anchors.centerIn: parent
        text: "12:34"
        font.pixelSize: Style.font.title
        color: Color.foreground
    }
}

```

Automatically receives `shell`, `pluginRegistry`, and other context properties on load.

## Architecture Files

| Path | Responsibility |
|------|----------------|
| `shell/services/PluginRegistry.qml` | Discovery, validation, lifecycle management, hot-reload |
| [`docs/omarchy-shell.md`](https://github.com/basecamp/omarchy/blob/main/docs/omarchy-shell.md) | Manifest format, IPC reference, configuration schema |
| `shell/shell.qml` | Shell instantiation, IPC target exposure, config wiring |
| `shell/plugins/*/*/manifest.json` | Reference first-party plugin implementations |
| [`test/shell.d/plugins-test.sh`](https://github.com/basecamp/omarchy/blob/main/test/shell.d/plugins-test.sh) | Manifest parsing and safety validation tests |

## Summary

- **Omarchy shell plugins** are Quickshell QML components discovered through [`manifest.json`](https://github.com/basecamp/omarchy/blob/main/manifest.json) scanning in `shell/plugins` and `~/.config/omarchy/plugins`
- **`shell/services/PluginRegistry.qml`** validates manifests, manages enabled state, and orchestrates loading
- **IPC commands** (`enablePlugin`, `summon`, `call`) control runtime behavior without shell restarts
- **Hot-reload** via `inotifywait` and **cloning** with preserved identity enable safe customization
- Injected properties (`shell`, `omarchyPath`, `pluginRegistry`) provide plugin context

## Frequently Asked Questions

### What file format defines an Omarchy shell plugin?

A [`manifest.json`](https://github.com/basecamp/omarchy/blob/main/manifest.json) file at the plugin root declares metadata including `id`, `version`, `kinds`, and `entryPoints`. The schema requires `schemaVersion: 1` and enforces safe relative paths for entry points.

### Can I modify built-in Omarchy plugins without losing updates?

Yes. Use the **cloning** feature: `omarchy enablePlugin` with a first-party plugin creates a copy under `~/.config/omarchy/plugins/<user>.<id>/`. This preserves the original IPC identity while allowing modifications; the clone automatically supersedes the original when enabled.

### How do Omarchy shell plugins receive data when summoned?

The `summon` IPC method passes a JSON payload to the plugin's `open()` entry point. For bar widgets and persistent services, injected properties like `shell` and `pluginRegistry` enable ongoing runtime interaction.