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

Under the hood

libwol: the app API

One stable C ABI for apps in any language: windows, menus, notifications, files, power and more.

What it is

libwol (wol.h and libwol.so.1) is the API WolOS apps use for windows, menus, notifications, the clipboard, file panels, power, network and sound facts. It is a C ABI over Rust, so any language can call it.

Why

Apps break when the system under them changes shape. libwol's symbols are versioned and never removed or changed, only added, so an app built today keeps working.

How

Include wol.h, link libwol.so.1, poll the context's file descriptor and dispatch. The rules and each version's additions are listed below.

Reference

One API for apps from any language: wol.h + libwol.so.1. Code: crates/wol-abi (C ABI over Rust), crates/wol-lua (Lua widgets), wire contracts in shared::portal (app -> wold) and shared::view (app <-> Kit host), wold side in api::portal.

Rules (header comment has the full text)

RuleMeans
Opaque handlesno struct layout is public, ever
_create / _copyyou own a reference: _release it
_get_borrowed, lives as long as its argument
StringsUTF-8 + length in; NUL-terminated + length out
Errorswol_status return; wol_error ** filled only on failure, with a sentence to show
Threadsretain/release anywhere; the rest one thread at a time per context
Callbacksvoid *user_data; run only inside wol_context_dispatch; must not unwind
Event looppoll wol_context_get_fd, then wol_context_dispatch(ctx, 0, &err)
Stabilitysymbols versioned by abi/wol.map; never removed or changed, only added in a new node

Adding API: implement + test, declare in include/wol.h, add the symbol to a new node in abi/wol.map and to abi/wol-<version>.symbols, snapshot the header as abi/wol-<version>.h at release. cargo test -p wol-abi checks all of it, links libwol.so.1 with link.sh and runs tests/c/hello.c (1.0), tests/c/parts.c (1.1) and tests/c/menus.c (1.2) against fake wold and Kit hosts. Version defines are the only lines a newer header changes.

Build: cargo build --release -p wol-abi, then crates/wol-abi/link.sh target/release/libwol.a <dir> (the map alone decides exports). Install libwol.so.1 and wol.h.

Security

  • libwol has no privilege. In-process: reading the person's own settings and world-readable facts. Everything else is a shared::portal::Call to wold, decided with the kernel's peer identity and the consent grants (Settings > Privacy & Security, /etc/wolos/consent.json).
  • No telemetry. libwol opens the wold socket and the Kit host socket ($XDG_RUNTIME_DIR/wol-kit.sock) and nothing else.
  • Every argument is checked (NULL, length, UTF-8, control characters, ranges). Panics stop at the boundary as WOL_ERR_INTERNAL.
  • A call that needs the person's session (clipboard, panels, audio) is checked by wold first, then asked of the shell of the caller's account (Event::SessionAsk, Call::SessionAnswer). wold matches each answer to a waiting ask of that account; a call waits at most a few seconds, and at most 64 wait at once.
  • Decoders: shared/tests/portal_fuzz.rs (every cargo test) and fuzz/ (cargo +nightly fuzz run ipc_decode).

Surface: what is in, what comes, in order

#FamilyPrefixState
1core: version, errors, strings, context, event loop, subscriptionswol_version, wol_error_, wol_string_, wol_context_, wol_subscribe, wol_event_1.0
2system facts, appearance, accentwol_system_info_1.0 (change events: topic theme)
3notificationswol_notification_1.0 post; 1.1 buttons (_create, _add_action, _set_action_handler, _send); 1.2 _replace, _withdraw (only the process that posted it)
4app, windows, Kit partswol_window_, wol_stack_, wol_label_, wol_button_, wol_widget_, wol_row_, wol_list_, wol_spacer_, wol_text_field_, wol_toggle_, wol_slider_, wol_image_, wol_menu_, wol_toolbar_1.0 stack, label, button; 1.1 row, list, spacer, text field, toggle, slider, image, set_text/copy_text, value getters and setters, close handler and wol_window_close; 1.2 menus (wol_window_add_menu) and toolbars (wol_window_set_toolbar)
8clipboardwol_clipboard_1.3 _copy_text, _set_text: grants clipboard-read, clipboard-write
5IPC between apps: typed messages, services, app topicswol_message_, wol_endpoint_, wol_publish1.11 wol_endpoint_create and wol_publish (grant ipc), wol_message_send (grant ipc:<peer path> per peer app): routed by wold within one account; the sender is the executable the kernel reports
6files and portals: open/save panels, granted file handles (fd passing)wol_file_, wol_panel_1.5 wol_panel_open, wol_panel_save, wol_file_get_fd, _get_name: grant file-panels; the desktop opens the person's pick and the descriptor is passed on, so wold never opens a path for an app
7settings and app preferenceswol_prefs_1.4 _copy_string, _set_string, _remove, _copy_keys: grant app-preferences; per account and executable, kept by wold
9search: Search providers and querieswol_search_1.9 _query (grant search); _provide, _reply_add (grant search-provider): wold asks only that account's providers, for 200 ms, each as its own process
10power: battery, power source, keep-awake assertions, Low Power Modewol_power_1.6 _copy_info (grant power-info), _keep_awake and wol_assertion_ (grant keep-awake): a logind inhibitor that lasts as long as the app's connection to wold
11network: reachability, interfaces, Wi-Fi statewol_network_1.7 _copy_info: grant network-info; online, and interface names and kinds, never an address or a network name
12audio: output, volume, now playingwol_audio_1.8 _copy_info (grant audio-info), _set_volume, _set_muted (grant audio-control), run by the desktop
13devices: USB, Bluetooth, iPhonewol_device_1.12 _copy_list (grant devices), _open (grant device:<vendor>:<product> per device): wold opens the USB node and passes the descriptor, never for a keyboard, disk or hub
14sharing and WolConnect: share sheet, nearby deviceswol_share_1.13 _text: grant share; the person sees the text and chooses copy or save. Nearby devices: next, once wold finds them
15services: the app's background jobs (wold, per account)wol_service_1.14 _start, _stop, _copy_status: grant background; wold supervises the caller's own executable as the account, never a program the app names
16packages: installed apps, ask to installwol_package_through wol-admind
18answers: one read-only tree for what a service returnswol_info_1.4
17accounts: who is signed in, name, picturewol_account_1.10 _copy_info: grant account-info; the caller's own account, from the kernel's uid. Accounts have no picture yet

