Appearance
Mounts
A mount is the dot-delimited logical name an actor occupies under an authority root. It is one of the three names every live thing has — and the most commonly conflated. Conflating mount with runtime identity, or with wire prefix, produces real bugs (see CLAUDE.md Rule 11 and the dashboard authority-mismatch incidents recorded in WORKSTREAMS/loose-ends/).
The mount path is the Service identity (Kubernetes-style). It is long-lived. The runtime that hosts it is the pod — replaceable. A DeploymentInstance owns a mount root and its component mounts; the runtime underneath can be swapped, restarted, or co-tenanted with other packages without callers having to know. See P1.40 for the docker-for-actors model in full.
Three names. Do not collapse them.
Source:
CLAUDE.mdRule 11 ("Bus addressing"). Quoted: "Three names every live thing has (do not collapse them)."
| Name | Example | Stable across | Owned by |
|---|---|---|---|
| Mount path (logical / Service identity) | system.runtimes, chat.conversation | runtime restarts, machine swaps | system.registry |
| Runtime identity (physical, the pod) | RUNTIME-HOST-CONTROL-PLANE, runtime://chat-dev-01 | this process only | system.runtimes |
| Wire prefix (transport) | COM.NIMBLETEC.RICHARD-SANTOMAURO | environment lifetime | environment config |
The mount answers "what actor?". The runtime identity answers "which process?". The wire prefix answers "on which NATS namespace?". A request to chat.conversation resolves through system.registry to whichever runtime is currently serving it, and is published on the local wire prefix's inbox subject for that mount.
Note on multi-package runtimes. A runtime is a substrate for actors, not "the runtime for one specific package." Multiple packages with compatible isolation declarations (
runtime.isolation: shared) can co-tenant in one runtime. Mount paths stay stable; the runtime identity behind them is an operational concern. SeeP1.40.
What "mount" means at runtime
When a runtime starts an actor, the runtime tells system.registry to register a logical mount claim mapping the mount to that runtime. Other actors discover the binding by name (registry.resolve {logicalMount: 'chat.conversation'}) and call ops on the mount without knowing which runtime serves it.
The op declarations on system.registry itself (verbatim from projects/matrix-3/packages/system-platform/src/ServiceRegistryActor.ts:175):
typescript
static override accepts = {
'service.register': { description: 'Register a service endpoint (name -> mount path)', name: 'string', mount: 'string' },
'service.resolve': { description: 'Resolve a service name to its mount path', name: 'string' },
'service.list': { description: 'List all registered service endpoints', includeAll: 'boolean?' },
'service.deregister': { description: 'Remove a service registration', name: 'string' },
'registry.register': { description: 'Register a live logical mount claim for a component/provider', logicalMount: 'string', componentId: 'string?', providerRuntimeId: 'string?', localMount: 'string?' },
'registry.heartbeat': { description: 'Refresh liveness for a logical mount claim/provider', logicalMount: 'string?', componentId: 'string?', providerRuntimeId: 'string?' },
'registry.unregister':{ description: 'Unregister logical mount claims for a component/provider', logicalMount: 'string?', componentId: 'string?', providerRuntimeId: 'string?' },
'registry.list': { description: 'List live logical mount claims', prefix: 'string?', includeStale: 'boolean?' },
'registry.resolve': { description: 'Resolve a logical mount to live providers', logicalMount: 'string', includeStale: 'boolean?' },
'registry.children': { description: 'List direct logical children under a prefix', prefix: 'string?', includeStale: 'boolean?' },
'registry.providers': { description: 'List providers for logical mounts', logicalMount: 'string?', includeStale: 'boolean?' },
};The registry.* ops are the product-level surface; service.* is a compatibility alias for older callers.
Mount path conventions
Mounts use lowercase dot-separated identifiers. Examples that appear in the code today:
system.runtimes
system.registry
system.gateway.http
system.factotum
system.inference.openai
chat
chat.conversation
chat.identity
director
flowpad
host.control
system.devicesMount names follow these rules:
- Lowercase, kebab-case allowed inside a token (
system.gateway.http,system.inference.openai). - Top-level tokens identify the package family (
system.*,chat.*,flowpad.*). - Children of the same actor share the parent prefix (
chat.conversation,chat.identity). - The mount path appears on the wire dot-joined to the root and the facet:
{root}.chat.conversation.$inbox.
Note: the local-mount path is a NATS subject identifier. It must obey NATS subject rules: no spaces, no leading dot, no trailing dot, no
*/>in identifiers (those are wildcards).
Singleton vs multi-provider mounts
Some mounts are singletons under an authority root — exactly one process may own them. From WORKSTREAMS/core-and-packaging/MATRIX-AUTHORITY-MODEL.md:
"Singleton authority mounts have one live owner per authority root.
system.runtimes,system.registry,system.devices,system.auth,host.control. Twomatrix upcalls for the same singleton on one Host = silent claim collision (see AGENTS.md § Worker Bootstrap Gotchas)."
Other mounts allow multiple providers (e.g., a workspace mount that several runtimes may serve). The registry's registry.providers op returns the full list. Resolution policy is up to the caller; today most callers take the first live provider.
Mount kind taxonomy
MATRIX-DISCOVERY-METADATA-SPEC.md distinguishes four kinds of "thing at a mount":
| Concept | Lives where |
|---|---|
MountedInstance | live actor at a mount, registered in system.registry, introspectable via $introspect |
ServiceEndpoint | a MountedInstance that exposes headless ops/events |
AppRoute | a MountedInstance with an HTTP route exposed via system.gateway.http |
WorkerEndpoint | background/session worker exposed by a runtime |
A mount may be all of these at once: an actor that handles ops, emits events, serves a webapp, and runs a background session.
Local mount vs logical mount
registry.register accepts both logicalMount and localMount. They are usually the same. The distinction matters when a runtime serves a logical name through a local forwarding path:
logicalMount: 'system.inference'— the public name everyone calls.localMount: 'system.inference.openai'— the actor inside the provider runtime that actually handles the call.
The registry consumer typically only cares about logicalMount; localMount is metadata used by the gateway and Director for drill-down.
Mount vs URL appName
Mounts are internal addresses. They are not URL segments. The HTTP gateway exposes packages under appName slugs (e.g., /apps/chat/, /apps/director/) which are declared in matrix.json.webapp.appName. The appName grammar is [a-z0-9][a-z0-9-]*; the mount grammar is [a-z0-9.-]+ (dotted). They are different namespaces.
Note: under public Spaces, the URL looks like
/<spacePath>/<appName>/, never/<mount>/<op>. Internal mount names are not user-facing. SeeWORKSTREAMS/core-and-packaging/MATRIX-DISCOVERY-METADATA-SPEC.mdfor the appName/spacePath grammars.
See also
- Subjects — how the mount appears in the wire grammar.
- Authority roots — what
{root}is. - Mount claims — claim mechanics.
- Registry vs catalog — the authority/projection split.
- Singleton claims — for mounts that allow only one live owner.