Every way to talk to LunaDash.
The dev branch exposes one owner-only Unix control socket, a versioned Settings API, the Quickshell shell object and Plugin SDK 2's synchronous C ABI. This page documents the contracts implemented by the current source tree.
This is a local session API, not HTTP. LunaDash binds a QLocalServer with user-only access. The website documents the API; it cannot control a running desktop.
Wire protocol and ludashctl
Processes launched by LunaDash inherit LUNADASH_CONTROL. The client still accepts the historical LUDASH_CONTROL, then searches the runtime directory for one unambiguous *-control socket. Prefer LUNADASH_CONTROL in new code.
lunadashctl status
lunadashctl workspace 1
lunadashctl appearance '{"gap":16,"animations":true}'
lunadashctl quit confirmThe CLI sends one compact UTF-8 JSON object followed by a newline. method and value are strings, so structured payloads are JSON encoded inside value. The compositor accepts at most 65,536 request bytes, applies a three-second server timeout and returns one compact JSON object plus a newline.
{"method":"appearance","value":"{\"gap\":16}"}
Invalid JSON returns {"error":"Invalid request"}. An unsupported method/value returns {"error":"Invalid command or value"}. lunadashctl waits up to 1.5 seconds per socket read and refuses responses larger than 1 MiB.
import json, os, socket
path = os.environ.get("LUNADASH_CONTROL") or os.environ.get("LUDASH_CONTROL")
if not path:
raise RuntimeError("LUNADASH_CONTROL is not set")
request = {"method": "appearance", "value": json.dumps({"gap": 16})}
with socket.socket(socket.AF_UNIX) as client:
client.settimeout(3)
client.connect(path)
client.sendall(json.dumps(request).encode() + b"\n")
data = b""
while b"\n" not in data:
chunk = client.recv(65536)
if not chunk or len(data) + len(chunk) > 1024 * 1024:
raise RuntimeError("Invalid LunaDash response")
data += chunk
response = json.loads(data.split(b"\n", 1)[0])
if "error" in response:
raise RuntimeError(response["error"])
print(json.dumps(response, indent=2))Status is the discovery API
Use status to discover client ids, display modes, connection names, plugin ids, available power/session actions and layout actions instead of hard-coding host-specific values.
| Field | Type | Purpose |
|---|---|---|
| clients | array | Window ids, metadata, geometry, workspace, focus/mapping and minimize/maximize/fullscreen state. |
| workspace / tiling / windowLayout | number / objects | Current workspace, layout snapshot, template id, overlap policy, settings schema/values and actions. |
| appearance | object | Effective desktop preferences. |
| extensions | object | Installed plugins, remote Store catalogue, target registry, extensions document/path and Store state. |
| settingsApi | object | Settings API version and editable target descriptors; same payload as settings-describe. |
| shellModules | object | Module document/descriptors/path, custom-code trust and runtime errors. |
| audio / network / power | objects | Service availability, devices/profiles, busy state and errors. |
| display / brightness / ddcBrightness | objects | Output modes/scale/pending confirmation, backlight state and DDC/CI monitors. |
| input / shortcuts | objects | Keyboard/backend/protocol state and effective shortcut map. |
| screenCapture | object | zwlr_screencopy protocol/version, last path, busy phase and error. |
| xwayland | object | XWayland compatibility and utility-surface state. |
| system / update / sessionActions / settingsTools | objects | System state, updater, logind actions and validated settings helpers. |
| language / translations | string / object | Locale and shell translation dictionary. |
| settingsSerial / pickerSerial / launcherSerial | numbers | Interaction serials used by the shell to open UI surfaces. |
| graphicsApi / shaderReady / graphicsFailed | string / booleans | Renderer-health summary for the active wlroots path. |
Each ordinary status.clients[] entry includes a positive session-local id, title, app id, icon, desktop id, X11 flag, workspace, geometry, focus/minimize/maximize/fullscreen/mapped flags and buffer/content sizes. Do not persist these ids across compositor restarts.
Every control method
The tables are aligned with WaylandCompositor::control() on the dev branch. Examples use the CLI form; direct socket clients send the same method/value pair.
State and settings
| Method | Value | Behavior | Example |
|---|---|---|---|
| status | Empty | Return the complete compositor snapshot: windows, workspaces, appearance, displays, input, audio, network, power, plugins, modules, capture state and Settings API descriptors. | lunadashctl status |
| settings-describe | Empty | Return Settings API v1 and every editable target. Each descriptor contains id, schema, values and a SHA-256 revision token. | lunadashctl settings-describe |
| settings-update | JSON {"target":"...","revision":"...","changes":{...}} | Patch one Settings API target. The revision must still match; unknown fields, stale revisions, read-only changes and schema-invalid values are rejected. The request is capped at 24 KiB. | lunadashctl settings-update '{"target":"builtin:window-animation","revision":"<revision>","changes":{"duration":260}}' |
| appearance | Partial desktop-preferences JSON | Validate and atomically apply a desktop preference patch; invalid or unknown fields reject the update. | lunadashctl appearance '{"gap":16,"animations":true}' |
| reset-preferences | Empty | Remove the desktop preference group, then reapply keyboard configuration and layout. Plugin/module/default-app documents are separate. | lunadashctl reset-preferences |
| open-settings | Empty or a built-in page id | Open Settings at general, appearance, windows, shortcuts, display, input, input-method, sound, network, bluetooth, power, applications, privacy, system, devices, about, modules, plugins or dashboard. | lunadashctl open-settings plugins |
Windows and workspaces
| Method | Value | Behavior | Example |
|---|---|---|---|
| workspace | Zero-based workspace index | Switch to an existing workspace. The index must be within appearance.workspaceCount. | lunadashctl workspace 1 |
| focus | Positive client id | Switch to the client's workspace, restore it if minimized, focus it in the layout and give the surface keyboard focus. | lunadashctl focus 12 |
| activate-window | Positive client id | Activate a task/window through the compositor task activation path. | lunadashctl activate-window 12 |
| minimize | Positive client id | Minimize the client, update XWayland state when applicable, update the layout and synchronize focus. | lunadashctl minimize 12 |
| close | Positive client id | Request a normal application close; this is not a process kill. | lunadashctl close 12 |
| group-window | JSON {"window":ID,"target":ID} | Ask the active layout to group one window with another. The layout must support group-with. | lunadashctl group-window '{"window":12,"target":7}' |
| expel-window | Positive client id | Ask the active layout to expel a window from its current group. | lunadashctl expel-window 12 |
| window-layout-settings | JSON matching status.windowLayout.schema | Patch the active layout template settings and rearrange immediately. | lunadashctl window-layout-settings '{"gap":12}' |
| window-layout-action | JSON {"action":"<id>","payload":{...}} | Invoke an action advertised by status.windowLayout.actions. Only action and payload are accepted; required work-area data is injected by the compositor. | lunadashctl window-layout-action '{"action":"expel","payload":{"window":12}}' |
| switch-window | Positive client id | Select a particular item in the active compositor window switcher. | lunadashctl switch-window 12 |
| switch-step | previous or next | Step the switcher backward for previous, otherwise forward. | lunadashctl switch-step next |
| switch-accept | Empty | Accept the current window-switch selection. | lunadashctl switch-accept |
| switch-cancel | Empty | Cancel the current window-switch operation. | lunadashctl switch-cancel |
| launcher-visible | true or false | Synchronize launcher visibility with the compositor; primarily used by the LunaDash shell. | lunadashctl launcher-visible true |
Input and shortcuts
| Method | Value | Behavior | Example |
|---|---|---|---|
| shortcut-capture | true or false | Enable/disable shortcut-capture mode while Settings records a key combination. | lunadashctl shortcut-capture true |
| shortcuts | Partial JSON mapping known action ids to shortcut strings | Update bindings. Values are Disabled or a Meta/Alt key combination; duplicates and Alt+Tab / Alt+Shift+Tab are rejected. | lunadashctl shortcuts '{"launchTerminal":"Meta+Return","closeWindow":"Meta+Q"}' |
| reset-shortcuts | Empty | Restore the built-in shortcut map, including workspace1..10 and moveToWorkspace1..10. | lunadashctl reset-shortcuts |
| send-key | copy, paste, cut or selectAll | Inject the corresponding Ctrl chord into the currently focused wlroots keyboard surface. | lunadashctl send-key copy |
Applications and URLs
| Method | Value | Behavior | Example |
|---|---|---|---|
| default-apps | JSON object of command arrays | Save default terminal/files/browser command arrays. Empty terminal/files commands can use LunaDash fallbacks. | lunadashctl default-apps '{"browser":["google-chrome-stable"]}' |
| launch-default | terminal, files or browser | Launch the configured default. Terminal/files can fall back to LunaDash built-ins; browser requires an available browser command. | lunadashctl launch-default browser |
| launch-command | Non-empty JSON command array | Launch a program directly without a shell. Every argument is a string up to 1024 characters. | lunadashctl launch-command '["foot","-e","htop"]' |
| launch-with-x11 | JSON command array; CLI also accepts -- program args | Launch through the external-command path with authenticated X11 compatibility prepared. | lunadashctl launch-with-x11 -- xterm -e htop |
| launch-x11 | Command-line string parsed by QProcess::splitCommand | Launch directly through XWayland. This parses arguments; it does not evaluate shell operators. | lunadashctl launch-x11 'xterm -e htop' |
| launch-application | JSON exactly {"desktopId":"...","command":[...]} | Launch a desktop-app command after capability resolution. desktopId <=256 chars; command has 1–64 non-empty strings, each <=1024 chars. | lunadashctl launch-application '{"desktopId":"org.example.App","command":["example-app"]}' |
| open-url | URL string | Resolve the configured browser command and open the URL. | lunadashctl open-url https://github.com/LuYishan-4/LunaDash |
| system-tool | Id from status.settingsTools | Open one validated system-settings helper; discover ids through status rather than guessing executables. | lunadashctl system-tool '<tool-id>' |
Plugins
| Method | Value | Behavior | Example |
|---|---|---|---|
| extension-save | Complete extensions schemaVersion 1 JSON | Validate and atomically save built-in/plugin configuration, refresh PluginManager and rearrange. Maximum size: 24 KiB. | lunadashctl extension-save '{"schemaVersion":1,"builtins":{},"plugins":{}}' |
| extension-install | Plugin id from status.extensions.remote | Start an install from the reviewed Store catalogue. The immediate response is pending; progress/error lives in extensions.remote. | lunadashctl extension-install org.example.plugin |
| extension-remove | User-installed plugin id | Disable and remove only a plugin under the canonical user LunaDash plugin root. System plugins cannot be removed here. | lunadashctl extension-remove org.example.plugin |
| extension-error | JSON {"id":"package@target","error":"message"} | Report a plugin runtime failure; messages are capped at 1024 chars. Send an empty error to clear quarantine and retry/refresh. | lunadashctl extension-error '{"id":"org.example.panel@panel","error":""}' |
Shell modules
| Method | Value | Behavior | Example |
|---|---|---|---|
| module-validate | Complete shell-modules JSON | Validate the versioned module document without writing it. | lunadashctl module-validate "$(cat shell-modules.json)" |
| module-save | Complete shell-modules JSON | Validate, atomically save and apply the module document. | lunadashctl module-save '<json>' |
| module-template | Template id | Install a supported starter module file without overwriting an existing user file. | lunadashctl module-template panel |
| module-code-trust | true or false | Opt in/out of executing user custom QML; this code is trusted user code, not sandboxed. | lunadashctl module-code-trust true |
| module-reset | Empty | Restore built-in module settings and disable custom-code trust while preserving user QML files. | lunadashctl module-reset |
| module-error | JSON {"id":"module-id","error":"message"} | Report or clear a custom-module runtime error so recovery content remains available. | lunadashctl module-error '{"id":"panel","error":"load failed"}' |
Audio, network and power
| Method | Value | Behavior | Example |
|---|---|---|---|
| audio | JSON with exactly device plus id, volume or mute | device is output/input. Select output with id, set integer volume 0..100, or set mute boolean. Requires wpctl/WirePlumber and no pending audio change. | lunadashctl audio '{"device":"output","volume":50}' |
| network | JSON with "action" and action-specific fields | Structured NetworkManager/Bluetooth operations; accepted actions are documented below. Read status.network for current names/devices first. | lunadashctl network '{"action":"wifi-connect","ssid":"My WiFi","password":"secret"}' |
| power-profile | Profile from status.power.profiles | Switch with powerprofilesctl to an advertised profile such as power-saver, balanced or performance. | lunadashctl power-profile balanced |
| session-action | suspend, reboot or poweroff | Call systemd-logind only when status.sessionActions reports the action available. | lunadashctl session-action suspend |
Displays
| Method | Value | Behavior | Example |
|---|---|---|---|
| brightness | Integer 1..100 | Set the active backlight through brightnessctl; status.brightness must report an available device. | lunadashctl brightness 75 |
| ddc-brightness | JSON {"id":"<device-id>","percent":0..100} | Set and verify external-monitor VCP 0x10 brightness through ddcutil. | lunadashctl ddc-brightness '{"id":"i2c-6:DELL U2720Q","percent":60}' |
| ddc-refresh | Empty | Rescan DDC/CI displays and query current brightness. | lunadashctl ddc-refresh |
| display-configure | JSON containing mode and/or scale | Test+commit a primary-display change. mode must be advertised; scale is 1.0..3.0. Success starts a 15-second confirmation timer. | lunadashctl display-configure '{"scale":1.25}' |
| display-confirm | Empty | Persist the pending display width/height/refresh/scale before the 15-second revert timer expires. | lunadashctl display-confirm |
| display-revert | Empty | Immediately restore the display state saved before the pending change. | lunadashctl display-revert |
| desktop-size | Nested output size | Resize a supported windowed nested session; physical/fullscreen sessions reject unsupported resizing. | lunadashctl desktop-size 1440x900 |
Capture and wallpaper
| Method | Value | Behavior | Example |
|---|---|---|---|
| capture | Absolute new output path | Start an asynchronous screenshot save. Relative paths and existing files are rejected; response includes path/pending/phase. | lunadashctl capture /tmp/lunadash-shot.png |
| screenshot | Empty | Start the normal LunaDash screenshot flow; read status.screenCapture for completion/error. | lunadashctl screenshot |
| wallpaper | 0 or 1 | Select a built-in shader wallpaper palette and set wallpaperMode to shader. | lunadashctl wallpaper 1 |
| choose-wallpaper | Empty | Open Settings → Appearance and signal the image picker. | lunadashctl choose-wallpaper |
| wallpaper-image | Absolute local path or file URL | Validate and select an image wallpaper. | lunadashctl wallpaper-image /home/user/Pictures/wallpaper.png |
| wallpaper-default | Empty | Clear the custom image wallpaper and restore the default image state. | lunadashctl wallpaper-default |
Language, setup and maintenance
| Method | Value | Behavior | Example |
|---|---|---|---|
| language | Supported locale id | Save a supported LunaDash UI locale. | lunadashctl language zh_TW |
| configure-network | Empty | Open nm-connection-editor, otherwise run nmtui in a supported terminal. | lunadashctl configure-network |
| check-update | Empty | Ask the update checker to refresh; read status.update for the result. | lunadashctl check-update |
| setup | Empty | Mark first-run setup incomplete and reopen the welcome flow. | lunadashctl setup |
| finish-setup | Empty | Record first-run setup completion. | lunadashctl finish-setup |
| quit | Literal "confirm" | Request normal LunaDash session shutdown. Empty is rejected. | lunadashctl quit confirm |
Network action payloads
network accepts structured operations, never arbitrary nmcli text. Actions are bluetooth-scan, network-reset, connection-reconnect, wifi-scan, wifi-connect, connection-up/down/delete, device-connect/disconnect, networking-enable, wifi-enable, connection-autoconnect, connection-ipv4-auto/manual, connection-ipv6-auto/disabled and connection-route-metric.
lunadashctl network '{"action":"wifi-scan"}'
lunadashctl network '{"action":"wifi-connect","ssid":"Lab WiFi","password":"..."}'
lunadashctl network '{"action":"networking-enable","enabled":true}'
lunadashctl network '{"action":"connection-ipv4-manual","name":"Lab","address":"192.0.2.20/24","gateway":"192.0.2.1","dns":"1.1.1.1"}'Shortcut ids
Known actions are focusLeft/Right/Up/Down, groupLeft/Right, reorderLeft/Right, expelWindow, centerColumn, widenColumn, narrowColumn, maximizeWindow, closeWindow, minimizeWindow, launchTerminal, launchFiles, launchLauncher, screenshot, plus workspace1..10 and moveToWorkspace1..10. Values are Disabled or one key with modifiers; at least Meta or Alt is required.
Settings API version 1
settings-describe returns descriptors with id, name, type, category, normalized schema, current values and a SHA-256 revision. The revision hashes schema plus values for optimistic concurrency.
builtin:<extension-target>
plugin:<package-id>:<extension-target>
module:<module-id>:<section>
layout:<active-window-template># Read descriptors
lunadashctl settings-describe
# Send the selected target's exact revision with a partial change
lunadashctl settings-update '{"target":"plugin:org.example.motion:window-animation","revision":"<64-hex-sha256>","changes":{"duration":280}}'If another editor changed the target, the patch fails with Settings changed. Reload before applying this edit.. Schemas can describe boolean, string, integer, number and bounded enum-array fields. Plugin schemas intentionally allow only toggle, select, number and slider controls.
QML shell API
SDK 2 Quickshell plugins receive shell, validated settings and target-specific context before construction. Treat shell.state as read-only and perform mutations through control methods.
| Member | Type | Use |
|---|---|---|
| shell.state | var | Latest status snapshot; treat it as read-only and mutate through commands. |
| shell.command(method, value) | function | Queue a ludashctl operation. JSON-valued methods must receive JSON.stringify(...). |
| shell.commandCompleted(method, result) | signal | Emitted after a queued command returns with parsed JSON. |
| shell.launch(id) | function | Launch terminal/files/browser defaults, open Settings/Plugins, or a built-in LunaDash desktop app. |
| shell.openUrl(url) | function | Open a URL through the configured browser. |
| shell.setAppearance(changes) | function | Convenience wrapper around the appearance control method. |
| shell.openSettingsPage(page) | function | Open the shell Settings surface at a page id. |
| shell.settingsOpen | bool | Current Settings visibility. |
| shell.focusedTitle | string | Focused client title, or LunaDash when none is focused. |
| shell.tr(source) | function | Translate with state.translations, falling back to source. |
| shell.notify(title, body, kind, details) | function | Show shell feedback while respecting notification preferences. |
| shell.configureNetwork() | function | Open the host network editor while preserving first-run behavior. |
import QtQuick
Item {
required property var shell
required property var settings
required property var context
Text { text: shell.tr("Focused") + ": " + shell.focusedTitle }
TapHandler { onTapped: shell.command("workspace", "1") }
Connections {
target: shell
function onCommandCompleted(method, result) {
if (method === "workspace" && result.error)
context.reportError(result.error)
}
}
}Every visual context adds target, built-in source, builtinSettings, effective mode and reportError(message). A plugin may expose pluginReady: false; a replacement does not hide the built-in until the loader is ready and pluginReady is true.
Native Plugin SDK 2 ABI
Native effects do not receive Qt or wlroots pointers. Hooks run synchronously on the compositor thread over UTF-8 JSON; do not block, retain host pointers, run nested event loops or create lifetime-escaping callbacks/background work.
#include <stddef.h>
#include <stdint.h>
#define LUDASH_PLUGIN_API_VERSION 2u
typedef struct ludash_plugin_api {
uint32_t struct_size;
uint32_t api_version;
const char *metadata_json;
int (*process)(const char *request, char *response, size_t capacity);
} ludash_plugin_api;
int ludash_plugin_process(const char *request, char *response, size_t capacity);
const ludash_plugin_api *ludash_plugin_entry_v2(void);
int ludash_plugin_write_json(const char *json, char *response, size_t capacity);The SDK generates ludash_plugin_entry_v2, embeds the built manifest and produces the receipt. The loader checks struct size, API version 2, process pointer and equality between embedded and installed metadata.
{
"target":"window-animation",
"mode":"replace",
"context":{},
"builtin":{"duration":220,"enterOffset":12,"focusOpacity":0.82,"exitScale":0.9,"easing":"outCubic"},
"current":{},
"settings":{"duration":280}
}builtin is always available. Replacement hooks get empty current; augment hooks receive the previous valid result. Return a complete JSON object and its byte length excluding NUL, or a negative value. The response buffer is 262,145 bytes. Invalid/oversized output quarantines the instance and preserves the built-in/previous result.
The native targets are window-animation, shell-animation, window-rules and window-layout. Their contracts plus every visual target are in the Plugin SDK and registration guide.
Trust boundary: Quickshell plugins run with user permissions and native effects run inside the compositor without a sandbox.