Repository navigation
Authoring
A plugin adds conditions (true/false checks) and actions (things to do) to NtfyBar's rules. It is a folder with a plugin.json manifest and one JavaScript file. It runs in a sandbox. Canonical text: docs/authoring.md.
com.example.my-plugin/ folder name = plugin id
├── plugin.json
└── main.js
Files sit directly in the folder (no subfolders). Every file must be listed in files.
{
"id": "com.example.my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "One line, up to 120 characters.",
"author": { "name": "Your Name", "url": "https://github.com/you" },
"apiVersion": 1,
"minAppVersion": "1.0.0",
"entry": "main.js",
"files": ["plugin.json", "main.js"],
"permissions": ["ui"],
"permissionReasons": { "ui": "Shows the build result in a popup." },
"providers": [
{
"id": "com.example.my-plugin.failed-build",
"kind": "condition",
"name": "Build failed",
"description": "The message reports a failed build.",
"function": "isFailedBuild",
"params": [{ "key": "branch", "label": "Branch", "kind": "text", "placeholder": "main" }]
}
],
"popup": { "engine": "declarative" }
}| Field | Rules |
|---|---|
id |
lowercase reverse-DNS, up to 100 characters, equals the folder name |
name |
up to 60 characters |
description |
required, up to 120 characters |
apiVersion |
1 |
minAppVersion |
optional, oldest NtfyBar that can run it |
entry |
the JavaScript file, listed in files
|
files |
every file, plugin.json included; letters, digits, _ . -, no leading dot |
permissions |
see Permissions |
permissionReasons |
required for each permission, up to 120 characters, shown to the user |
providers |
at least one |
popup |
optional, {"engine": "declarative"} is the only engine today |
Each provider:
| Field | Rules |
|---|---|
id |
starts with your plugin id plus a dot |
kind |
condition or action
|
name, description
|
shown in the rule editor, description up to 120 characters |
function |
a global function in the entry file |
params |
optional {key, label, kind, placeholder}; kind is text, number, bool, topic, or (1.1+) choice with "choices": ["a", "b"] (2–20 values) |
A package with an invalid manifest is not loaded. A broken provider is skipped on its own. The Plugins tab lists every problem.
// condition: return true or false
function isFailedBuild(ctx, params) {
return /failed/i.test(ctx.message.title || "") &&
(!params.branch || ctx.message.message.indexOf(params.branch) >= 0);
}
// action: return undefined, or {popup: …} (needs "ui")
function showResult(ctx, params) {
return { popup: { title: ctx.message.title, accent: "critical", blocks: [{ text: ctx.message.message }] } };
}
// optional: called when a button on your popup is pressed
function onPopupAction(ctx, buttonId, state) {
return { close: true };
}Any other return type, or a thrown exception, is logged as a provider error. NtfyBar keeps running.
{
message: {
id, time, // ntfy message id, unix seconds
title, message,
priority, // 1 (min) … 5 (urgent)
tags, // array of strings
click, icon,
actions, attachment, // as ntfy sends them
contentType, // "text/markdown" for markdown messages
isCatchUp // true if it arrived while NtfyBar was offline
},
topic: { id, label }
}Topic names and server addresses never reach a plugin. Where one appears in a message, it becomes [topic] or [server].
The values the user entered in the rule editor, by key. topic params hold a topic id.
Each ntfy method exists only while its permission is granted, and the user can switch permissions off at any time. Check first:
if (ntfy.publish) ntfy.publish({ topicId: ctx.topic.id, message: "seen" });- Plain JavaScript (JavaScriptCore). No
fetch,XMLHttpRequest,require, timers,processor DOM. - A call may run for 1 second. After three timeouts the plugin is disabled until the user reloads plugins.
- Calls run on NtfyBar's main thread: keep them short.
- Call
ntfymethods inside your functions, not at the top level.
- NtfyBar › Settings › Plugins › Sources › Add Folder…, pick your plugin folder.
- Installed › your plugin › Review and Enable….
- Settings › Rules › add a rule that uses your condition or action.
- After editing, press Reload Plugins.
Local plugins are marked Unverified source. Logs:
/usr/bin/log show --last 10m --predicate 'subsystem == "com.padina.ntfybar" AND category == "plugins"'| What | Limit |
|---|---|
| One file | 2 MB |
| Whole package | 10 MB |
| One call | 1 s (3 timeouts disable until reload) |
ntfy.history |
200 messages per call |
ntfy.publish |
10 per minute per plugin; quiet hours cap the priority |
ntfy.storage |
1 MB per plugin |
ntfy.copy |
1 MB of text |
| Popup | 20 blocks, 3 buttons, text block 4 KB, 50 table rows, state 64 KB |
Next: Permissions, Popup Spec, Publishing.
NtfyBar Plugins · MIT © Roy Padina · Support on Ko-fi ☕