Appearance
App surfaces
The webapp block in matrix.json describes everything Host Service and the platform shells need to serve your package as an app: where to read static assets from, what URL to mount it under, what to call it in the launcher, which shell to put it in. This page is the field-by-field reference.
Full webapp block
json
"webapp": {
"distDir": "./dist/browser",
"entry": "index.html",
"appName": "chat",
"displayName": "Chat",
"icon": "💬",
"navOrder": 30,
"description": "Conversation and agent workspace",
"shells": ["platform", "edge"],
"base": "relative",
"routePrefix": "/apps/chat"
}Required fields
distDir
json
"distDir": "./dist/browser"Directory the gateway serves, relative to package root. Anything inside this directory is reachable under the app's route. The gateway does not serve files outside distDir. The directory must exist at runtime (i.e., your build script writes it before matrix run).
entry
json
"entry": "index.html"The HTML entry inside distDir that the gateway serves for <routePrefix>/. Conventionally index.html. The file must reference your bootstrapEntry and browserEntry JS modules (typically with <script type="module" src="./bootstrap.js"></script>).
appName
json
"appName": "chat"URL-safe slug. Default route prefix is /apps/<appName>/. Conventionally matches namespace so the URL and bus address line up: /apps/chat/ ↔ chat.* actors.
The launcher uses appName as the app's stable identifier; user-installable preferences (favourite apps, recent apps) keyed off this.
displayName
json
"displayName": "Chat"Human-readable name shown in the launcher tile. Localizable in tooling (the launcher reads its own translations layered over this default). Should be brief — under 20 characters reads cleanly.
shells
json
"shells": ["platform", "edge"]Array picking which shells include your app:
"platform"— matrix-web (account/HiveCast shell). Apps users see when logged into their account."edge"— matrix-edge (Device shell). Apps relevant when looking at a specific Device.
Most product apps (chat, flowpad, smithers, director) ship in both. Device-specific apps (host config, device diagnostics) ship in "edge" only. Account-only apps (billing, org admin) ship in "platform" only.
The shells read this field from each installed package's manifest at startup and add tiles accordingly.
Optional fields
icon
json
"icon": "💬"Emoji shown in the launcher. Single character / glyph. PNG/SVG support is target state.
Status: target state, not yet implemented. Image-based icons. Today launchers render the emoji directly.
navOrder
json
"navOrder": 30Sort key in the launcher (lower first). Conventional ranges in the tree:
| Range | Used by |
|---|---|
| 1-19 | Reserved for shell-internal navigation. |
| 20-29 | Director-class system tools. |
| 30-49 | Primary user apps (chat, flowpad). |
| 50-69 | Configuration apps (inference-settings). |
| 70-89 | Documentation apps (docs-hivecast, docs-sdk). |
| 90-99 | Diagnostic / dev-only apps. |
Picked by the maintainer of each package. Do not collide with existing values without coordination.
description
json
"description": "Conversation and agent workspace"Tooltip / launcher subtitle. One short sentence.
base
json
"base": "relative"Tells the bundler that built dist/ to use relative URLs in built assets. Important when a webapp is mounted under a non-root prefix (/apps/chat/). With base: "relative", the served HTML works behind any prefix.
The actual base value is set in the bundler config (e.g., vite.config.ts). The webapp.base: "relative" field is informational — it tells the runner the package was built relative-aware, which lets the runner mount it under arbitrary prefixes.
routePrefix
json
"routePrefix": "/apps/chat"Override the default /apps/<appName> route. Use when:
- You need a top-level path (e.g.,
/admin). - You need a different namespace (e.g.,
/v2/apps/chat). - The default conflicts with another package.
Without routePrefix, the default /apps/<appName>/ is used. The leading slash is required.
Working examples
Minimal webapp (projects/matrix-3/packages/director/matrix.json)
json
"webapp": {
"distDir": "dist",
"entry": "index.html",
"appName": "director",
"displayName": "Director",
"icon": "🧭",
"navOrder": 20,
"description": "Matrix actor hierarchy explorer",
"shells": ["platform", "edge"]
}Hybrid (projects/matrix-3/packages/chat/matrix.json)
json
"webapp": {
"distDir": "./dist/browser",
"entry": "index.html",
"appName": "chat",
"displayName": "Chat",
"icon": "💬",
"navOrder": 30,
"description": "Conversation and agent workspace",
"shells": ["platform", "edge"],
"base": "relative"
}Docs surface (projects/matrix-3/packages/docs-sdk/matrix.json)
json
"webapp": {
"appName": "docs-sdk",
"displayName": "Matrix SDK Docs",
"icon": "📚",
"navOrder": 93,
"description": "Build Matrix actors, components, and packages using the SDK. Stability levels, testing, and migration guides.",
"shells": ["platform"],
"routePrefix": "/apps/docs-sdk"
}This is the manifest of the very docs site you are reading — routePrefix is set explicitly, shells is platform-only, and navOrder: 93 puts it in the docs band.
What the gateway does
When Host Service starts a runtime with webapp declared:
- Reads
distDir,entry,routePrefix(or derives fromappName). - Allocates an HTTP port for the runtime.
- Asks
system-gateway-httpto add a route:GET <routePrefix>/<path>→ static file underdistDir. - Adds the package to the launcher's view via
system.catalog.
The gateway handles caching, content-type detection, and 404s for files outside distDir. SPA-style fallback (everything not found falls back to entry) is the default — required because client-side routers handle deep links.
Discovery from the platform shell
The platform shell (matrix-web) reads system.catalog.search({ kind: 'webapp', shells: ['platform'] }) (or equivalent), gets the list of apps with shells including "platform", and renders launcher tiles ordered by navOrder. Clicking a tile navigates to <routePrefix>/.
This is the closest thing the SDK has to an "app store" today: declare webapp in your matrix.json, install the package, and it appears in the launcher.
See also
- Package manifest — the rest of the manifest.
- Component packaging — UI-side concerns.
- Vertical Slice Rule — why your package owns its UI, not the shell.
Source:
projects/matrix-3/packages/{chat,director,docs-sdk,flowpad,inference-settings,smithers}/matrix.json— every webapp field exercised by at least one shipping package.