Workflow & Publishing
Local development
Section titled “Local development”Drop your plugin under $XDG_DATA_HOME/noctalia/plugins/<plugin>/ (or add a path source pointing at your dev directory), then enable it once. Edits to .luau files hot-reload automatically; manifest changes are picked up on the next config reload. Drive an entry’s onIpc handler from the shell to test:
# A bar widget - target a specific output (focused / connector / a-bar-name) or all:noctalia msg plugin me/hello:hello focused greet "hi there"
# A service - it is a singleton with no output, so address it with the `all` target:noctalia msg plugin me/hello:ticker all refresh
# A panel - toggle by full entry id:noctalia msg panel-toggle me/hello:panelThe target picks which live instances receive the event: focused (the focused output), a connector or <connector>:<bar-name>, or all. Services and other non-bar entries have no output, so they only match all.
Publishing
Section titled “Publishing”Plugins are distributed from source repos - one repo holds many plugins, each in its own subdirectory matching the part of the id after the / (so me/hello lives at hello/). Add a catalog.toml at the repo root indexing every plugin so it can be listed and compat-checked without a full clone:
[[plugin]]id = "me/hello"name = "Hello"version = "1.0.0"author = "me"license = "MIT"icon = "puzzle"description = "A friendly greeter."deprecated = falseplugin_api = 3tags = ["demo"]dependencies = ["slurp"]Catalog rows require id, name, and a positive integer plugin_api; rows without them are ignored.
Keeping older Noctalia versions supported
Section titled “Keeping older Noctalia versions supported”When you raise a plugin’s plugin_api, every Noctalia below that level loses the plugin - unless the catalog names an older revision they can still run. Add a [[plugin.release]] row for each API level your history supports, newest first:
[[plugin]]id = "me/hello"version = "2.0.0"plugin_api = 9# ...
[[plugin.release]]plugin_api = 3version = "1.4.0"rev = "5082ed5f85e795513b8485e4cedc66d5a2c816ff"A host picks the newest release its API range allows and exports that exact commit, so a user on an older Noctalia gets v1.4.0 instead of nothing, and moves up to v2.0.0 on their next update after upgrading. The store labels the row so the older version is never installed silently.
Each row needs plugin_api, version, and a full 40-character rev; rows that are malformed, or that sit at or above the tip’s own level, are dropped. Generate them by walking git log -- <subdir>/plugin.toml newest-first and keeping each revision that lowers the API level - the update-catalog.py script in the official and community repos does this for you, and it needs full history (fetch-depth: 0 in CI). Release rows apply to git sources only; a path source has no revisions to export.
Users add your repo with noctalia msg plugins source add <name> git <url>, then enable plugins from it.
Sharing your plugin
Section titled “Sharing your plugin”To get your plugin into the plugin store every Noctalia user already has, open a PR against community-plugins - its README has the submission rules. Your plugin ships a plugin.toml, its entry scripts, a README.md, a thumbnail.webp, and translations/en.json; CI validates all of it and regenerates the catalog on merge. Directory names are first-come in that repo, so a plugin named weather can only exist once there.
official-plugins is maintained by the core team and does not take third-party plugins.