Not stable yet. WolOS is in active development and the current ISO is a test build: try it in a virtual machine or on a spare disk, not on a computer you depend on.

WolOS
Manual contents

Decisions

0031: Plugins, themes and layouts

Folder plugins in a sandbox, themes for every toolkit, and layouts as data.

Status: proposed (main, 2026-11-01; lead asked for it the same day). Builds on 0010 (widget packages), 0020 (view tree, Lua sandbox), 0021 (wopkg), 0027 (one app cannot take the desktop), 0029 (wolos builds nothing for release). Omarchy (MIT) is the reference for format and ecosystem; its palettes are already ported under `crates/core/src/theme/builtin/NOTICE`.

Context

the shell can be extended only by one Lua desktop card (crates/wol-lua), a JSON widget package (ADR 0010) or a theme JSON (crates/shared/src/theme.rs). The menu bar, Dock, Search, Control Center and hotkeys are closed; themes stop at toolkits (Hyprland colours are static in hyprland.conf); the bar and Dock edges are constants in crates/shell/src/kit/surface.rs. Omarchy shows that a plugin ecosystem forms fast (1,700+ repositories in months) when the format is one folder, one manifest and one CLI, and that most plugins are small: a bar item, a panel, a theme, a web app launcher, a toggle. Omarchy runs plugins unsandboxed; WolOS will not. The lead wants: a powerful plugin and theme system integrated everywhere, an app to make themes and plugins, a layout abstraction so a Windows-like layout can come later, an API that AI agents can write plugins against easily, and plugin updates that do not wait for OS releases.

