Appearance
App surfaces
A package "app surface" is its browser-facing UI declaration. In the catalog, that's the webapp field. This is searchable metadata only — the live URL that serves the app comes from system.gateway.http, not from the catalog.
Webapp shape in the catalog
typescript
// SystemPackageRegistryActor.ts:30-37
webapp?: {
appName?: string;
displayName?: string;
description?: string;
shells: readonly string[];
icon?: string;
routePrefix?: string;
};Field-by-field
appName
The URL segment under /apps/<appName>/. Lowercase kebab-case: /^[a-z0-9][a-z0-9-]*$/.
Examples: chat, director, flowpad, inference-settings.
This name is what the gateway routes on; the catalog stores it for display and search.
shells
Where the app appears. Values:
| Shell | Where it shows |
|---|---|
platform | The HiveCast platform shell at /apps/web/ |
edge | The Matrix Edge device shell at /apps/edge/ |
developer | Developer-only views (Director, debug surfaces) |
A package may declare multiple shells. The shell decides whether to render the launcher card; the gateway routes the URL regardless.
displayName, description, icon
Free-form display fields. Used by the launcher in each shell. icon is typically an emoji (📚, 💬) or a relative asset path.
routePrefix
The full URL prefix. Typically /apps/<appName>/, but a package may override (rare). The catalog stores this verbatim from matrix.json.
Indexing rules
The reader (readMetadata in SystemPackageRegistryActor) folds an empty or missing webapp block into "no app surface": the field is absent in the indexed entry, not present-but-empty. This is important because search filters like shells: 'platform' skip records with no webapp.
Search hooks
| Filter | Match against |
|---|---|
shells: 'platform' | webapp.shells[] includes 'platform' (case-insensitive) |
text: 'chat' | searches webapp.appName, webapp.displayName, webapp.description, webapp.shells[] (among other fields) |
There is no filter for appName directly; use text if you need to search on it.
What this is NOT
- Not a gateway route. The catalog tells you a package declares an app. Whether the app is actually mounted, served, and routable is the gateway's responsibility (
system.gateway.http.gateway.http.status). - Not a launcher feed. The launcher pulls from
/api/apps, which is the gateway projection over live runtimes — not the catalog projection over installable packages. - Not a permission gate. The catalog field does not control who can open the app. Authorization is at the gateway and at the actor.
Cross-link to the gateway docs
The gateway side of this — how appName becomes a URL, how shells decide which launcher to show, how routes resolve — is covered in Hosting Gateway / App routes. The catalog side is the publication-time declaration; the gateway side is the serve-time route.
See also
Source:
projects/matrix-3/packages/system-catalog/src/SystemPackageRegistryActor.ts:30-37,WORKSTREAMS/core-and-packaging/MATRIX-DISCOVERY-METADATA-SPEC.md.