1. Pack it
From your project, run:bun run pack does the same. The mentra CLI’s
mentra pack is the same command.) It builds your miniapp and writes build/:
Your own
miniapp.json must not declare bundle; pack adds it to the copy it
writes. See the CLI reference for signing.
2. Host it
Upload the contents ofbuild/ to any static host that serves HTTPS: GitHub
Pages, Cloudflare Pages or R2, S3, or a plain web server. The folder’s URL is
your miniapp URL, for example https://notes.example.com/.
For each release:
- Bump
versioninminiapp.json(semantic version, such as1.4.2). - Run
mentra-miniapp pack. - Upload the new ZIP first, and
miniapp.jsonlast, so your URL never names a ZIP that isn’t there yet. A ZIP never changes once published; each version is a new file.
ETag or Last-Modified header. The Mentra App asks
for miniapp.json conditionally, so an unchanged miniapp costs one small 304
response.
3. Match the package name to your domain
A package name is your miniapp’s identity, so it can only be served from a domain that owns it. The name must start with the reversed registrable domain of the URL, plus at least one more part:- A hyphen in a domain label is an underscore in the package name:
my-site.commay usecom.my_site.*. - A domain whose labels can’t become package-name parts, such as a label that starts with a digit, can’t serve a miniapp.
- Internationalized domains are written in their
xn--form. com.mentra.*is reserved for Mentra’s own domain,mentraglass.com.
301 or 308) from your miniapp URL to
another HTTPS URL on the same registrable domain moves the miniapp, and
installed copies update from the new URL. Moving to another domain means a new
package name.
4. Share it
Anyone with the URL can install it. They can paste the URL into Add a miniapp in the Mentra App, scan a QR code of it, or openhttps://apps.mentraglass.com/open?url=<your miniapp URL>. The install sheet
shows where the miniapp comes from before it installs.
Installed miniapps check for new releases when the Mentra App starts, when it
comes to the foreground, when a miniapp opens, and every 15 minutes while it
runs.
Age rating and listing
Two optional fields inminiapp.json describe your miniapp to people.
miniapp.json
ageRatingis one of4+,9+,13+,16+or18+. A miniapp rated above the Mentra App’s own App Store rating installs or opens only for a person who has told the Mentra App they are old enough. WithoutageRatinga miniapp is never gated. A value that isn’t one of the five counts as18+.listingholds the category for the Mentra Miniapp Store. The rest of a listing comes from fields you already have:name,descriptionandicon. A listing has no screenshots.
Get listed in the Mentra Miniapp Store
Every miniapp is unlisted by default: installable by anyone with its URL, and out of browse and search. To be listed:- Install your miniapp once in the Mentra App from its URL. That registers the URL with Mentra.
- Sign in to the Mentra Developer Console with your Mentra account and submit the URL under My miniapps.
- Mentra staff review the submission and approve or reject it. My miniapps shows where each of your submissions stands: pending review, listed, changes waiting for review, rejected (with the reason) or delisted.
name, description, icon, ageRating and
listing.category, exactly as your miniapp.json has them, and My miniapps
previews it that way. Private miniapps
and miniapps Mentra hasn’t verified can’t be listed.
Staff review a listing again when its text or icon changes, never for a code
update. Until they approve the change, the Store keeps showing the version they
approved. The Store sees an icon change when the icon’s URL changes, so give a
new image a new file name. Changing your listing means changing your
miniapp.json; there is nothing to edit in the Console. Check for changes now
looks at Mentra’s latest copy of your miniapp.json right away instead of waiting for the hourly check.
Private miniapps
A miniapp’s server can decide who gets its files, the way a website decides who gets its pages. Use@mentra/auth on the server that hosts them, with a worker
in front of the static files if your host is only static:
- A request without a valid Mentra token gets
401withWWW-Authenticate: Bearer realm="mentra", package="<packageName>". The Mentra App answers with a token for that package only, and retries. A server that never asks is public, and the Mentra App sends it no account, token or cookie. - A token your
authorizefunction refuses gets403. Onlytrueadmits a request. session.workspaceIdnames the person’s workspace only when that workspace assigned your miniapp through Fleet; otherwise it isnull.- Admitted responses are marked
Cache-Control: private, so shared caches never serve a gated file to someone else. - A private miniapp can’t be listed in the Mentra Miniapp Store.
workspaceId.
Build against your own machine
mentra-miniapp dev serves your project from your computer and prints a QR of
its local-network URL, http://<your-lan-ip>:<port>/. The Mentra App installs
it like any miniapp URL, shows it as “From your local network”, and reloads it
when you save. See the CLI reference.
A local-network URL has no domain to vouch for its package name, so a local build
could claim any name. Two rules make that useless:
- A local build keeps its data apart from any other install of the same package name.
- The miniapp tokens it gets are marked as a local build’s, and
@mentra/authrefuses them unless your backend allows local builds. Turn that on for your development backend only:
createMentraAuth({ packageName, allowLocalBuilds: true }). On a backend that
allows them, session.localBuild is true for a local build, and its
workspaceId is always null. A local build never gets system privileges.
What persists on the phone
A miniapp’ssession.storage, session.blob and its screen’s localStorage
(up to 5 MB) survive restarts. See Storage
for what the phone keeps and what it doesn’t.
Next steps
Distribution
Versioning, signing and installing test builds on your own phones.
The manifest
ageRating, listing and every other miniapp.json field.