Decision

  • A plugin is a folder <id>/ with plugin.toml and the files it names. Search order: ~/.local/share/wolos/plugins/<id>/ (user, reloaded on change), /usr/share/wolos/plugins/<id>/ (packaged). Ids are reverse-DNS or <user>.<name>, lowercase; org.wolos.* is reserved. No symlinks, 1 MiB total, writes only under $XDG_STATE_HOME/wolos/plugins/<id>/.
  • Manifest plugin.toml, schema 1 (frozen in crates/shared/src/plugins.rs; unknown key = load error with a sentence): header schema, id, name, version, author, license, description, min_wolos, permissions; capability tables, each with an id: [[menubar]] (script, side), [[dock]] (name, icon, exec), [[widget]] (script, sizes; today's lua:<id> card), [[panel]] (script, anchor), [[search]] (script, kind, prefix), [[tile]] (icon, label, toggle), [[toggle]] (on, off argv; flag file in $XDG_STATE_HOME/wolos/toggles/), [[hotkey]] (default, exec, group; becomes a user-rebindable shortcuts::Custom), [[webapp]] (name, url, icon, profile), [[theme]] (file), [[look]] (file), [[hypr]] (file, whitelist of windowrule, layerrule, bezier, animation; never bind, exec, plugin, env, source). The schema is published as docs/plugins/plugin.schema.json and installed at /usr/share/wolos/plugins.schema.json so agents can validate; the whole API is documented in one file, docs/modules/plugins.md.
  • Code is Lua in the existing sandbox (wol-lua: 16 MiB, 5M instructions, no io/os/require). API v1 = today's wol.* plus the view nodes that exist in shared::view but not in the prelude (image, toggle, slider, list, item, text_field), wol.state.get/set (per-plugin JSON, 64 KB), wol.run{argv, timeout} (permission run, argv only, through widgets::run_limited), wol.http_get (per-host grant as ADR 0010), wol.open, wol.toggle_set, wol.clipboard.text (new capability, through wold), wol.show(panel). Anything that changes the system is wol.run{"wol", ...}: it passes controller::approve and the audit log, so plugins inherit the helper policy and appear in Settings > Assistants & Automation under the plugin id. No new privilege path. Load never acts.
  • Permissions and limits: declared in the manifest, shown before enabling (Store sheet, capability_row), stored as grants in helpers.json keyed by plugin id, revocable in Settings. Each render and click is budgeted; a plugin that fails three renders in a row is disabled with a toast saying why. Hypr fragments are parsed into hyprprefs keywords, never sourced raw.
  • Distribution, independent of OS releases: (a) folder drop; (b) wol plugin add <git-url|path|.wolplug> (stage, check, move; hooks never run); (c) a signed plugin index (index.json + Ed25519 signature, key in /usr/share/wolos/keys/) consumed by a new plugins source in wopkg (ADR 0021 recipe model) and by a Store "Plugins" page. A .wolplug is a tar.zst of the folder plus SHA256 and optional SHA256.sig (ADR 0011 form). Installs are user level; wopkg update updates plugins without touching pacman. Unsigned plugins are marked unreviewed like AUR and need the typed yes. The index repository (wolos-plugins) accepts pull requests validated by wol plugin check in CI.
  • Agent-first CLI: wol plugin new <id> [--kinds ...] (scaffold with a passing check), wol plugin check <dir> [--json] (schema, ids, sizes, symlinks, Lua loads, every entry renders with sample state, theme contrast, hypr whitelist), wol plugin run <dir> --kind <k> [--click n --state f] (prints the Node tree as JSON, no display needed), list|enable|disable|remove|update|pack|preview.
  • Themes v2: theme.json stays (tokens + extends) and gains optional terminal (16 colours, derived when absent), wallpaper, iconTheme, lock, look. New renderers in crates/core/src/theme/: Hyprland colours (hyprbars bar and text, borders, shadow, written to hypr/wolos/theme.conf), lock screen, terminal palettes (foot exists; btop, alacritty, kitty, ghostty opt-in), zsh and PowerShell prompt colours. User templates ~/.config/wolos/themed/<name>.tpl with {{token}}, {{color3}}, {{token|strip}}, {{token|rgb}} rendered on every switch. Theme-from-wallpaper generator must pass check() (contrast). Theme picker panel on ⌘⌃T, background picker on ⌘⌃W.
  • Layouts: shared::layout::Layout { bar: Edge, bar_style: Menubar | Taskbar, dock: Option<Edge>, dock_merged_into_bar, workspaces_in_bar, tray_in_bar } saved in shell.json. Now: Spec::bar(edge) and Spec::dock(edge) take anchors from Layout; the bar becomes slots left/centre/right of Vec<BarItem> (plugin items are BarItem::Plugin); Dock::content takes Vec<DockItem> and an orientation; the hot zone and reveal direction follow the dock edge; Hyprland layer namespaces stay. Later, "Windows-like" = Layout{bar: Bottom, bar_style: Taskbar, dock: None, dock_merged_into_bar: true, workspaces_in_bar: true} plus one BarItem::Tasks and a start item; no other surface changes.
  • Plugin Maker / Theme Studio (wol-plugin-maker, GTK4): editor (TOML/Lua, GtkSourceView) with live preview reusing the widget gallery, a fake bar with the item inserted, Control Center tile preview and the theme preview with token sliders and live contrast; Check (same function as the CLI), Pack, Publish (opens the index pull request with the hash). Reached from Settings > Appearance ("Make a theme...") and Store > Plugins ("Make one").
  • Omarchy features ported natively in v0.12 (MIT, rewritten, not loaded as QML): web apps (wol webapp add, frameless browser window, .desktop, Search and Dock entries), toggles as state with Control Center tiles (Stay Awake, Silence, Bar, Touchpad), theme-from-wallpaper and the two pickers, Hyprland colours from the theme, capture menu additions (colour picker, OCR to clipboard, QR decode kept out of history), clipboard history with images and 1-day expiry that skips verification codes, reminders (wol remind), monitor profiles with auto-apply on hotplug, update badge in the menu bar, and a reference bar plugin org.wolos.stats that proves the API and is the documentation example.

Consequences

one manifest, one sandbox, one view tree: plugin UI cannot leave the HIG and a broken plugin shows a sentence, not a crash. Plugins reach system services only through wol verbs, so helper policy, audit and Always-Allow cover them unchanged; a plugin that needs more is a libwol app (ADR 0020). Plugin updates ride wopkg's signed index, not OS releases, so ADR 0029 is untouched. Omarchy plugins are ports, not loads; most are small. The layout refactor is mechanical and must land before plugin bar items, because BarItem and DockItem are the frozen seam. Memory and instruction limits of wol-lua now protect the menu bar, so the existing bomb tests are extended to every capability.

Reference synced from wonderlandos/wolos docs/adr/0031-shell-plugins-themes-layouts.md.