-
Notifications
You must be signed in to change notification settings - Fork 0
Manifest Reference
manifest.json is the only WordPress-specific file in a ware. It lives at the root of the archive and tells Bazaar how to register your app — its name, where to find the entry HTML, where to put it in the sidebar, and who can access it.
{
"name": "Ledger",
"slug": "ledger",
"version": "1.0.0"
}That's all you need. Bazaar fills in sensible defaults for everything else.
{
"name": "Ledger",
"slug": "ledger",
"version": "1.2.0",
"author": "Nick",
"description": "Generate and manage invoices directly from wp-admin.",
"icon": "icon.svg",
"entry": "index.html",
"menu": {
"title": "Invoices",
"position": 30,
"capability": "manage_options",
"parent": null,
"group": "finance"
},
"shared": ["react", "react-dom", "react/jsx-runtime"],
"permissions": ["read:posts"],
"permissions_network": [
"https://api.stripe.com",
"https://cdn.example.com"
],
"health_check": "https://api.example.com/health",
"jobs": [
{
"id": "sync_invoices",
"label": "Sync invoices from payment provider",
"interval": "hourly",
"endpoint": "https://example.com/wp-json/bazaar/v1/jobs/ledger/sync_invoices"
}
],
"license": {
"type": "key",
"url": "https://example.com/api/validate-license",
"required": true
},
"updateUrl": "https://registry.example.com/wares/ledger.json",
"homepage": "https://example.com/wares/ledger"
}| Property | Value |
|---|---|
| Type | string |
| Required | Yes |
| Example | "Ledger" |
Human-readable display name. Shown in the Bazaar gallery card and as the browser tab title when the ware is open.
| Property | Value |
|---|---|
| Type | string |
| Required | Yes |
| Pattern | [a-z0-9-]+ |
| Example | "ledger" |
Unique identifier for the ware. Used as:
- the directory name under
wp-content/bazaar/ - the path segment in all REST API URLs (
/bazaar/v1/serve/ledger/…) - the WordPress menu slug (
bazaar-ware-ledger)
Caution
The slug is permanent. Changing it after installation requires deleting and re-installing the ware. Choose it carefully — lowercase letters, numbers, and hyphens only.
| Property | Value |
|---|---|
| Type | string |
| Required | Yes |
| Format | Semver recommended (MAJOR.MINOR.PATCH) |
| Example | "1.0.0" |
Version string displayed in the gallery. Bazaar does not currently enforce semver but it is strongly recommended.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Example | "Nick" |
Creator name shown in the gallery card. Can be a person, team, or company.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Example | "Generate and manage invoices from wp-admin." |
Short description shown in the gallery card. One or two sentences is ideal.
| Property | Value |
|---|---|
| Type |
string (path relative to archive root) |
| Required | No |
| Default | "icon.svg" |
| Example |
"icon.svg", "assets/logo.png"
|
Path to the ware's sidebar icon within the archive. Supported formats:
| Format | Notes |
|---|---|
| SVG ✓ recommended | Embedded as a data: URI — scales perfectly, respects WP admin colour schemes. Keep under 4 KB. |
| PNG / JPG / WebP | Served via the Bazaar file server. Use 20×20 px (or 40×40 for retina). |
If the file is missing or the path is wrong, Bazaar falls back to dashicons-admin-plugins.
| Property | Value |
|---|---|
| Type |
string (path relative to archive root) |
| Required | No |
| Default | "index.html" |
| Example |
"app.html", "dist/index.html"
|
The HTML file Bazaar loads in the iframe. Must exist in the archive.
Tip
If your build tool outputs to a subdirectory (e.g. dist/index.html), either set "entry": "dist/index.html" in the manifest — or zip from inside dist/ so index.html sits at the archive root. The latter is usually cleaner.
An object that controls how the ware's admin page is registered. All sub-fields are optional.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | Same as name
|
| Example | "Invoices" |
The label shown in the WordPress sidebar. Shorter than name is usually better here.
| Property | Value |
|---|---|
| Type | integer |
| Required | No |
| Default | Appended to end |
| Example | 30 |
WordPress menu position integer. Common reference points:
| Position | Default item |
|---|---|
| 2 | Dashboard |
| 5 | Posts |
| 10 | Media |
| 20 | Pages |
| 25 | Comments |
| 60 | Appearance |
| 65 | Plugins |
| 70 | Users |
| 75 | Tools |
| 80 | Settings |
Tip
Use a non-round number (e.g. 26 instead of 25) to avoid collisions with other plugins.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | "manage_options" |
| Example |
"edit_posts", "read"
|
The WordPress capability a user must have to see and access the ware. Bazaar enforces this on both menu registration and the REST file-serving endpoint.
| Capability | Who has it |
|---|---|
manage_options |
Administrators only |
edit_posts |
Editors, Authors, Administrators |
publish_posts |
Authors and above |
read |
All logged-in users (Subscribers and above) |
Note
This is a minimum capability check. For finer-grained access control inside your ware, implement it yourself using the WordPress REST API (/wp/v2/users/me).
| Property | Value |
|---|---|
| Type | string | null |
| Required | No |
| Default |
null (top-level menu item) |
| Example | "tools.php" |
When set, the ware becomes a submenu item under an existing top-level menu.
| Slug | Parent menu |
|---|---|
tools.php |
Tools |
options-general.php |
Settings |
upload.php |
Media |
edit.php |
Posts |
edit.php?post_type=page |
Pages |
bazaar-ware-{slug} |
Another installed ware |
| Property | Value |
|---|---|
| Type | string | null |
| Required | No |
| Default | null |
| Example | "finance" |
An arbitrary group label used by the Bazaar shell to visually cluster related wares in the sidebar nav. Multiple wares with the same group value are rendered under a shared section header. Has no effect on WordPress's own menu registration.
| Property | Value |
|---|---|
| Type | string[] |
| Required | No |
| Default | [] |
An array of WordPress capability tokens the ware requires. These are checked against the current user's capabilities before the ware loads.
{
"permissions": ["read:posts", "edit:posts"]
}| Property | Value |
|---|---|
| Type |
string[] | true | null
|
| Required | No |
| Default | null |
Declares what external origins the ware is permitted to fetch from. This is a top-level field, separate from permissions.
-
null(default) — no network restrictions (zero-trust not enforced for this ware) -
true— ware may fetch from any HTTPS origin -
string[]— an explicit allowlist of HTTPS origins
When set, the zero-trust service worker intercepts all fetch() calls from this ware and blocks requests to unlisted origins.
{
"permissions_network": [
"https://api.stripe.com",
"https://fonts.googleapis.com"
]
}Note
The WordPress site's own origin is always implicitly allowed. Zero-trust enforcement only activates if permissions_network is non-null and the ware also declares "zero_trust": true.
| Property | Value |
|---|---|
| Type | boolean |
| Required | No |
| Default | false |
When true, the zero-trust service worker is activated for this ware. Must be combined with a non-null permissions_network to have any effect. Wares that set zero_trust: true without a permissions_network value will not have network enforcement applied.
| Property | Value |
|---|---|
| Type | string[] |
| Required | No |
| Default | [] |
| Example | ["react", "react-dom"] |
A list of npm package names that the ware wants to load from the Bazaar shell's shared bundle, rather than bundling its own copy.
When the shell serves a ware's HTML entry file, it injects a <script type="importmap"> that maps each declared package name to a versioned, content-hashed URL hosted by the plugin. The browser downloads that URL once and caches it forever — subsequent ware iframes that declare the same dependency get the compiled module from the browser's V8 bytecode cache with zero re-download.
Currently provided shared packages:
| Package | Version |
|---|---|
react |
19.x |
react-dom |
19.x |
vue |
3.x |
To opt in, declare the packages in manifest.json and mark them as external in your Vite config so they are not bundled:
{
"shared": ["react", "react-dom"]
}// vite.config.ts
build: {
rollupOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'],
},
},Tip
The create-ware React and Vue scaffolds include both of these automatically — you don't need to configure anything manually when using npm create ware@latest.
Note
This field is purely opt-in. Wares that don't declare shared continue to bundle their own copy of React/Vue and require no changes. Shared and self-bundled wares can coexist on the same install.
| Property | Value |
|---|---|
| Type |
string (URL) |
| Required | No |
| Example | "https://api.example.com/health" |
A URL that Bazaar polls to determine whether an external dependency (API, service, etc.) is reachable. The result is surfaced in the Bazaar shell UI as a status indicator (ok / warn / error) and pushed in real-time via the SSE stream.
Bazaar performs a GET request with a 5-second timeout and maps HTTP status codes to:
| HTTP range | Status |
|---|---|
| 200–299 | ok |
| 300–499 | warn |
| 500+ or network error | error |
| Property | Value |
|---|---|
| Type | array |
| Required | No |
Declares background jobs that Bazaar should schedule via WP-Cron on install. Each job is an object:
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier within this ware (e.g. sync_orders) |
label |
string |
Yes | Human-readable description shown in the Bazaar shell |
interval |
string |
Yes | WP-Cron schedule: hourly, twicedaily, daily, or any custom schedule name |
endpoint |
string |
No | REST URL Bazaar calls when the job fires. Omit if you register a WP-Cron hook directly in a companion plugin. |
{
"jobs": [
{
"id": "sync_products",
"label": "Sync products from API",
"interval": "hourly",
"endpoint": "https://example.com/wp-json/bazaar/v1/jobs/my-ware/sync_products"
}
]
}Admins can view scheduled jobs and trigger them manually via GET /bazaar/v1/jobs/{slug} and POST /bazaar/v1/jobs/{slug}/{job_id}.
| Property | Value |
|---|---|
| Type | object |
| Required | No |
Controls license-key enforcement for paid wares.
| Field | Type | Default | Description |
|---|---|---|---|
type |
"free" | "key"
|
"free" |
"key" enables license-key gating |
url |
string |
"" |
URL Bazaar POSTs { slug, key, site } to for remote validation |
required |
boolean |
false |
When true, installation is blocked until a key is stored. The value is stored internally as the string "true" — both true (JSON boolean) and "true" (string) are accepted on input |
{
"license": {
"type": "key",
"url": "https://example.com/api/validate-license",
"required": true
}
}Set and validate keys with wp bazaar license set <slug> <key> or through the Bazaar admin UI.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
Top-level field. URL to a JSON file describing the latest available version of this ware. Bazaar compares the returned version to the installed version and flags the ware as outdated if a newer one is available (wp bazaar outdated).
{
"updateUrl": "https://registry.example.com/wares/my-ware.json"
}| Property | Value |
|---|---|
| Type | string |
| Required | No |
Top-level field. Canonical page for this ware (shown in the Bazaar gallery card).
| Property | Value |
|---|---|
| Type | string |
| Required | No |
Top-level field. Base64-encoded RSA signature over the archive contents. When present, Bazaar verifies this signature on install using the configured public key. Generated by wp bazaar sign.
| Property | Value |
|---|---|
| Type |
"standard" | "trusted" | "verified"
|
| Required | No |
| Default | "standard" |
Sandbox privilege level granted to this ware. Controls which extra sandbox attributes are set on the iframe. This value is set and managed by the site admin — the manifest value is a hint that may be overridden.
| Property | Value |
|---|---|
| Type | array |
| Required | No |
A schema for ware-specific configuration values exposed via the Config REST API (GET /bazaar/v1/config/{slug}). Each entry in the array is a field definition object. Admins can set these values through the Bazaar UI or via wp bazaar config.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
An absolute REST URL that Bazaar can query for search results when this ware is the active context. Must return JSON results in a format the Bazaar shell can display.
Bazaar runs these checks on upload and rejects the ware if any fail:
- File has a
.wpextension - File is a valid ZIP archive
-
manifest.jsonexists at the archive root -
name,slug, andversionare present non-empty strings -
slugmatches[a-z0-9-]+ -
slugis not already installed - The
entryfile exists in the archive - No PHP files anywhere in the archive (
.php,.phtml,.phar,.php5,.php7, etc.) - Archive contains no more than 2,000 files
- No symlinks or path-traversal entries (e.g.
../) - Compression ratio does not exceed 100:1 (zip-bomb guard)
- Total uncompressed size is under the configured limit (default 50 MB)
- Bump the version in
manifest.json - Re-package:
npm run package - Install with force:
wp bazaar install my-ware.wp --force
If your ware declares an updateUrl, Bazaar can check for and apply updates automatically:
wp bazaar outdated # list wares with newer versions available
wp bazaar update ledger # update to the latest version
wp bazaar update --all # update everythingThe URL should serve a JSON file like:
{
"version": "1.3.0",
"download_url": "https://registry.example.com/wares/ledger-1.3.0.wp",
"changelog": "Fixed date formatting on generated PDFs."
}Getting Started
Reference
Internals