Appearance
Mount claims (registry reference)
This page is the registry-side reference for mount claims. The conceptual overview is on Mount claims (addressing); this page is the op-by-op surface for system.registry.
Source:
projects/matrix-3/packages/system-platform/src/ServiceRegistryActor.ts. The actor is mounted atsystem.registryand exposes theregistry.*ops listed below.
The op surface (verbatim)
typescript
// ServiceRegistryActor.ts:175
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 service.* family is a v1 compatibility alias for named service endpoints (a single name → mount mapping). New code uses registry.* exclusively.
registry.register
Register a live mount claim. Called by a runtime when it mounts an actor that exposes a logical service.
Payload:
typescript
{
logicalMount: string; // public name, e.g. 'chat.conversation'
componentId?: string; // actor instance id (defaults to logicalMount)
componentType?: string; // class name (e.g. 'ChatConversationActor')
authorityRoot?: string; // owning Space root; defaults to caller's transport root
scope?: string; // optional sub-scope
providerRuntimeId?: string; // process serving this claim (defaults to caller's runtimeId)
runtimeWireRoot?: string; // wire prefix of that runtime
localMount?: string; // mount inside that runtime (defaults to logicalMount)
packageName?: string; // owning package
metadata?: object; // arbitrary; consumed by catalog
heartbeatTtlMs?: number; // default 30000 (DEFAULT_HEARTBEAT_TTL_MS)
}Reply:
typescript
{
ok: true,
entry: IRegistryView, // the stored claim, including status & expiresAt
}Side effect: emits registry.registered on $events.
registry.heartbeat
Refresh liveness on an existing claim. The runtime must call this within heartbeatTtlMs (default 30 s) or the claim transitions to stale.
Payload: any of logicalMount, componentId, providerRuntimeId, packageName. The actor uses these to identify the claim. The most common form is { providerRuntimeId } to refresh all claims for one runtime in one call.
Reply: { ok: true, refreshed: number } — number of claims actually refreshed.
Side effect: emits registry.heartbeat on $events.
registry.resolve
Look up live providers for a logical mount.
Payload:
typescript
{
logicalMount: string;
includeStale?: boolean; // default false
}Reply:
typescript
{
ok: true,
logicalMount: string,
providers: IRegistryView[], // each entry includes runtimeWireRoot, localMount, status
}If includeStale: true, the response includes claims past their TTL. Typical resolution code uses the first provider in live status:
typescript
const { providers } = await actor.invoke('system.registry', 'registry.resolve', {
logicalMount: 'chat.conversation',
});
const live = providers.filter(p => p.status === 'live');
const target = live[0]; // simple policy: pick the first
const targetSubject = `${target.runtimeWireRoot}/${target.localMount}/$inbox`;registry.list
List all live claims, optionally filtered by prefix.
Payload:
typescript
{
prefix?: string; // 'chat.' returns chat.* claims
includeStale?: boolean; // default false
}Reply:
typescript
{
ok: true,
count: number,
claims: IRegistryView[],
}registry.children
List direct logical children under a prefix. Used by Director and the catalog to render a tree.
Payload:
typescript
{
prefix?: string;
includeStale?: boolean;
}Reply:
typescript
{
ok: true,
prefix: string,
children: Array<{
mount: string,
segment: string,
isClaim: boolean,
providerCount: number,
hasChildren: boolean,
}>,
}registry.providers
List all providers for one or many logical mounts.
Payload:
typescript
{
logicalMount?: string;
includeStale?: boolean;
}Reply: { ok: true, providers: IRegistryView[] } — flat list across mounts (filtered if logicalMount is given).
registry.unregister
Remove claims for a runtime or a specific (mount, runtime) pair.
Payload: identifier(s) — providerRuntimeId (most common, removes all claims for one runtime) or specific logicalMount + providerRuntimeId.
Reply:
typescript
{ ok: true, removed: IRegistryView[] }Side effect: emits registry.unregistered on $events with the removed claims.
Heartbeat TTL
Default heartbeatTtlMs is 30 seconds (verbatim from ServiceRegistryActor.ts:119):
typescript
private static readonly DEFAULT_HEARTBEAT_TTL_MS = 30_000;Runtimes typically heartbeat every 10 seconds with a 30-second TTL, giving 3x headroom for transient delays.
Stale handling today
registry.list and registry.resolve filter out stale claims by default. There is no automatic eviction of stale records — they remain in the in-memory map until explicit unregister. A future eviction sweep is backlog material; today the catalog and Director treat status: 'stale' records as "was here, may be back".
What registry.register does NOT do
- It does not validate that no other provider already holds the mount. Two registrations for the same logical mount and different
providerRuntimeIdare accepted; resolution returns both. - It does not enforce singleton invariants. The authority-model rules require ops policy at the calling layer to detect double-claims on singletons. See Singleton claims.
- It does not check the runtime is alive. A registration without a heartbeat goes stale after
heartbeatTtlMs; the registry does not proactively detect dead runtimes.
See also
- Mount claims (addressing) — claim shape and lifecycle.
- Singleton claims — single-owner mounts.
- Instance claims — multi-provider mounts.
- Registry vs catalog — authority/projection rule.