manifest.json reference
Every widget has a manifest.json at the root of its .nepget bundle. It tells
NepTunes how to load and present the widget.
Required fields
| Field | Type | Description |
|---|---|---|
manifestVersion |
Int | Manifest format version. Always 1. |
id |
String | Unique identifier in reverse-domain notation, e.g. com.example.my-widget. |
name |
String | Display name shown in the widget picker. |
version |
String | Semantic version, e.g. 1.0.0. |
entry |
String | Entry HTML file, typically index.html. |
defaultSize |
Object | Default window size: { "width": Int, "height": Int } in pixels. |
Optional fields
| Field | Type | Description |
|---|---|---|
author |
String | Author name. |
description |
String | Short description of the widget. |
license |
String | License identifier, e.g. MIT. |
homepage |
String | Homepage URL. |
minSize |
Object | Minimum window size { width, height }. Applies to user resizing and to NepTunes.setSize() alike, whatever resizable says. |
maxSize |
Object | Maximum window size { width, height }. Applies to the same paths. Omitting it is not “unbounded” — a widget window is never larger than the display it is on. |
resizable |
Bool | Whether the user can resize the widget — by dragging its edges, or a resize handle the widget draws itself. Defaults to true; set it to false for a fixed-size widget. |
preview |
String | Preview image filename inside the bundle. |
icon |
String | Icon filename inside the bundle. |
permissions |
Array | The permissions the widget needs. |
settings |
Object | A { "schema": [ … ] } object describing user settings. |
minNepTunesVersion |
String | Minimum NepTunes version, e.g. 4.2.0. If it’s newer than the running app, the update is shown as blocked instead of installed. |
authorPublicKey |
String | Base64 of your Ed25519 signing key’s public half — 32 raw bytes, no ed25519: prefix. Required when the bundle ships a bundle.sig, and it must equal that file’s authorPublicKey; declaring it without a signature makes the bundle refuse to install. Pinned on first install; a change forces users to re-consent. See Updates & signing. |
releaseNotes |
String | What changed in this version. Shown in the update confirmation sheet. |
bundle.sig — the manifest’s signature sidecar
A signed bundle carries a second file next to manifest.json: bundle.sig, holding
the author’s Ed25519 signature over the SHA-256 hash of every file in the bundle —
manifest.json included. You never hand-write it; embed-sign generates it
(widget-tools.mjs —
Updates & signing explains how to get it and where keygen
puts your keys):
node widget-tools.mjs embed-sign MyWidget.nepget \
--key .keys/my-widget-author.pem
node widget-tools.mjs embed-verify MyWidget.nepget
Two rules tie the two files together, and NepTunes enforces both at install time:
manifest.authorPublicKeymust equalbundle.sig’sauthorPublicKey.- A manifest that declares
authorPublicKeywith nobundle.sigis rejected — claiming an identity without proving it is worse than claiming none.
Because the manifest is itself covered by the signature, any manifest edit (a version
bump, a new permission) means re-running embed-sign. The full format, the exact bytes the
signature covers, and the verification order are in
Updates & signing.
Complete example
This is the manifest from the “Now Playing” widget in the gallery:
{
"manifestVersion": 1,
"id": "pl.micropixels.neptunes.widget.now-playing",
"name": "Now Playing",
"version": "1.9.0",
"author": "NepTunes",
"description": "Displays the currently playing track with playback controls",
"license": "MIT",
"homepage": "https://neptunesmac.app",
"entry": "index.html",
"defaultSize": { "width": 368, "height": 220 },
"minSize": { "width": 328, "height": 200 },
"resizable": false,
"preview": "preview.jpg",
"permissions": ["artwork", "playbackControl"],
"settings": {
"schema": [
{
"id": "showArtwork",
"type": "checkbox",
"label": "Show Album Artwork",
"default": true
},
{
"id": "theme",
"type": "radio",
"label": "Theme",
"options": [
{ "value": "dark", "label": "Dark" },
{ "value": "light", "label": "Light" }
],
"default": "dark"
}
]
}
}