Event topics (wol_subscribe)

JSON is the shared::events line: theme {"event":"theme","id":"wol-dark-blue"}, workspace {"event":"workspace","name":"2"}, active-window {"event":"active-window","class":"…","title":"…","pid":7}, open-window, close-window, job. Notifications go only to the shell; a pressed notification button goes only to the process that posted it, and runs the handler set with wol_notification_set_action_handler.

Menus and toolbars are data on the window (ToHost::Show), drawn by the Kit host: a menu bar under the title row and toolbar buttons in it. cmd in a shortcut is the Command key, which WolOS hands to the app in front as Control (shell::appmenu). What the person chooses comes back as a key; libwol runs the handler only for an enabled key the app added.

Kit parts: a change made through the API redraws the window the part is in (if shown); a change the person made is recorded first, then the part's change callback runs, so getters inside it read the new value.

Kit host and notifications in the shell

  • wol-shell listens on $XDG_RUNTIME_DIR/wol-kit.sock (0600, own account) and starts one wol-kit-host per app connection: the app's windows carry its own window class, and a broken app takes down only its host. Protocol rules: shell::app_host (hello first, at most 16 windows, 256 KiB lines); drawing: shell::ui::app_view (a new tree of the same shape updates parts in place).
  • shell::app_notices subscribes to wold as the account's notification shower and posts each app notification to the session notification server (banners, Notification Center, Focus); a pressed button goes back to wold as notification.answer.

Lua widgets (crates/wol-lua)

A widget is one file in ~/.config/wolos/widgets/<id>.lua (id: lowercase letters, digits, -), reloaded when it changes. On the desktop it is kind lua:<id> (wol widgets add lua:<id>); it renders at its refresh on the desktop widget timer and after a click (shell::ui::lua_widgets). Example: crates/wol-lua/widgets/clock.lua.

return wol.widget {
  name = "Counter", refresh = 0, permissions = { "notifications" },
  render = function()
    return wol.row { spacing = 8, wol.label { "Hello" }, wol.spacer(),
      wol.button { label = "Ping", on_click = function() wol.notify("Ping", "from Lua") end } }
  end,
}
APIGives
wol.stack{spacing=, ...}, wol.row{...}, wol.label{text, role=}, wol.button{label=, on_click=}, wol.spacer()view tree parts (roles: display, large, title, body, secondary, caption)
wol.now(){year, month, day, weekday (1 = Monday), hour, minute, second}
wol.system(){os_name, os_version, kernel, appearance, accent, accent_rgb}
wol.notify(title, body)only with permissions = {"notifications"}; returns ok, message

Sandbox: string, table, math, utf8; no io, os, require, debug, load, bytecode; 16 MiB; 5 M instructions per load, render or click. Services appear after the file loads.

Other languages

All call the same wol.h; strings go as pointer + byte length, so no language needs NUL-terminated buffers. Bindings live in crates/wol-abi/bindings/, each with a hello (notification, event) and a parts (Kit parts, button, notification buttons) that tests/bindings.rs builds and runs against the same fake wold and Kit host as the C programs:

  • Swift: swift/module.modulemap over wol.h (import Wol); no generated code, the header is the binding.
  • Crystal: crystal/lib_wol.cr, @[Link("wol")] lib LibWol.
  • .NET (C#, F#, Alice): dotnet/Wol.Native.cs, P/Invoke on libwol.so.1; Utf8.cs hands strings over as pointer + length.

Crystal and .NET declarations are generated from wol.h (tests/common/bindgen.rs) and the test fails when they differ. For a new ABI version: edit wol.h, run WOL_BLESS=1 cargo test -p wol-abi --test bindings, then add the new calls to a C test and to services in Swift, Crystal and C# (tests/common/services.rs checks them all). A missing toolchain skips its language; WOL_BINDINGS_REQUIRED=1 makes that an error.

Reference synced from wonderlandos/wolos docs/modules/abi.md.