Appearance
Registry vs catalog
The Matrix protocol distinguishes authority surfaces (which accept writes and own truth) from projection surfaces (which only read and must be derivable from authorities). Conflating them is the single most common source of inconsistency in package code.
The rule
Source:
WORKSTREAMS/core-and-packaging/MATRIX-AUTHORITY-MODEL.md§ Rules. Quoted: "Authority surfaces accept writes; projection surfaces are read-only.system.catalog,/api/apps, and/api/identity/runtime-summaryMUST NOT accept writes that bypass the authoritative actor. If you find a write landing in a projection surface, fix the call site to write through the authority instead."
The four authority actors
| Actor | What it owns | Key writes | Key reads |
|---|---|---|---|
system.runtimes | physical runtime/process inventory | runtimes.register, runtimes.deregister, runtimes.heartbeat | runtimes.list, runtimes.status, runtimes.registered |
system.registry | logical mount claims / providers | registry.register, registry.heartbeat, registry.unregister | registry.resolve, registry.list, registry.children, registry.providers |
system.devices | linked-Device inventory / presence | devices.heartbeat, devices.runtimes.register | devices.list, devices.get, devices.runtimes.list |
system.auth | namespace claims, host-link redemption | auth.namespace.claim, auth.hostLink.redeem, auth.hostLink.verifyHeartbeat | auth.namespace.resolve, auth.session.validate |
Plus host.control (per-Host supervisor) and system.factotum (credentials), each with their own writes/reads.
The catalog (read projection)
system.catalog does not own anything. It is a read projection over system.runtimes + system.registry + system.gateway.http + $introspect. Its full 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?' },
};There is no catalog.register, no catalog.update. The catalog cannot be "corrected" by writing to it — it can only be made consistent by writing to the authorities behind it.
The "physical vs logical" split
system.runtimes and system.registry answer different questions:
| Question | Answered by |
|---|---|
| What processes are running? | system.runtimes — physical process inventory |
Who serves chat.conversation? | system.registry — logical mount claims |
A runtime can register multiple claims. A claim can briefly outlive a runtime (until TTL). Director reads both to build the namespace tree with drill-down into runtime details.
This separation is critical: a process might be alive (runtimes.list returns it) but no longer claim the mounts it used to (registry.resolve won't find it). The catalog joins both views and reports the currently-resolvable state.
Why projections must be read-only
If a projection accepted writes, you'd have two writers for the same fact: the authority and the projection. They'd drift. Reconcile loops would chase phantom inconsistencies.
The rule keeps the protocol composable: any new read surface (a gateway projection, a dashboard view, an LLM agent's discovery feed) is correct by construction as long as it derives from authorities and never short-circuits a write through them.
/api/apps and /api/identity/runtime-summary
These are HTTP-side projections:
/api/apps— shell launcher feed. Derived fromsystem.gateway.httproute status, which is derived from runtime metadata. Not authoritative./api/identity/runtime-summary— dashboard composite. Session-scoped read ofsystem.devices+ device links + registered runtimes +/api/appsfor the signed-in principal. Not authoritative.
Both follow the same rule: writes go through actors; HTTP-side reads project on demand.
What about system.bindings?
system.bindings (projects/matrix-3/packages/system-bindings/) is a legacy/v14 compatibility surface. It accepts writes (bindings.register, bindings.remove) and is retained because some older deployments still use it. From MATRIX-AUTHORITY-MODEL.md:
"
system.bindingsretention decision: retain as explicitly documented v14 compatibility surface. Sunset when no production caller remains."
New product code MUST use system.registry instead. bindings.list may still appear in diagnostics output but must be visibly labeled "v14 compatibility" in any product surface that exposes it.
Singleton invariant
Some authority mounts are singletons under an authority root — exactly one live owner allowed. The list (verbatim from MATRIX-AUTHORITY-MODEL.md):
"
system.runtimes,system.registry,system.devices,system.auth,host.control."
Two matrix up calls for the same singleton on one Host = silent claim collision. The registry today does not actively reject the second writer; the second silently shadows the first. See Singleton claims.
See also
- Mount claims — the registry-side claim shape.
- Singleton claims — the singleton invariant.
- Discovery projections — what
system.catalogand/api/appsactually return. MATRIX-AUTHORITY-MODEL.md— full authority/projection table.