Skip to content

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:

ShellWhere it shows
platformThe HiveCast platform shell at /apps/web/
edgeThe Matrix Edge device shell at /apps/edge/
developerDeveloper-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

FilterMatch 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.

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.