How Shell IPC Works Between Omarchy bin/ Scripts and the Quickshell Instance

Omarchy's command-line tools marshal JSON payloads through a Unix-domain socket, delegating execution to the Quickshell daemon via the omarchy-shell helper and quickshell ipc command.

The Omarchy desktop environment uses a decentralized architecture where lightweight shell scripts in bin/ communicate asynchronously with the running Quickshell instance. This shell IPC mechanism relies on a Unix-domain socket transport layer, allowing CLI utilities to trigger QML service actions without blocking the user interface.

The IPC Architecture Overview

Omarchy's IPC stack consists of three distinct layers: the command-line wrapper, the socket transport helper, and the QML service registry. When you execute an omarchy command, the request traverses from Bash through a JSON-encoded socket message to a specific QML service implementation.

The transport uses $XDG_RUNTIME_DIR/quickshell.sock as the communication endpoint. This design keeps the desktop shell responsive while exposing a synchronous-feeling interface to shell scripts and external tools.

Step-by-Step Message Flow

Step 1: CLI Wrapper Builds the JSON Payload

The entry point at bin/omarchy parses sub-commands and constructs a compact JSON object describing the requested operation. For example, an OSD command generates a payload like {cmd: "osd.show", args: {icon: "󱓞", message: "Hello world", duration: 3000}}.

Once serialized, the script invokes the omarchy-shell binary, passing the target service ID and the JSON string as arguments. This abstraction keeps the main CLI logic separate from the underlying transport mechanics.

Step 2: Socket Transmission via omarchy-shell

The bin/omarchy-shell helper executes the quickshell ipc send command, which writes the JSON payload to Quickshell's Unix-domain socket at $XDG_RUNTIME_DIR/quickshell.sock. This low-level transport is handled by the Quickshell CLI tool, ensuring atomic message delivery to the daemon.

The helper blocks until the daemon acknowledges receipt, providing synchronous error handling for the calling script while the actual work happens asynchronously inside the QML runtime.

Step 3: Quickshell Daemon Routing

Upon receiving the socket message, the Quickshell daemon decodes the JSON and routes it through shell/services/PluginRegistry.qml. This registry maintains a map of service IDs to QML component instances, dynamically loading plugins from the shell/plugins/ directory.

The registry extracts the cmd field to determine the target service (e.g., osd, nightlight, media) and invokes the corresponding method on the registered QML object. This hot-reloadable architecture allows adding new IPC endpoints without restarting the desktop shell.

Step 4: Service Execution

The targeted QML service—such as shell/plugins/services/media/Service.qml or shell/plugins/services/nightlight/Service.qml—executes the requested action. Services leverage Quickshell APIs like Quickshell.execDetached to spawn external processes without blocking the UI thread, or Quickshell.Hyprland to manipulate Wayland compositor state.

For resource resolution, services call Quickshell.iconPath to locate themed icons, ensuring consistent visual presentation across CLI-triggered actions.

Step 5: Result Feedback

If the command expects a return value, the service writes a response JSON back to the socket. The omarchy-shell wrapper reads this reply and exits with the appropriate status code, which the original bin/omarchy script forwards to the user. This round-trip maintains the standard Unix exit-code contract while operating over an asynchronous message bus.

Key Implementation Details

Environment Propagation

The QML root at shell/shell.qml injects critical environment variables into the Quickshell runtime using Quickshell.env. This includes HOME, OMARCHY_PATH, and XDG_RUNTIME_DIR, ensuring that child processes spawned via Quickshell.execDetached inherit the correct configuration context to locate resources and execute subsequent omarchy commands.

Service Registration Protocol

Each plugin under shell/plugins/services/*/ declares a unique serviceId in its QML metadata. The PluginRegistry scans the pluginsDir at startup, registering each service under its ID. The CLI uses this namespace to address commands (omarchy <service-id> <action>), creating a discoverable, modular command structure.

Practical Examples

Trigger a transient OSD notification:

omarchy osd show '{"icon":"󱓞","message":"Hello world","duration":3000}'

This executes the full IPC pipeline: bin/omarchy builds the payload, bin/omarchy-shell transmits it via quickshell ipc, the registry routes to the OSD service, and shell/plugins/services/osd/Service.qml renders the notification using Quickshell.execDetached.

Toggle the night-light service:

omarchy nightlight toggle

Here, the target is shell/plugins/services/nightlight/Service.qml, which toggles the Wayland night-light state through the Quickshell.Hyprland API rather than spawning external processes.

Summary

  • Transport Layer: Uses $XDG_RUNTIME_DIR/quickshell.sock via the quickshell ipc command for reliable Unix-domain socket communication.
  • Entry Points: bin/omarchy parses CLI arguments and bin/omarchy-shell handles socket transmission.
  • Routing: shell/services/PluginRegistry.qml dynamically dispatches JSON commands to registered QML services.
  • Execution: Services utilize Quickshell.execDetached and Quickshell.Hyprland to perform actions without blocking the UI.
  • Environment: shell/shell.qml propagates critical env vars via Quickshell.env to ensure resource paths resolve correctly in spawned processes.

Frequently Asked Questions

How does the omarchy CLI find the running Quickshell instance?

The omarchy-shell helper locates the active session through the $XDG_RUNTIME_DIR/quickshell.sock Unix-domain socket. This path is determined by the Quickshell daemon on startup and is predictable within the user's runtime directory, eliminating the need for PID files or D-Bus registration.

Can I call Quickshell services from custom shell scripts?

Yes. Any script can invoke omarchy-shell <service-id> '<json-payload>' to send commands to the desktop. The JSON format follows the {cmd: "...", args: {...}} schema expected by shell/services/PluginRegistry.qml, allowing custom integrations with Omarchy's service layer.

What happens if the Quickshell daemon is not running?

If the socket at $XDG_RUNTIME_DIR/quickshell.sock does not exist, the quickshell ipc command invoked by bin/omarchy-shell will fail with a connection error, and the wrapper exits with a non-zero status. The bin/omarchy script propagates this failure, alerting the user that the desktop environment is inactive.

Where are new IPC services defined?

New services are implemented as QML files in shell/plugins/services/, each declaring a unique serviceId. The shell/services/PluginRegistry.qml automatically discovers and registers these components at runtime, enabling hot-reloading of functionality without restarting the IPC infrastructure.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →