Appearance
Discovery projections
A discovery projection is a read-only view computed from authoritative state. Matrix has three first-class projections today: system.catalog, /api/apps, and /api/identity/runtime-summary. Each derives from the same authority sources but answers a different question.
Source:
WORKSTREAMS/core-and-packaging/MATRIX-AUTHORITY-MODEL.md§ "Authority / projection table". The rule is enforced everywhere: projections accept no writes; consistency comes from the authorities.
system.catalog
Canonical search and namespace projection. Composes system.runtimes, system.registry, system.gateway.http route status, and $introspect.
Op surface (verbatim from projects/matrix-3/packages/system-catalog/src/SystemCatalogActor.ts:140):
typescript
static override accepts: Record<string, unknown> = {
'catalog.list': { description: 'List canonical catalog entries derived from system.registry and system.runtimes', prefix: 'string?' },
'catalog.resolve': { description: 'Resolve one canonical catalog entry and include provider metadata', mount: 'string' },
'catalog.roots': { description: 'List top-level canonical catalog namespace roots', options: 'object?' },
'catalog.children': { description: 'List immediate child namespace segments under a canonical catalog prefix', prefix: 'string?' },
'catalog.tree': { description: 'Return the canonical catalog namespace tree', prefix: 'string?' },
'catalog.search': { description: 'Search live runtime catalog surfaces and actor/service mounts', text: 'string?', prefix: 'string?', mount: 'string?', packageName: 'string?', accepts: 'string?', emits: 'string?', skill: 'string?', kind: 'string?', inferredKind: 'string?', declaredKind: 'string?', shells: 'string?', status: 'running|stopped|all?' },
};catalog.search is the workhorse. Filter by:
| Filter | Matches against |
|---|---|
text | full-text on description, mount, package |
prefix | mount path prefix (chat., system.inference.) |
mount | exact mount |
packageName | the package owning the mount |
accepts | actors that accept this op (e.g., inference.start) |
emits | actors that emit this event |
skill | declared skill (from MatrixActor.skills) |
kind / inferredKind / declaredKind | actor kind classification |
shells | webapp shell role (platform, edge, developer) |
status | running / stopped / all |
Concrete catalog entry (from ISystemCatalogEntry in source):
typescript
{
mount: "system.inference.openai",
type: "OpenAIInferenceActor",
purpose: "OpenAI inference provider",
ops: ["inference.start", "inference.cancel"],
packageName: "@open-matrix/driver-openai",
providerCount: 1,
providers: [
{
provider: "rt-inference-openai",
runtimeId: "rt-inference-openai",
packageName: "@open-matrix/driver-openai",
localMount: "system.inference.openai",
runtimeRoot: "COM.NIMBLETEC.RICHARD-SANTOMAURO",
runtimeType: "service",
authorityRoot: "COM.NIMBLETEC.RICHARD-SANTOMAURO"
}
]
}/api/apps
HTTP-side shell launcher feed. Returns the list of webapp routes the gateway is currently serving, plus enough metadata for the shell to render a launcher (icon, displayName, navOrder, shells).
/api/apps is derived from system.gateway.http route metadata, which is itself derived from runtime registrations whose packages declare a webapp block in matrix.json. The rule (verbatim from MATRIX-AUTHORITY-MODEL.md):
"
/api/appsMUST be derivable from gateway routes that, in turn, derive from runtime metadata. No standalone hosted-apps store."
Per-entry shape (target):
json
{
"appName": "chat",
"displayName": "Matrix Chat",
"icon": "...",
"navOrder": 30,
"shells": ["platform", "edge"],
"routePrefix": "/apps/chat/",
"runtimeId": "rt-chat-dev-01",
"packageName": "@open-matrix/chat",
"description": "..."
}The shell calls /api/apps, builds the navbar, and uses the returned routePrefix for navigation.
/api/identity/runtime-summary
Dashboard composite. Session-scoped read of system.devices + device links + registered runtimes + /api/apps for the signed-in principal. Returns a single response with everything the dashboard needs to render the user's view of their devices and apps.
The rule (verbatim):
*"
/api/identity/runtime-summaryMUST be derivable fromsystem.devices
deviceLinks+ registered runtimes +/api/apps, scoped to the authenticated principal. No cross-tenant data."*
Cross-tenant boundary is enforced at the HTTP gateway: the principal in the session cookie governs which records appear.
What is NOT a projection
These look like they could be projections but are something else:
system.runtimes— authority. Owns the physical inventory; acceptsruntimes.register/runtimes.heartbeat.system.registry— authority. Owns the logical mount claims.system.devices— authority. Owns the linked-Device inventory.system.bindings— legacy compatibility surface that accepts writes. Not a projection.
A projection has zero register/update/store ops. If you see a write op on a "catalog" or "view" actor, it is misnamed (or ought to be moved to an authority).
Building a new projection
When you need a new view, the recipe is:
- Identify the authority surfaces it derives from (
system.runtimes/system.registry/system.gateway.http/ etc.). - Define the projection actor with read-only ops —
<view>.list,<view>.resolve,<view>.search. - Implement each read by composing authority reads on demand. No local write-through cache that another writer doesn't refresh.
- Document divergence: if the projection caches, document the staleness bound (e.g., "10 s stale max").
Do not create an authoritative system.apps or system.deployments registry without explicit ontology approval. The discovery-metadata spec (WORKSTREAMS/core-and-packaging/MATRIX-DISCOVERY-METADATA-SPEC.md) is the gate for new authority types.
See also
- Registry vs catalog — the rule.
- Mount claims — what feeds the catalog.
MATRIX-DISCOVERY-METADATA-SPEC.md— the discovery contract.MATRIX-AUTHORITY-MODEL.md— the authority/projection table.