Skip to content

Manifest reference

Every add-on is a folder with a manifest.json at its root. The manifest says what the add-on is, which Budvo versions it works with, what it may ask for and where its files are.

json
{  "$schema": "https://budvo.nulldaily.com/schemas/addon-manifest-v1.json",  "manifest": 1,  "id": "pixel-pets",  "name": "Pixel Pets",  "version": "1.2.0",  "kind": "feature",  "runtime": "app@1",  "publisher": "NullDaily",  "summary": "Raise a pixel pet. Feeding it feeds your debt payoff.",  "compat": { "budvo": ">=0.4.0 <1.0.0", "sdk": "^1.0.0" },  "entry": "app/index.html",  "icon": "icon.png",  "permissions": ["summary:read", "jar:pledge"],  "pledge": { "daily_max_cents": 500, "reason": "Food, toys and treats for your pet go into your jar." },  "network": [],  "ui": { "nav": { "label": "Pets", "icon": "paw-print" } }}

Fields

FieldRequiredMeaning
manifestyesManifest format version. Always 1 for now.
idyesLowercase letters, digits and dashes, 3 to 40 characters, like pixel-pets. Unique across the marketplace, and never changes once published.
nameyesWhat people see, up to 40 characters.
versionyesThe add-on's version, in semver: 1.2.0.
kindyestheme, feature or connector, for the catalog. It must match the runtime (below).
runtimeyestheme@1, app@1, or (later) connector@1.
publisheryesYou, or your company.
summarynoOne sentence for the catalog, up to 200 characters.
compat.budvoyesA semver range of Budvo server versions the add-on supports.
compat.sdkapp@1A semver range of the SDK and bridge protocol it was built for.
entryyestheme@1: the tokens file. app@1: the HTML page loaded in the sandbox. A path inside the folder.
iconnoA PNG or SVG in the folder, around 256 by 256.
permissionsyesWhat the add-on may ask the bridge for. Use [] for none.
pledgewith jar:pledgedaily_max_cents (the most it may pledge per day) and reason (a sentence shown to the owner).
networknoHostnames the add-on may fetch through net.fetch. Empty or missing means none.
leaguewith league:shareshares: what the add-on shares with a league, up to 120 characters, shown to the owner and each person.
ui.navnoAdds a page to the dashboard's Add-ons menu: a label (up to 24 characters) and a Lucide icon name.

Runtimes and kinds go together:

RuntimeKind
theme@1theme
app@1feature
connector@1connector

Catalog fields

The marketplace also reads these when you publish. Budvo servers ignore them.

FieldMeaning
descriptionThe long description, in markdown. Without it, the folder's README.md is used.
tagsUp to 8 short lowercase words, like ["game", "pixel-art"].
notesRelease notes for this version, in markdown.
price_cents, billing, currencyFor paid add-ons: billing is free, one_time or subscription. Everything is free for now.

Permissions

PermissionBridge methodsNeeds
summary:readsummary.read
debts:readdebts.read
goals:readgoals.read
jar:readjar.read
jar:pledgejar.pledgea pledge object
storagestorage.get, storage.set
netnet.fetchat least one host in network
league:shareleague.create, league.join, league.list, league.publish, league.read, league.invite, league.leave, league.deletea league object

context.get, ui.toast and ui.resize need no permission. Ask only for what you use: owners see the list before turning your add-on on, and reviewers ask about anything that looks unneeded. theme@1 add-ons can't have permissions.

Pledges

json
"pledge": {  "daily_max_cents": 500,  "reason": "Food, toys and treats for your pet go into your jar."}
  • daily_max_cents is the most the add-on may pledge in a day. The owner can set a lower limit, never a higher one. Keep it small: a few dollars is plenty for a game.
  • reason is shown to the owner next to the limit. Say plainly what earns a pledge.
  • Amounts are integer cents in the household's currency.

A pledge only adds to the jar. Above the day's limit the rest is dropped and the bridge answers with capped: true; see jar.pledge.

Network

json
"permissions": ["net"],"network": ["api.elections.kalshi.com"]

