Skip to content

system.registry

system.registry is the logical mount claim table owned by the system runtime. It maps canonical mount names (chat.conversation, system.agents.memory) to the local mount + provider runtime ID + wire root that actually serve them. Director treats it as the primary source of the displayed catalog.

Ops Director consumes

registry.list (read)

DirectorRuntimeAdapter.listRegistryBindings (src/services/DirectorRuntimeAdapter.ts:234-287):

ts
const result = await RequestReply.execute(
  context,
  'system.registry',
  'registry.list',
  {},
  { timeoutMs: 5000, targetRoot: root },
) as {
  ok?: boolean,
  claims?: Array<{ logicalMount?: string; providers?: RegistryProviderEntry[] }>,
};
if (result?.ok !== true || !Array.isArray(result.claims)) {
  throw new Error('system.registry registry.list returned an invalid response');
}

Director then walks each claim, normalizes its providers, and emits a BindingRuleEntry:

ts
{
  match: <logicalMount>,                  // canonical mount, e.g. 'chat.conversation'
  ops?: string[],                         // optional op subset declared in metadata
  forward: <provider.localMount>,         // local mount the provider actually serves
  provider: <runtimeId>,                  // who serves it
  runtimeId,
  localMount,
  packageName?: string,
  runtimeWireRoot?: string,
  componentType?: string,
  description?: string,
  source?: string,
  hasChildren?: boolean,
}

Unlike system.runtimes, the registry call throws on bad responses instead of returning empty. That surfaces misconfigured registries explicitly rather than masking them as "nothing mounted".

Combined with system.runtimes

loadCatalogWithRuntimes runs registry.list and runtimes.registered in parallel (Promise.all, line 359-362), then buildCatalogFromBindings merges them. Each binding becomes a CatalogEntry; runtime inventory entries enrich type, purpose, and hasChildren when the runtime advertised them.

If the canonical catalog is empty, loadCatalogWithRuntimes returns { source: 'empty-registry', fallbackUsed: false } rather than synthesizing entries from runtime heartbeats alone — Director honestly shows an empty tree when the registry is empty.

Resolution semantics

resolveCanonicalTarget (src/services/DirectorRuntimeAdapter.ts:185-198) is how Director turns an operator-typed canonical mount into the actual (mount, targetRoot) pair to invoke. The chain:

  1. Read all bindings.
  2. resolveBoundTarget(canonicalMount, bindings, runtimes) finds a binding rule whose match equals the canonical mount.
  3. Returns { mount: <localMount>, targetRoot: <runtimeWireRoot or root> }.
  4. If no binding matches, throw Mount is not registered in system.registry: <canonicalMount>.

This is what makes the Detail tab's $introspect work even when the canonical mount differs from the local mount on the runtime.

Operator note

The registry is the contract. Two failure modes:

  • Registry empty. No runtime called runtimes.register on its claims. Check the runtime's startup log.
  • Registry has entries but tree is empty. The runtime is dead but the registry still has stale claims. Check system.runtimes runtimes.registered — if the runtime ID isn't there, the registry is stale and a fresh host.control runtimes.reconcile should clean it up.

See also

Source: projects/matrix-3/packages/director/src/services/DirectorRuntimeAdapter.ts:185-525 (resolution + catalog construction).