Manifest
A Pulsar extension has a normal manifest.json with one extra key, pulsar. Firefox ignores that key (with a warning), so the same file also loads in desktop Firefox.
A full example
{
"manifest_version": 3,
"name": "Price Watch",
"version": "1.2.0",
"description": "Tells you when saved items cost less.",
"browser_specific_settings": { "gecko": {
"id": "pricewatch@dealab.example", // required, never changes
"update_url": "https://dealab.example/pulsar/updates.json" // only for extensions outside the store
} },
"permissions": ["storage", "notifications"],
"host_permissions": ["*://shop.example/*"],
"background": { "scripts": ["pulsar.js", "background.js"] }, // pulsar.js first
"content_scripts": [{ "matches": ["*://shop.example/*"], "js": ["content.js"] }],
"options_ui": { "page": "options.html" },
"pulsar": {
"api": 1, // the Pulsar API version you speak (required to call the API)
"permissions": ["background"], // Pulsar permissions
"tint": "#BE185D", // your tile colour
"icon": "shield", // optional: a Pulsar icon instead of your own icon
"engines": ["converter"] // engines you call with engines.call
}
}The pulsar key
| Field | Type | Required | What it does |
|---|---|---|---|
| api | number | yes, to use the API | The Pulsar API version your extension speaks. Today 1. Without it, calls to Pulsar are refused (not_allowed). |
| permissions | string[] | no | Pulsar permissions. Today only "background", for background jobs and schedules. |
| tint | #RRGGBB | no | Your tile colour in the extensions row, and the colour of media cards in your drawer. |
| icon | string | no | A Pulsar icon name. Pulsar draws it on your tile instead of your own icon, so it matches the app in light and dark. |
| engines | string[] | no | Engines you call, like ["converter"]. Shown at install as “Use the Converter engine”. See Engines. |
pulsar key is all you need; the store refuses files that ask for nativeMessaging.Background scripts
Load pulsar.js before your own script. In Manifest V3 the background is an event page: it may sleep. Pulsar wakes it when it has something for you (the drawer opened, a setting changed, a scheduled check), and pulsar.js answers by connecting.
"background": { "scripts": ["pulsar.js", "background.js"] }- Register your
Pulsar.on(...)listeners at the top level of your script, so they exist when Pulsar wakes you. - Send your specs again in
Pulsar.onConnect(...): it runs on every (re)connection. - Manifest V2 with
"persistent": trueworks too, but build new extensions as MV3 (see site access below).
Popup and options pages
| Key | In Pulsar |
|---|---|
| action.default_popup | Opens in Pulsar’s drawer when the user taps your tile. One at a time; window.close() closes the drawer. Leave it out to use a native drawer (popup.set). |
| options_ui.page | Shown in Pulsar’s drawer as your settings, unless you send a native settings spec with settings.set. Then runtime.openOptionsPage() opens it in a tab. |
| web_accessible_resources | As in Firefox. Use it for pages you open from a content script. |
Popups only open on the user’s tap: action.openPopup() called by your code is ignored. A tile with no popup and no native drawer runs your action.onClicked handler, like Chrome.
Site access
People choose where an extension runs: All sites, Ask me or Selected sites, and Disable on this site for one site. For Manifest V3 extensions, Pulsar grants and takes back your real host permissions to match. Manifest V2 host permissions can’t be taken back, so Pulsar can only hide the extension: build MV3.
- Ask for the narrowest patterns you need. Pulsar builds each site’s grant from your declared pattern (
https://*/*becomeshttps://*.news.example/*). - With All sites, Pulsar can’t subtract one site from your permissions. It tells you instead (
site.accessanddisabledSitesinhello), and you must stop working there. The store checks this. optional_host_permissionsyou ask for withpermissions.requeststay granted when the user agrees.
Ids and versions
browser_specific_settings.gecko.idis required. Use an email-like id on a domain you own. It can never change: it is how Pulsar, the store and updates know your extension.- Ids ending in
@pulsarbrowser.comare kept for Pulsar. - Every upload needs a higher
versionthan the last one (1.2.0→1.2.1). - A file from outside the store can never replace an installed store extension with the same id.
What Pulsar won’t install
- Extensions from the Chrome Web Store, Mozilla’s store or Microsoft’s store. People see: “Pulsar only installs extensions made for Pulsar.”
- Unsigned
.xpifiles (sign them, or use a Lite extension). - Files over 50 MB.