Skip to content

Static assets

The gateway does not read static files directly. Each package's runtime mounts an asset endpoint actor (MatrixHttpAssetEndpointActor), and the gateway calls that actor over Matrix request/reply for the bytes.

Why this design

The gateway runs in one process. Package runtimes run in their own processes (via host-service). The gateway has no filesystem access to a package's dist/browser/ directory unless it shares the same process — which it usually does not.

Delegating the file read to the package's own runtime keeps the boundary clean: each package owns its own assets, and the gateway is purely a routing/relay layer.

Asset endpoint

MatrixHttpAssetEndpointActor (projects/matrix-3/packages/system-gateway-http/src/index.ts:80-219) mounts as <appName>.http (or system.runtimes.<runtimeId>.http if a runtime ID is provided).

typescript
static accepts = {
  'http.asset.status':  { ... },
  'http.asset.resolve': { pathname, appName? },
  'http.asset.request': { pathname, appName?, method?, headers? },
};

The endpoint's config is supplied at mount time:

typescript
interface IMatrixHttpAssetEndpointConfig {
  packageName: string;
  appName: string;
  distDir: string;            // absolute path resolved at mount
  entryFile: string;          // typically "index.html"
}

The package runtime fills these from its matrix.json.webapp block plus its own install path.

Path resolution

resolveStaticAssetPath (in index.ts) takes the request pathname and resolves it against distDir. Path-traversal protection is enforced:

  • .. is rejected.
  • The resolved file must stay inside distDir.
  • Decoded slash-in-segment ambiguity is rejected.

On miss for an SPA-style URL, the endpoint falls back to entryFile (typically index.html). The MatrixHttpAssetEndpointActor.onHttpAssetResolve returns either:

json
{ "ok": true, "packageName": "...", "appName": "...", "pathname": "...", "assetPath": "...", "filePath": "..." }
// or
{ "ok": false, "error": "No package asset matched the request" }

MIME types

The endpoint's MIME table:

typescript
// system-gateway-http/src/index.ts:14-24
const MIME_TYPES: Record<string, string> = {
  '.css':  'text/css; charset=utf-8',
  '.html': 'text/html; charset=utf-8',
  '.ico':  'image/x-icon',
  '.js':   'text/javascript; charset=utf-8',
  '.json': 'application/json; charset=utf-8',
  '.map':  'application/json; charset=utf-8',
  '.png':  'image/png',
  '.svg':  'image/svg+xml',
  '.txt':  'text/plain; charset=utf-8',
};

Anything else gets application/octet-stream. This list is intentionally narrow — Matrix's app surface today uses HTML/JS/CSS/images.

Body relay limit

typescript
const MAX_RELAY_BODY_BYTES = 10 * 1024 * 1024;   // 10 MB

Files larger than 10 MB cannot be relayed through one Matrix request/reply message. The asset endpoint returns:

json
{
  "ok": false,
  "statusCode": 502,
  "headers": { "content-type": "application/json; charset=utf-8" },
  "error": "Asset response exceeded relay limit of 10485760 bytes"
}

For large files (videos, big bundles), this limit forces a different design.

Status: target state — stream-based or content-addressed delivery. v0/v0.5 packages keep webapp bundles under 10 MB by code-splitting and hashed asset URLs. Large-blob delivery (model weights, video) is a later workstream.

Cache-control

The endpoint sets cache-control: no-cache on every response. This is intentional during pre-launch — fast invalidation matters more than CDN-level caching.

Status: target state — hashed-immutable cache-control. A future revision will distinguish hashed asset URLs (*.abc123.js) from index.html and set cache-control: public, max-age=31536000, immutable on the former. Tracked in the v0.5 launch hygiene set.

HEAD requests

GET and HEAD are both supported. HEAD returns the same headers but no body. Methods other than GET/HEAD return 405 with Allow: GET, HEAD.

Asset endpoint vs. origin-backed route

A webapp route can be served either by:

ModeConfigured byUsed by
assetMountwebapp.assetMount in the runtime metadata (typically auto-derived as <appName>.http)Production package runtimes that ship built assets in dist/browser/.
originwebapp.origin (e.g. http://127.0.0.1:5173)Local development with a Vite dev server.

The gateway's _resolveRoute prefers origin when present, else assetMount. The development-time Vite proxy is not the production path.

See also

Source: projects/matrix-3/packages/system-gateway-http/src/index.ts:80-219.