Hostnames only: no https://, port, path or wildcard. List each subdomain you use. Requests are GET only, and responses must be JSON or text up to 1 MB. With the net permission you need at least one host; without it, leave network empty.

Leagues

json
"permissions": ["storage", "league:share"],"league": { "shares": "display name, picks and points" }

Friends leagues are kept on the Budvo website and only take league data: a display name, a random member id per league and pick entries in a fixed shape (no amounts, stakes or jar). Say plainly what you share in shares; the owner reads it next to the "Share leagues" switch, and each person sees it before agreeing. See pc.league.

json
"ui": { "nav": { "label": "Pets", "icon": "paw-print" } }

Adds a page to the dashboard's Add-ons menu that shows your entry. The icon is any Lucide name in kebab case.

Compat and versions

Your add-on's version

  • Use semver. Bump the patch for fixes (1.2.1), the minor for new things (1.3.0) and the major when something changes in a way people would notice, like new permissions or a bigger pledge limit.
  • A published version never changes. To ship anything, even a typo fix, bump version and publish again.

compat.budvo

A semver range of the Budvo server versions your add-on works with. It's required, and it's checked when an add-on is installed, every time the server starts, and before the server updates itself: the update screen warns about add-ons the new version would switch off. A server outside the range won't load the add-on and says why.

RangeMeans
>=0.4.0 <1.0.00.4.0 up to, not including, 1.0.0. The usual choice.
^0.4.00.4.x only, since versions before 1.0 treat each minor as a major.
>=0.4.00.4.0 and anything later. Risky: a future major could break it.

Development builds count as their base version: a server running 0.4.0-dev matches >=0.4.0.

compat.sdk

For app@1 add-ons: the range of SDK and bridge protocol versions you built against, usually ^1.0.0. Servers publish the SDK version they speak in /api/status (for example "sdk": "1.0.0").

  • Breaking changes to the bridge bump the SDK major, to 2.0.0.
  • Servers keep speaking every major they support, so an add-on built for 1.x keeps working after 2.0 arrives.
  • An add-on whose compat.sdk isn't satisfied isn't loaded.

More examples

A theme:

json
{  "manifest": 1,  "id": "theme-midnight",  "name": "Midnight",  "version": "1.0.1",  "kind": "theme",  "runtime": "theme@1",  "publisher": "NullDaily",  "compat": { "budvo": ">=0.2.0 <1.0.0" },  "entry": "theme.json",  "permissions": [],  "summary": "Deep navy surfaces with an indigo accent.",  "tags": ["dark", "blue"]}

An app@1 add-on with network access and storage:

json
{  "manifest": 1,  "id": "forecast-pledges",  "name": "Forecast Pledges",  "version": "1.0.0",  "kind": "feature",  "runtime": "app@1",  "publisher": "NullDaily",  "compat": { "budvo": ">=0.4.0 <1.0.0", "sdk": "^1.0.0" },  "entry": "app/index.html",  "icon": "icon.svg",  "permissions": ["net", "jar:pledge", "storage"],  "pledge": { "daily_max_cents": 1000, "reason": "When a call settles, $1 goes into your jar if you called it and $3 if you didn't." },  "network": ["api.elections.kalshi.com"],  "ui": { "nav": { "label": "Forecasts", "icon": "sparkles" } }}

An add-on with no permissions at all, which can still show pages and toasts:

json
{  "manifest": 1,  "id": "breathing-break",  "name": "Breathing Break",  "version": "0.1.0",  "kind": "feature",  "runtime": "app@1",  "publisher": "You",  "compat": { "budvo": ">=0.4.0 <1.0.0", "sdk": "^1.0.0" },  "entry": "index.html",  "permissions": []}

Check it

terminal
npx budvo-addon validate my-addon

The validator checks the manifest against the schema, the semver ranges, permission names and what they need, that the entry and icon exist, theme tokens and colors, files the sandbox would block, and the 5 MB size limit. It exits with an error code when something's wrong, so it fits in CI; --strict fails on warnings too.