Manifest Reference
Every field in a Brewser app manifest, and how it's generated
Every Brewser app ships a manifest.json describing its identity, the capabilities it needs, and
how it should behave on the console. This page is the field-by-field reference.
Brewser generates the manifest for you
You never author the published manifest by hand. You fill
in the submission form on brewser.io, and Brewser builds a
validated manifest.json from your answers. The manifest below is what comes out of that
process and ships inside your app on the SD card. It's useful to understand, but the form is where
you set these values.
The submission side validates every field against a strict schema (unknown fields are rejected), so a malformed manifest can't reach the catalogue.
A real manifest, annotated
This is the generated manifest for the Save Demo app
(com.natureglass.savedemo), lightly annotated:
{
"id": "com.natureglass.savedemo", // reverse-domain id: com.<publisher>.<app>
"name": "Save Demo", // shown in the catalogue
"version": "1.0.0", // semver
"description": "<p>...</p>", // rich text (a small HTML allowlist)
"summary": "A hands-on test harness for the Brewser save & leaderboard APIs.",
"logo": "assets/appbanner.jpg", // relative path inside your bundle
"entry": "index.html", // the first file Brewser opens
"categories": ["Apps", "Demos"], // one or more catalogue categories
"permissions": [], // declared capabilities (see below)
"compatibility": ["switch", "web"], // where the app is meant to run
"allowed_origins": [], // external hosts the app may reach
"developer": "@natureglass", // your publisher display name
"license": "MIT",
"tags": ["Leaderboards", "Save Game"],
"exitGame": "PLUS", // controller button that exits the app
"fullscreen": true, // app owns the whole screen
"hideMouseDocked": false, // software-cursor behaviour, docked
"hideMouseUndocked": false, // ... and handheld
"publishedAt": "2026-08-06T10:20:30Z" // set once, on first publish
}Field reference
| Field | Required | Type | Notes |
|---|---|---|---|
id | Yes | string | com.<publisher>.<app>, lowercase. Stable, so don't change it between versions. |
name | Yes | string | User-facing name. Don't impersonate other products. |
version | Yes | string | Semantic version, e.g. 1.2.0. |
description | Yes | string | Rich text with a small HTML allowlist (p, br, strong, em, ul/ol/li, code, a). |
summary | No | string | Short one-liner (≤100 chars) for cards. |
logo | Yes | string | Relative path to your icon/banner inside the bundle. |
entry | Yes | string | The first file Brewser opens. Defaults to index.html; must exist in the bundle. |
categories | Yes | string[] | One or more catalogue categories. |
permissions | Yes | string[] | Declared capabilities; see below. May be empty. |
compatibility | Yes | string[] | Where the app runs: switch, web, mobile. |
allowed_origins | Yes | string[] | External https?:// hosts the app may fetch/XHR/WebSocket. Media and image element loads are exempt — see below. Empty if it makes no network calls. |
user_origins | No | boolean | Set when the server address is chosen by the user at runtime. Undeclared origins then raise a one-time on-console approval instead of being denied — see below. |
developer | Yes | string | Your publisher display name. |
license | Yes | string | An SPDX-style license id (MIT, Apache-2.0, GPL-3.0, …). |
exitGame | Yes | string | The controller button that exits the app (see Controls). |
fullscreen | Yes | boolean | Whether the app takes over the whole screen. |
hideMouseDocked / hideMouseUndocked | No | boolean | Software-cursor visibility per dock state. |
freshProcessOnExit | No | boolean | Ask the runtime for a clean context on exit. |
publishedAt | Yes | string | ISO timestamp, set once at first publish and carried forward. |
tags | No | string[] | Free-form tags for discovery. |
genre / features | No | string[] | Curated genre and feature labels. |
buttonMapping | No | object | Maps keyboard keys to Switch controls; see Controls. |
Field values like
categories,license,genreandfeaturesare validated against curated lists on the platform, so the form offers you the valid choices rather than free text.
Permissions
permissions is a list of the capabilities your app declares up front. Users see them before
launching, and the runtime uses them to decide what your app may touch: network access, local
storage, device info, account access, and so on. Requesting a capability your app doesn't use only
adds friction, so declare the minimum you need. Apps that request nothing skip the permission
prompt entirely, so users can launch them directly.
Hardware access (USB, Bluetooth, and the other device APIs covered in
Hardware APIs) is declared here too. Declaring a peripheral says that
the app talks to hardware of a given kind. Choosing a specific device (filtering by USB
vendor/product id, or by Bluetooth service UUID) happens in your app's own requestDevice() call,
exactly as it does in Chrome. Those filters live in your JavaScript, not in the manifest.
The exact set of permission names is a curated list maintained by Brewser, so it can grow over time. If your app needs a capability you don't see offered, mention it in your submission.
Network needs two things
If your app reaches the internet, it needs both a network capability and every external host
listed in allowed_origins:
"allowed_origins": [
"https://api.example.com",
"https://cdn.example.com"
]The security scanner cross-checks the hosts your code actually contacts against this list; an undeclared host is flagged. See Networking in Apps for what the runtime can reach, and Security Review for how the check works.
WebSocket hosts count too. An app using Multiplayer declares the room service alongside its other hosts:
"allowed_origins": [
"wss://ws.brewser.io"
]Media and images are exempt. The list applies to what your code fetches — fetch,
XMLHttpRequest, WebSocket, and <script> / <link> / <iframe> loads. It does not apply to
<video>, <audio>, <source> (HLS included), <img> or CSS background-image. Those still
need the network capability, but they can load from any host.
That's there so a streaming app can pin what it knows without having to guess what it can't. A Twitch client declares its two API hosts and plays from whichever CDN edge the API hands back:
"permissions": ["network"],
"allowed_origins": ["https://gql.twitch.tv", "https://usher.ttvnw.net"]The split follows the API that issues the request, not the kind of bytes that come back. If you
fetch() an HLS playlist to parse it yourself, that host must be declared; only the URL you hand
to <video> is exempt.
Brewser's own *.brewser.io services (Saves, Leaderboards, the wss://ws.brewser.io relay) are
exempt too, over TLS only. You never declare those.
When the user picks the server: user_origins
Some apps can't declare anything, because the address is typed by the person using them — a
Jellyfin server, a Home Assistant box, a NAS. Set user_origins:
{
"permissions": ["local_network", "storage"],
"allowed_origins": [],
"user_origins": true
}Brewser then asks once, on the console, before your app reaches a host that isn't in
allowed_origins, and remembers the answer for that app. The prompt is drawn by the shell, not by
your page, and offers Allow (remembered), Just once, or Deny.
This is tighter than leaving allowed_origins empty, not looser. An empty list on its own
means no origin restriction at all; with user_origins set, an empty list means every origin
needs approval. That's why the security scanner stops flagging
network with an empty list once you opt in.
Nothing else changes: media and image element loads still never prompt, *.brewser.io still never
prompts, and a redirect to a host the user didn't approve is refused rather than re-asked.
What doesn't need declaring
Platform features like Saves & Leaderboards need nothing
in the manifest; they're not hardware. They work through the brewser.js SDK
and only depend on the user being signed in for their cloud parts.
Related
- Quickstart: where these values get filled in.
- Content & Submission Policy: what's allowed.
- Controls:
exitGameandbuttonMapping. - Multiplayer: declaring
wss://ws.brewser.iofor real-time apps.

Brewser Docs