Skip to content

Space routes

A Matrix Space has a public path. Apps under that Space are reached at:

/<routeKey>/<appName>/<asset-path>
/<publicNamespace>/<appName>/<asset-path>

The two grammars are alternatives — both refer to the same Space, with publicNamespace = space.<routeKey> (the typed namespace form). RouteKey is a v1 alias for the user-facing "Space path" terminology.

RouteKey grammar

/<routeKey>/<appName>/<asset-path>

Where:

  • routeKey: dot-delimited DNS-label-style identifier, e.g. alt.stories.ghost-stories.funny.
  • appName: lowercase kebab-case.

Per parseRouteKeyWebappRoute in projects/matrix-3/packages/system-gateway-http/src/http-routing.ts:

typescript
const ROUTE_KEY_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/;

A routeKey must:

  • Match this regex.
  • NOT match the public-namespace pattern (space.*, domain.*, system.*) — those are typed namespaces handled separately.
  • NOT be in the reserved set: api, auth, login, logout, signup, setup, settings, apps, app, assets, static, branding, nats-ws, matrix, system, admin, root, support, docs, help, billing, status, healthz, well-known, favicon.ico, manifest.json, hivecast, open-matrix, me.

Public-namespace grammar

/<publicNamespace>/<appName>/<asset-path>
/<publicNamespace>/apps/<appName>/<asset-path>

Where publicNamespace is one of:

  • space.<routeKey> (the canonical typed form for a Space)
  • domain.<...> (reserved, target state for domain claims)
  • system.<...> (reserved, system namespace)
typescript
const PUBLIC_NAMESPACE_PATTERN =
  /^(space|domain|system)\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/;

Both forms (/<publicNamespace>/<appName>/ and /<publicNamespace>/apps/<appName>/) work. The first is the user-friendly form; the second forces the explicit apps/ segment that's safer for auto-generated tools.

Dispatch order

In _dispatch (http-listener.ts):

1. parsePublicNamespaceWebappRoute(path)   ← typed namespace tried first
2. parseRouteKeyWebappRoute(path)          ← routeKey alias second
3. parseExplicitAuthorityWebappRoute(path) ← email-prefixed third
4. /apps/<appName>/...                     ← generic app last

A path that parses as both a public namespace and a routeKey is dispatched as the public namespace.

Mapping to internal routes

When a Space route parses successfully, the URL is rewritten internally to /apps/<appName>/<asset-path> and the gateway issues an asset request as if it were a generic /apps/ URL. The Space identity is carried in metadata (the route record's routeKey / publicNamespace) for HTML rewriting, so emitted HTML keeps the Space-prefixed URL form.

HTML rewriting

When the asset response is HTML, the gateway rewrites every reference to /apps/<appName>/... to /<routeKey>/<appName>/... (or the public-namespace equivalent) so navigation stays inside the Space's URL prefix. rewriteWebappHtmlResponse (http-routing.ts:251-273) does this with simple prefix substitution.

This means: a package's static HTML can use plain /apps/chat/... references; the gateway rewrites them at serve time so the Space prefix is preserved.

Trailing-slash redirects

If the path is /<routeKey>/<appName> (no trailing slash, no asset path), the parser sets requiresTrailingSlashRedirect: true. The handler then issues a 302 to add the trailing slash. This avoids relative-URL breakage in the rendered HTML.

See also

Source: projects/matrix-3/packages/system-gateway-http/src/http-routing.ts:97-167, projects/matrix-3/packages/system-gateway-http/src/http-routing.ts:350-392.