Appearance
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 MBFiles 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) fromindex.htmland setcache-control: public, max-age=31536000, immutableon 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:
| Mode | Configured by | Used by |
|---|---|---|
assetMount | webapp.assetMount in the runtime metadata (typically auto-derived as <appName>.http) | Production package runtimes that ship built assets in dist/browser/. |
origin | webapp.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.