LUNADASH / API

Every way to talk to LunaDash.

LunaDash exposes one owner-only Unix control socket, a versioned Settings API and Plugin SDK 2's synchronous C ABI; the shell reaches it through SlintLu. This page documents the contracts implemented by the current source tree.

This is a local session API, not HTTP. LunaDash listens on a Unix socket that only your user can open. 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.

TERMINAL
lunadashctl status
lunadashctl workspace 1
lunadashctl appearance '{"gap":16,"animations":true}'
lunadashctl quit confirm

The 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.

WIRE REQUEST
{"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.

DIRECT SOCKET / PYTHON
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.

FieldTypePurpose
clientsarrayWindow ids, metadata, geometry, workspace, focus/mapping and minimize/maximize/fullscreen state.
workspace / tiling / windowLayoutnumber / objectsCurrent workspace, layout snapshot, template id, overlap policy, settings schema/values and actions.
appearanceobjectEffective desktop preferences.
extensionsobjectInstalled plugins, the target registry, the extensions document and path, and per-plugin errors.
settingsApiobjectSettings API version and editable target descriptors; same payload as settings-describe.
audio / network / powerobjectsService availability, devices/profiles, busy state and errors.
display / brightness / ddcBrightnessobjectsOutput modes/scale/pending confirmation, backlight state and DDC/CI monitors.
input / shortcutsobjectsKeyboard/backend/protocol state and effective shortcut map.
screenCaptureobjectzwlr_screencopy protocol/version, last path, busy phase and error.
xwaylandobjectXWayland compatibility and utility-surface state.
system / update / sessionActions / settingsToolsobjectsSystem state, updater, logind actions and validated settings helpers.
language / translationsstring / objectLocale and shell translation dictionary.
settingsSerial / pickerSerial / launcherSerial / shellActionSerialnumbersSerials SlintLu watches to open shell surfaces when a shortcut asks (settings, Super+W, launcher, control center).
graphicsApi / shaderReady / graphicsFailedstring / booleansRenderer-health summary for the active wlroots path.
workspaceCountnumberThe number of workspaces: the occupied ones plus one empty workspace after them.
tilingModestringfill or scroll: the tiling mode in use.
wallpaperPosterstringA file:// still frame of a video wallpaper; empty for images.
wallpaperLivebooleanWhether mpvpaper is playing the video wallpaper below the shell.

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

MethodValueBehaviorExample
statusEmptyReturn the complete compositor snapshot: windows, workspaces, appearance, displays, input, audio, network, power, plugins, modules, capture state and Settings API descriptors.lunadashctl status
settings-describeEmptyReturn Settings API v1 and every editable target. Each descriptor contains id, schema, values and a SHA-256 revision token.lunadashctl settings-describe
settings-updateJSON {"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}}'
appearancePartial desktop-preferences JSONValidate and atomically apply a desktop preference patch; invalid or unknown fields reject the update.lunadashctl appearance '{"gap":16,"animations":true}'
reset-preferencesEmptyRemove the desktop preference group, then reapply keyboard configuration and layout. Plugin/module/default-app documents are separate.lunadashctl reset-preferences
open-settingsEmpty or a built-in page idOpen 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

MethodValueBehaviorExample
workspaceZero-based workspace indexSwitch to a workspace. Workspaces are dynamic: the occupied ones plus one empty workspace after them (status.workspaceCount).lunadashctl workspace 1
focusPositive client idSwitch to the client's workspace, restore it if minimized, focus it in the layout and give the surface keyboard focus.lunadashctl focus 12
activate-windowPositive client idActivate a task/window through the compositor task activation path.lunadashctl activate-window 12
minimizePositive client idMinimize the client, update XWayland state when applicable, update the layout and synchronize focus.lunadashctl minimize 12
closePositive client idRequest a normal application close; this is not a process kill.lunadashctl close 12
group-windowJSON {"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-windowPositive client idAsk the active layout to expel a window from its current group.lunadashctl expel-window 12
window-layout-settingsJSON matching status.windowLayout.schemaPatch the active layout template settings and rearrange immediately.lunadashctl window-layout-settings '{"gap":12}'
window-layout-actionJSON {"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-windowPositive client idSelect a particular item in the active compositor window switcher.lunadashctl switch-window 12
switch-stepprevious or nextStep the switcher backward for previous, otherwise forward.lunadashctl switch-step next
switch-acceptEmptyAccept the current window-switch selection.lunadashctl switch-accept
switch-cancelEmptyCancel the current window-switch operation.lunadashctl switch-cancel
launcher-visibletrue or falseSynchronize launcher visibility with the compositor; primarily used by the LunaDash shell.lunadashctl launcher-visible true
tiling-modetoggle, fill or scrollSwitch tiling between Fill (the split tree fills the screen) and Normal (niri-like scrollable columns). Same as Super+Shift+Space; stored as the tilingMode preference.lunadashctl tiling-mode toggle
switch-beginwindows or workspacesOpen the window switcher (every window in the taskbar) or the workspace switcher without a keyboard shortcut; switch-step, switch-window and switch-accept/switch-cancel drive it.lunadashctl switch-begin windows

Input and shortcuts

MethodValueBehaviorExample
shortcut-capturetrue or falseEnable/disable shortcut-capture mode while Settings records a key combination.lunadashctl shortcut-capture true
shortcutsPartial JSON mapping known action ids to shortcut stringsUpdate 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-shortcutsEmptyRestore the built-in shortcut map, including workspace1..10 and moveToWorkspace1..10.lunadashctl reset-shortcuts
send-keycopy, paste, cut or selectAllInject the corresponding Ctrl chord into the currently focused wlroots keyboard surface.lunadashctl send-key copy

Applications and URLs

MethodValueBehaviorExample
default-appsJSON object of command arraysSave default terminal/files/browser command arrays. Empty terminal/files commands can use LunaDash fallbacks.lunadashctl default-apps '{"browser":["google-chrome-stable"]}'
launch-defaultterminal, files or browserLaunch the configured default. Terminal/files can fall back to LunaDash built-ins; browser requires an available browser command.lunadashctl launch-default browser
launch-commandNon-empty JSON command arrayLaunch a program directly without a shell. Every argument is a string up to 1024 characters.lunadashctl launch-command '["foot","-e","htop"]'
launch-with-x11JSON command array; CLI also accepts -- program argsLaunch through the external-command path with authenticated X11 compatibility prepared.lunadashctl launch-with-x11 -- xterm -e htop
launch-x11Command-line string parsed by QProcess::splitCommandLaunch directly through XWayland. This parses arguments; it does not evaluate shell operators.lunadashctl launch-x11 'xterm -e htop'
launch-applicationJSON 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-urlURL stringResolve the configured browser command and open the URL.lunadashctl open-url https://github.com/LuYishan-4/LunaDash
system-toolId from status.settingsToolsOpen one validated system-settings helper; discover ids through status rather than guessing executables.lunadashctl system-tool '<tool-id>'

Plugins

MethodValueBehaviorExample
extension-saveComplete extensions schemaVersion 1 JSONValidate and atomically save built-in/plugin configuration, refresh PluginManager and rearrange. Maximum size: 24 KiB.lunadashctl extension-save '{"schemaVersion":1,"builtins":{},"plugins":{}}'
extension-installPlugin id from status.extensions.remoteStart 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-removeUser-installed plugin idDisable 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-errorJSON {"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":""}'

Audio, network and power

MethodValueBehaviorExample
audioJSON with exactly device plus id, volume or mutedevice 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}'
networkJSON with "action" and action-specific fieldsStructured 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-profileProfile from status.power.profilesSwitch with powerprofilesctl to an advertised profile such as power-saver, balanced or performance.lunadashctl power-profile balanced
session-actionsuspend, reboot or poweroffCall systemd-logind only when status.sessionActions reports the action available.lunadashctl session-action suspend

Displays

MethodValueBehaviorExample
brightnessInteger 1..100Set the active backlight through brightnessctl; status.brightness must report an available device.lunadashctl brightness 75
ddc-brightnessJSON {"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-refreshEmptyRescan DDC/CI displays and query current brightness.lunadashctl ddc-refresh
display-configureJSON containing mode and/or scaleTest+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-confirmEmptyPersist the pending display width/height/refresh/scale before the 15-second revert timer expires.lunadashctl display-confirm
display-revertEmptyImmediately restore the display state saved before the pending change.lunadashctl display-revert
desktop-sizeNested output sizeResize a supported windowed nested session; physical/fullscreen sessions reject unsupported resizing.lunadashctl desktop-size 1440x900

Capture and wallpaper

MethodValueBehaviorExample
captureAbsolute new output pathStart an asynchronous screenshot save. Relative paths and existing files are rejected; response includes path/pending/phase.lunadashctl capture /tmp/lunadash-shot.png
screenshotEmptyStart the normal LunaDash screenshot flow; read status.screenCapture for completion/error.lunadashctl screenshot
wallpaper0 or 1Select a built-in shader wallpaper palette and set wallpaperMode to shader.lunadashctl wallpaper 1
choose-wallpaperEmptyOpen Settings → Appearance and signal the image picker.lunadashctl choose-wallpaper
wallpaper-imageAbsolute local path or file URLValidate and select an image wallpaper.lunadashctl wallpaper-image /home/user/Pictures/wallpaper.png
wallpaper-defaultEmptyClear the custom image wallpaper and restore the default image state.lunadashctl wallpaper-default
wallpaper-favoriteadd:<path> or remove:<path>Mark a library wallpaper as a favourite or remove the mark.lunadashctl wallpaper-favorite add:/home/me/Pictures/Wallpapers/sky.png
wallpaper-refreshEmptyRescan the wallpaper library folder without opening the shell's picker.lunadashctl wallpaper-refresh

Language, setup and maintenance

MethodValueBehaviorExample
languageSupported locale idSave a supported LunaDash UI locale.lunadashctl language zh_TW
configure-networkEmptyOpen nm-connection-editor, otherwise run nmtui in a supported terminal.lunadashctl configure-network
check-updateEmptyAsk the update checker to refresh; read status.update for the result.lunadashctl check-update
setupEmptyMark first-run setup incomplete and reopen the welcome flow.lunadashctl setup
finish-setupEmptyRecord first-run setup completion.lunadashctl finish-setup
quitLiteral "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.

NETWORK EXAMPLES
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, toggleFullscreen, toggleTilingMode, closeWindow, minimizeWindow, launchTerminal, launchFiles, launchLauncher, launchOrbit, chooseWallpaper, randomWallpaper, toggleEyeCare, toggleScratchpad, openControlCenter, openClipboard, openPowerMenu, screenshot, plus workspace1..10 and moveToWorkspace1..10. Values are Meta/Alt/Ctrl/Shift combinations such as Meta+Shift+Space, or Disabled.

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.

TARGET ID FORMS
builtin:<extension-target>
plugin:<package-id>:<extension-target>
module:<module-id>:<section>
layout:<active-window-template>
PATCH FLOW
# 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.

Native Plugin SDK 2 ABI

Native effects do not receive 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.

ABI
#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.

REQUEST ENVELOPE
{
  "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; see Compositor plugins.

Trust boundary: native effects run inside the compositor without a sandbox.