1419 lines
47 KiB
TypeScript
Raw Permalink Normal View History

import * as fs from 'node:fs';
import * as path from 'node:path';
import { RequestReply } from '@open-matrix/core/engine/remoting/RequestReply.js';
import type { MatrixActor } from '@open-matrix/core/core/MatrixActor.js';
import { MatrixRuntime } from '@open-matrix/core/runtime/MatrixRuntime.js';
import {
countRuntimeDirectory,
publishRuntimePresence,
} from '@open-matrix/core';
import type {
MatrixMountCardinality,
MatrixPlacementClaimMode,
RuntimeInventoryPlacementMetadata,
} from '@open-matrix/contracts/placement';
import type { RuntimePresenceRecordV1 } from '@open-matrix/contracts/runtime-presence';
import type { ILoadedMatrixPackageEnvironment } from './package-environment.js';
import type { IResolvedRunnerTransportPlan } from './runner-transport.js';
import type {
ILoadedMatrixBindingDeclaration,
ILoadedMatrixServiceManifest,
} from './service-manifest.js';
import { readRunnerPlacementStandbyResults } from './runner-placement-policy.js';
import { BindingsRegistryActor } from '@open-matrix/system-bindings';
import { ServiceRegistryActor } from '@open-matrix/system-platform';
import { RuntimeManagerActor } from '@open-matrix/system-runtimes';
const RUNTIME_REGISTRY_MOUNT = 'system.runtimes';
const LOGICAL_REGISTRY_MOUNT = 'system.registry';
const BINDINGS_REGISTRY_MOUNT = 'system.bindings';
const RUNTIME_PRESENCE_TTL_MS = 30_000;
const LOCAL_CONTROL_PLANE_REQUEST_TIMEOUT_MS = 5_000;
const LINKED_CONTROL_PLANE_REQUEST_TIMEOUT_MS = 15_000;
interface IRuntimeInventoryEntry {
readonly mount: string;
readonly type?: string;
readonly runtimeId: string;
readonly claimKind: MatrixPlacementClaimMode;
readonly placement: RuntimeInventoryPlacementMetadata;
readonly description?: string;
readonly hasChildren?: boolean;
}
interface IPlacementDeclarationIndex {
readonly byMount: ReadonlyMap<string, MatrixMountCardinality>;
}
export interface IRunnerControlPlaneState {
readonly runtimeRegistryMount: string;
readonly logicalRegistryMount: string;
readonly bindingsRegistryMount: string;
readonly createdRuntimeRegistry: boolean;
readonly createdLogicalRegistry: boolean;
readonly createdBindingsRegistry: boolean;
}
export interface IRunnerRuntimeRegistration {
readonly runtimeId: string;
readonly runtimeRoot: string;
readonly presence: RuntimePresenceRecordV1;
readonly registryMount: string;
readonly logicalRegistryMount: string;
readonly controlTargetRoot?: string;
readonly protocolRequest?: boolean;
readonly runtimeMount?: string;
readonly controlMount?: string;
readonly created: boolean;
readonly actorCount: number;
readonly inventoryClaimCount: number;
readonly runtimeCount: number;
readonly registryOptional?: boolean;
/**
* Re-issue the inventory `registry.claim` calls and re-publish presence.
* Used by the heartbeat tick when `registry.claim.renew` reports the
* registry no longer has our claim (e.g. after the registry actor's
* in-memory `_claims` map was wiped by a SYSTEM runtime restart).
* Idempotent: `onRegistryClaim` preserves `registeredAt`/`claimedAt`
* when an existing entry is found.
*/
readonly reclaim: () => Promise<{ inventoryClaimCount: number }>;
}
export interface IRunnerRuntimeDeregistration {
readonly runtimeId: string;
readonly registryMount: string;
readonly removed: boolean;
readonly runtimeCount: number;
}
export interface IRunnerRuntimeHeartbeat {
readonly runtimeId: string;
readonly known: boolean;
}
export interface IRunnerWebappRouteMetadata {
readonly appName: string;
readonly routePrefix?: string;
readonly displayName?: string;
readonly icon?: string;
readonly navOrder?: number;
readonly description?: string;
readonly shells?: readonly string[];
readonly assetMount?: string;
readonly origin?: string;
readonly port?: number;
}
export interface IRunnerBindingRegistration {
readonly runtimeId: string;
readonly registryMount: string;
readonly logicalRegistryMount: string;
readonly controlTargetRoot?: string;
readonly protocolRequest?: boolean;
readonly registeredCount: number;
readonly registryClaimCount: number;
readonly registryOptional?: boolean;
readonly bindings: ReadonlyArray<{
readonly binding: string;
readonly localMount: string;
readonly ops: readonly string[];
readonly packageName: string;
}>;
/**
* Re-issue every server-side record this runner published at registration
* time: the canonical-binding `registry.claim` calls AND the
* `bindings.register` calls. Both are in-memory on their respective
* registry actors, so a single SYSTEM restart wipes them together. The
* heartbeat tick triggers reclaim when `registry.claim.renew` reports the
* registry no longer has our claims; bindings.register loss is treated as
* a co-occurring symptom of the same SYSTEM restart and reclaimed in
* lockstep. Both server-side ops are idempotent (onRegistryClaim
* preserves identity; bindings.register is keyed by provider+match), so
* calling reclaim() against a healthy registry is harmless.
*/
readonly reclaim: () => Promise<{
readonly registryClaimCount: number;
readonly bindingsRegisteredCount: number;
}>;
}
export interface IRunnerBindingDeregistration {
readonly runtimeId: string;
readonly registryMount: string;
readonly logicalRegistryMount: string;
readonly removedCount: number;
readonly registryRemovedCount: number;
}
export interface IRunnerBindingRenewal {
readonly runtimeId: string;
readonly refreshed: number;
}
interface ICanonicalBindingDefinition {
readonly binding: string;
readonly localMount: string;
readonly ops: readonly string[];
readonly packageName: string;
}
interface IRunnerHostPlacement {
readonly deviceId: string;
readonly hostId: string;
readonly hostName?: string;
readonly deviceSlug?: string;
}
interface IControlPlaneRequestOptions {
readonly timeoutMs: number;
readonly targetRoot?: string;
readonly protocolRequest?: boolean;
}
export async function ensureRunnerControlPlane(runtime: MatrixRuntime): Promise<IRunnerControlPlaneState> {
const createdRuntimeRegistry = await ensureRegistryActor(runtime, RUNTIME_REGISTRY_MOUNT, RuntimeManagerActor);
const createdLogicalRegistry = await ensureRegistryActor(runtime, LOGICAL_REGISTRY_MOUNT, ServiceRegistryActor);
const createdBindingsRegistry = await ensureRegistryActor(runtime, BINDINGS_REGISTRY_MOUNT, BindingsRegistryActor);
return {
runtimeRegistryMount: RUNTIME_REGISTRY_MOUNT,
logicalRegistryMount: LOGICAL_REGISTRY_MOUNT,
bindingsRegistryMount: BINDINGS_REGISTRY_MOUNT,
createdRuntimeRegistry,
createdLogicalRegistry,
createdBindingsRegistry,
};
}
export interface IEnsureRunnerControlPlaneOptions {
readonly createLocalRegistries?: boolean;
}
export async function ensureRunnerControlPlaneForMode(
runtime: MatrixRuntime,
options: IEnsureRunnerControlPlaneOptions = {},
): Promise<IRunnerControlPlaneState> {
const createLocalRegistries = options.createLocalRegistries ?? true;
const createdRuntimeRegistry = createLocalRegistries
? await ensureRegistryActor(runtime, RUNTIME_REGISTRY_MOUNT, RuntimeManagerActor)
: false;
const createdLogicalRegistry = createLocalRegistries
? await ensureRegistryActor(runtime, LOGICAL_REGISTRY_MOUNT, ServiceRegistryActor)
: false;
const createdBindingsRegistry = createLocalRegistries
? await ensureRegistryActor(runtime, BINDINGS_REGISTRY_MOUNT, BindingsRegistryActor)
: false;
return {
runtimeRegistryMount: RUNTIME_REGISTRY_MOUNT,
logicalRegistryMount: LOGICAL_REGISTRY_MOUNT,
bindingsRegistryMount: BINDINGS_REGISTRY_MOUNT,
createdRuntimeRegistry,
createdLogicalRegistry,
createdBindingsRegistry,
};
}
export async function registerRunnerRuntime(
runtime: MatrixRuntime,
manifest: ILoadedMatrixServiceManifest,
environment: ILoadedMatrixPackageEnvironment | undefined,
runnerTransport: IResolvedRunnerTransportPlan | undefined,
serviceMount?: string,
runtimeMount?: string,
controlMount?: string,
webappRoute?: IRunnerWebappRouteMetadata,
): Promise<IRunnerRuntimeRegistration> {
const controlPlane = await ensureRunnerControlPlaneForMode(runtime, {
createLocalRegistries: !isHostManagedEnvironment(environment),
});
const runtimeId = resolveRunnerRuntimeId(manifest, environment);
const runtimeRoot = readTransportRoot(runtime)
?? environment?.environment.runtime.root
?? manifest.root
?? runtimeId;
const hostPlacement = resolveRunnerHostPlacement(runtime, environment);
const registryOptional = isHostedAuthorityLeafEnvironment(runtime, environment);
const actorCount = runtime.getAllComponents().length;
const placementDeclarations = readPlacementDeclarations(manifest);
const requestOptions = controlPlaneRequestOptions(
runtime,
environment,
controlPlaneRequestTimeoutMs(environment),
);
const inventory = buildRuntimeInventory(runtime, runtimeId, placementDeclarations);
const presence = buildRuntimePresenceRecord({
runtimeId,
runtimeRoot,
manifest,
environment,
actorCount,
inventory,
serviceMount,
runtimeMount,
controlMount,
runnerTransport,
webappRoute,
hostPlacement,
});
const inventoryClaimCount = await registerRuntimeInventoryClaims(
runtime,
controlPlane.logicalRegistryMount,
manifest,
environment,
runtimeId,
runtimeRoot,
inventory,
serviceMount,
webappRoute,
hostPlacement,
registryOptional,
);
publishRuntimePresence(runtime.getRootContext(), presence, 'announce');
const runtimeCount = await tryReadRegisteredRuntimeCount(
runtime,
controlPlane.logicalRegistryMount,
);
// Closure for re-issuing the same inventory claims when the registry has
// forgotten us (renew returns renewed: 0). Captures everything needed —
// the runtime, the original manifest/environment, runtimeId, etc. —
// so the heartbeat tick can call reclaim() without re-resolving any
// identity. `onRegistryClaim` is idempotent for an existing entry, so
// calling reclaim() when the registry IS healthy is harmless.
const reclaim = async (): Promise<{ inventoryClaimCount: number }> => {
const freshActorCount = runtime.getAllComponents().length;
const freshInventory = buildRuntimeInventory(runtime, runtimeId, placementDeclarations);
const freshPresence = buildRuntimePresenceRecord({
runtimeId,
runtimeRoot,
manifest,
environment,
actorCount: freshActorCount,
inventory: freshInventory,
serviceMount,
runtimeMount,
controlMount,
runnerTransport,
webappRoute,
hostPlacement,
});
const reclaimedCount = await registerRuntimeInventoryClaims(
runtime,
controlPlane.logicalRegistryMount,
manifest,
environment,
runtimeId,
runtimeRoot,
freshInventory,
serviceMount,
webappRoute,
hostPlacement,
registryOptional,
);
// Re-announce presence so RuntimeManagerActor's _runtimes Map repopulates
// with us even if it was cleared by a SYSTEM restart.
publishRuntimePresence(runtime.getRootContext(), freshPresence, 'announce');
return { inventoryClaimCount: reclaimedCount };
};
return {
runtimeId,
runtimeRoot,
presence,
registryMount: controlPlane.runtimeRegistryMount,
logicalRegistryMount: controlPlane.logicalRegistryMount,
...(requestOptions.targetRoot ? { controlTargetRoot: requestOptions.targetRoot } : {}),
...(requestOptions.protocolRequest === true ? { protocolRequest: true } : {}),
...(runtimeMount ? { runtimeMount } : {}),
...(controlMount ? { controlMount } : {}),
created: true,
actorCount,
inventoryClaimCount,
runtimeCount,
...(registryOptional ? { registryOptional: true } : {}),
reclaim,
};
}
export async function deregisterRunnerRuntime(
runtime: MatrixRuntime,
registration: IRunnerRuntimeRegistration | undefined,
): Promise<IRunnerRuntimeDeregistration | undefined> {
if (!registration) {
return undefined;
}
if (registration.registryOptional === true) {
const departedAt = Date.now();
publishRuntimePresence(
runtime.getRootContext(),
{
...registration.presence,
lastHeartbeat: departedAt,
health: {
status: 'stopped',
updatedAt: departedAt,
},
},
'departed',
);
return {
runtimeId: registration.runtimeId,
registryMount: registration.registryMount,
removed: false,
runtimeCount: 0,
};
}
const registryReleased = await RequestReply.execute<Record<string, unknown>, { removed?: number }>(
runtime.getRootContext(),
registration.logicalRegistryMount,
'registry.release',
{ providerRuntimeId: registration.runtimeId },
registrationControlPlaneRequestOptions(registration, LOCAL_CONTROL_PLANE_REQUEST_TIMEOUT_MS),
);
const departedAt = Date.now();
publishRuntimePresence(
runtime.getRootContext(),
{
...registration.presence,
lastHeartbeat: departedAt,
health: {
status: 'stopped',
updatedAt: departedAt,
},
},
'departed',
);
const runtimeCount = await tryReadRegisteredRuntimeCount(
runtime,
registration.logicalRegistryMount,
);
return {
runtimeId: registration.runtimeId,
registryMount: registration.registryMount,
removed: typeof registryReleased.removed === 'number' && registryReleased.removed > 0,
runtimeCount,
};
}
async function tryReadRegisteredRuntimeCount(
runtime: MatrixRuntime,
_registryMount: string,
): Promise<number> {
// Slice 1b (control-actor-nav): runtime directory authority is
// `system.runtimes`, not the service registry. Read through the adapter.
try {
return await countRuntimeDirectory(runtime.getRootContext(), { includeStale: true });
} catch {
return 0;
}
}
export async function heartbeatRunnerRuntime(
runtime: MatrixRuntime,
registration: IRunnerRuntimeRegistration | undefined,
): Promise<IRunnerRuntimeHeartbeat | undefined> {
if (!registration) {
return undefined;
}
if (registration.registryOptional === true) {
const now = Date.now();
publishRuntimePresence(
runtime.getRootContext(),
{
...registration.presence,
inventory: rebuildRuntimeInventoryForHeartbeat(runtime, registration),
lastHeartbeat: now,
health: {
status: registration.presence.health?.status ?? 'ok',
...(registration.presence.health?.reason ? { reason: registration.presence.health.reason } : {}),
updatedAt: now,
},
},
'heartbeat',
);
return {
runtimeId: registration.runtimeId,
known: true,
};
}
const renewal = await RequestReply.execute<Record<string, unknown>, { renewed?: number }>(
runtime.getRootContext(),
registration.logicalRegistryMount,
'registry.claim.renew',
{
providerRuntimeId: registration.runtimeId,
ttlMs: 30_000,
},
registrationControlPlaneRequestOptions(registration, 5_000),
);
const now = Date.now();
publishRuntimePresence(
runtime.getRootContext(),
{
...registration.presence,
inventory: rebuildRuntimeInventoryForHeartbeat(runtime, registration),
lastHeartbeat: now,
health: {
status: registration.presence.health?.status ?? 'ok',
...(registration.presence.health?.reason ? { reason: registration.presence.health.reason } : {}),
updatedAt: now,
},
},
'heartbeat',
);
return {
runtimeId: registration.runtimeId,
known: typeof renewal.renewed === 'number' && renewal.renewed > 0,
};
}
export async function registerRunnerBindings(
runtime: MatrixRuntime,
manifest: ILoadedMatrixServiceManifest,
environment: ILoadedMatrixPackageEnvironment | undefined,
runtimeId: string,
serviceMount?: string,
): Promise<IRunnerBindingRegistration> {
const controlPlane = await ensureRunnerControlPlaneForMode(runtime, {
createLocalRegistries: !isHostManagedEnvironment(environment),
});
const nonCallableBindingMounts = await resolveNonCallableBindingMounts(runtime, manifest, serviceMount);
const bindings = deriveCanonicalBindings(manifest, environment, serviceMount)
.filter((binding) => !nonCallableBindingMounts.has(binding.localMount));
const placementDeclarations = readPlacementDeclarations(manifest);
const runtimeWireRoot = readTransportRoot(runtime)
?? environment?.environment.runtime.root
?? manifest.root
?? runtimeId;
const authorityRoot = environment?.environment.runtime.root
?? manifest.root
?? runtimeWireRoot;
const hostPlacement = resolveRunnerHostPlacement(runtime, environment);
const registryOptional = isHostedAuthorityLeafEnvironment(runtime, environment);
const timeoutMs = controlPlaneRequestTimeoutMs(environment);
const requestOptions = controlPlaneRequestOptions(runtime, environment, timeoutMs);
const publishBindingForwards = shouldPublishBindingForwards(environment);
const issueBindingClaims = async (): Promise<number> => {
let issued = 0;
for (const binding of bindings) {
const placement = placementForMount(
binding.localMount,
binding.binding,
resolveCardinalityForMount(placementDeclarations, binding.localMount),
true,
);
await RequestReply.execute(
runtime.getRootContext(),
controlPlane.logicalRegistryMount,
'registry.claim',
{
mount: binding.binding,
kind: 'actor',
componentId: `${binding.packageName}:${binding.binding}`,
componentType: 'actor',
authorityRoot,
scope: 'identity-relative',
providerRuntimeId: runtimeId,
runtimeWireRoot,
localMount: binding.localMount,
packageName: binding.packageName,
mode: placement.claimMode,
cardinality: placement.cardinality,
claimMode: placement.claimMode,
logicalMount: placement.logicalMount,
physicalMount: placement.physicalMount,
callable: placement.callable,
...registryClaimPlacement(hostPlacement),
metadata: {
ops: [...binding.ops],
source: 'runner-control-plane',
placement,
...hostPlacementMetadata(hostPlacement),
},
},
requestOptions,
);
issued += 1;
}
return issued;
};
const registerBindingForwards = async (): Promise<number> => {
if (!publishBindingForwards) {
return 0;
}
let registered = 0;
for (const binding of bindings) {
await RequestReply.execute(
runtime.getRootContext(),
controlPlane.bindingsRegistryMount,
'bindings.register',
{
provider: runtimeId,
runtimeId,
packageName: binding.packageName,
localMount: binding.localMount,
match: binding.binding,
ops: [...binding.ops],
forward: binding.localMount,
},
requestOptions,
);
registered += 1;
}
return registered;
};
const registryClaimCount = registryOptional ? 0 : await issueBindingClaims();
const bindingsRegisteredCount = registryOptional ? 0 : await registerBindingForwards();
// Reclaim re-issues every server-side record this runner published at
// registration time. Both registries (system.registry and system.bindings)
// store their state in-memory. A single SYSTEM restart wipes them
// together, so the heartbeat tick reclaims both in lockstep when
// registry.claim.renew reports loss.
const reclaim = async (): Promise<{
registryClaimCount: number;
bindingsRegisteredCount: number;
}> => {
const registryClaimCount = registryOptional ? 0 : await issueBindingClaims();
const bindingsRegisteredCount = registryOptional ? 0 : await registerBindingForwards();
return { registryClaimCount, bindingsRegisteredCount };
};
return {
runtimeId,
registryMount: controlPlane.bindingsRegistryMount,
logicalRegistryMount: controlPlane.logicalRegistryMount,
...(requestOptions.targetRoot ? { controlTargetRoot: requestOptions.targetRoot } : {}),
...(requestOptions.protocolRequest === true ? { protocolRequest: true } : {}),
registeredCount: bindingsRegisteredCount,
registryClaimCount,
...(registryOptional ? { registryOptional: true } : {}),
bindings,
reclaim,
};
}
export async function deregisterRunnerBindings(
runtime: MatrixRuntime,
registration: IRunnerBindingRegistration | undefined,
): Promise<IRunnerBindingDeregistration | undefined> {
if (!registration) {
return undefined;
}
if (registration.registeredCount === 0) {
return {
runtimeId: registration.runtimeId,
registryMount: registration.registryMount,
logicalRegistryMount: registration.logicalRegistryMount,
removedCount: 0,
registryRemovedCount: 0,
};
}
const removed = await RequestReply.execute(
runtime.getRootContext(),
registration.registryMount,
'bindings.remove',
{ provider: registration.runtimeId },
registrationControlPlaneRequestOptions(registration, 5_000),
) as { removed?: number };
return {
runtimeId: registration.runtimeId,
registryMount: registration.registryMount,
logicalRegistryMount: registration.logicalRegistryMount,
removedCount: typeof removed.removed === 'number' ? removed.removed : 0,
registryRemovedCount: 0,
};
}
export async function renewRunnerBindings(
runtime: MatrixRuntime,
registration: IRunnerBindingRegistration | undefined,
): Promise<IRunnerBindingRenewal | undefined> {
if (!registration) {
return undefined;
}
if (registration.registryOptional === true) {
return {
runtimeId: registration.runtimeId,
refreshed: registration.bindings.length,
};
}
const renewal = await RequestReply.execute(
runtime.getRootContext(),
registration.logicalRegistryMount,
'registry.claim.renew',
{
packageName: registration.bindings[0]?.packageName,
providerRuntimeId: registration.runtimeId,
},
registrationControlPlaneRequestOptions(registration, 5_000),
) as { renewed?: number };
return {
runtimeId: registration.runtimeId,
refreshed: typeof renewal.renewed === 'number' ? renewal.renewed : 0,
};
}
export function resolveRunnerRuntimeId(
manifest: ILoadedMatrixServiceManifest,
environment: ILoadedMatrixPackageEnvironment | undefined,
): string {
const runtimeId = environment?.environment.runtime.runtimeId?.trim();
if (runtimeId) {
return runtimeId;
}
const manifestId = manifest.serviceInstance.id.trim();
if (manifestId.length > 0) {
return manifestId;
}
return manifest.packageName;
}
function isHostManagedEnvironment(environment: ILoadedMatrixPackageEnvironment | undefined): boolean {
const matrixDir = environment?.environment.host?.matrixDir;
return typeof matrixDir === 'string' && matrixDir.trim().length > 0;
}
function isHostedAuthorityLeafEnvironment(
runtime: MatrixRuntime,
environment: ILoadedMatrixPackageEnvironment | undefined,
): boolean {
if (!isHostManagedEnvironment(environment)) {
return false;
}
const topology = readRecord(runtime.getRootContext().getService<unknown>('matrix-node-topology'))
?? readRecord(environment?.environment.topology);
const roles = readRecord(topology?.roles);
const authorityCoordinator = readRecord(roles?.authorityCoordinator);
if (authorityCoordinator?.active === false) {
return true;
}
const hostHome = trimToUndefined(runtime.getRootContext().getService<unknown>('matrix-host-home'))
?? trimToUndefined(environment?.environment.host?.matrixDir);
if (!hostHome) {
return false;
}
const linkRecord = readJsonObjectIfPresent(path.join(hostHome, 'credentials', 'hivecast-link.json'));
return linkRecord?.authorityCoordinator === false
&& Boolean(trimToUndefined(linkRecord.deviceRegistryRoot));
}
function readTransportRoot(runtime: MatrixRuntime): string | undefined {
const root = (runtime.transport as { root?: unknown }).root;
return typeof root === 'string' && root.trim().length > 0 ? root.trim() : undefined;
}
async function resolveNonCallableBindingMounts(
runtime: MatrixRuntime,
manifest: ILoadedMatrixServiceManifest,
serviceMount: string | undefined,
): Promise<ReadonlySet<string>> {
const mounts = new Set<string>();
for (const standby of readRunnerPlacementStandbyResults(runtime)) {
if (standby.placement.callable === false) {
mounts.add(standby.mount);
}
}
if (manifest.packageName === '@open-matrix/system') {
const systemMount = serviceMount?.trim() || manifest.mount?.trim() || manifest.packageRoot?.mount?.trim() || 'system';
const response = await RequestReply.execute<Record<string, unknown>, {
readonly children?: readonly unknown[];
}>(
runtime.getRootContext(),
systemMount,
'system.children',
{},
{ timeoutMs: 2_000 },
);
for (const child of response.children ?? []) {
const record = isRecord(child) ? child : null;
const mount = trimToUndefined(record?.mount);
const status = trimToUndefined(record?.status);
if (mount && status !== 'mounted') {
mounts.add(mount);
}
}
}
return mounts;
}
function buildRuntimeInventory(
runtime: MatrixRuntime,
runtimeId: string,
placementDeclarations: IPlacementDeclarationIndex,
): IRuntimeInventoryEntry[] {
const inventory: IRuntimeInventoryEntry[] = [];
for (const component of runtime.getAllComponents()) {
const mount = component.mount.trim();
if (mount.length === 0) {
continue;
}
const placement = placementForMount(
mount,
mount,
resolveCardinalityForMount(placementDeclarations, mount),
true,
);
const description = typeof (component.constructor as { description?: unknown }).description === 'string'
? (component.constructor as { description?: string }).description
: undefined;
inventory.push({
mount,
...(component.constructor.name ? { type: component.constructor.name } : {}),
runtimeId,
claimKind: placement.claimMode,
placement,
...(description ? { description } : {}),
...(hasIntrospectChildren(component) ? { hasChildren: true } : {}),
});
}
appendStandbyPlacementResults(inventory, runtime, runtimeId);
return inventory;
}
function rebuildRuntimeInventoryForHeartbeat(
runtime: MatrixRuntime,
registration: IRunnerRuntimeRegistration,
): IRuntimeInventoryEntry[] {
const previousPlacementByMount = new Map<string, RuntimeInventoryPlacementMetadata>();
for (const entry of registration.presence.inventory ?? []) {
if (entry.placement) {
previousPlacementByMount.set(entry.mount, entry.placement);
}
}
const inventory: IRuntimeInventoryEntry[] = [];
for (const component of runtime.getAllComponents()) {
const mount = component.mount.trim();
if (mount.length === 0) {
continue;
}
const previousPlacement = previousPlacementByMount.get(mount)
?? placementForMount(mount, mount, 'named-instance', true);
const description = typeof (component.constructor as { description?: unknown }).description === 'string'
? (component.constructor as { description?: string }).description
: undefined;
inventory.push({
mount,
...(component.constructor.name ? { type: component.constructor.name } : {}),
runtimeId: registration.runtimeId,
claimKind: previousPlacement.claimMode,
placement: previousPlacement,
...(description ? { description } : {}),
...(hasIntrospectChildren(component) ? { hasChildren: true } : {}),
});
}
appendStandbyPlacementResults(inventory, runtime, registration.runtimeId);
return inventory;
}
function appendStandbyPlacementResults(
inventory: IRuntimeInventoryEntry[],
runtime: MatrixRuntime,
runtimeId: string,
): void {
const seen = new Set(inventory.map((entry) => entry.mount));
for (const standby of readRunnerPlacementStandbyResults(runtime)) {
if (standby.runtimeId !== runtimeId || seen.has(standby.mount)) {
continue;
}
inventory.push({
mount: standby.mount,
runtimeId,
claimKind: standby.placement.claimMode,
placement: standby.placement,
});
seen.add(standby.mount);
}
}
function buildRuntimePresenceRecord(params: {
runtimeId: string;
runtimeRoot: string;
manifest: ILoadedMatrixServiceManifest;
environment: ILoadedMatrixPackageEnvironment | undefined;
actorCount: number;
inventory: readonly IRuntimeInventoryEntry[];
serviceMount: string | undefined;
runtimeMount: string | undefined;
controlMount: string | undefined;
runnerTransport: IResolvedRunnerTransportPlan | undefined;
webappRoute: IRunnerWebappRouteMetadata | undefined;
hostPlacement: IRunnerHostPlacement | undefined;
}): RuntimePresenceRecordV1 {
const now = Date.now();
const namespaceRoot = params.runtimeRoot;
return {
version: '1',
namespaceRoot,
runtimeId: params.runtimeId,
runtimeType: 'folder',
runtimeRoot: params.runtimeRoot,
authorityRoot: namespaceRoot,
app: params.manifest.packageName,
claims: buildRuntimeClaims(params.serviceMount, params.environment),
inventoryMode: 'embedded',
inventory: [...params.inventory],
...(params.controlMount ? { controlMount: params.controlMount } : {}),
heartbeatTtlMs: RUNTIME_PRESENCE_TTL_MS,
registeredAt: now,
lastHeartbeat: now,
health: {
status: 'ok',
updatedAt: now,
},
metadata: {
source: 'mx-runner-runtime-presence',
actorCount: params.actorCount,
packageDir: params.manifest.packageDir,
packageName: params.manifest.packageName,
// Runtime displayName is set by host-service at spawn (--name or
// generated). The host passes it via env so the runtime can publish
// it in its registry presence record. Consumers read this field
// directly; no derivation from package or runtimeId.
...(process.env.MATRIX_RUNTIME_DISPLAY_NAME ? { displayName: process.env.MATRIX_RUNTIME_DISPLAY_NAME } : {}),
...(params.environment ? {
environmentName: params.environment.envName,
environmentPath: params.environment.environmentPath,
} : {}),
...(params.runtimeMount ? { runtimeMount: params.runtimeMount } : {}),
...(params.manifest.mount ? { serviceMount: params.manifest.mount } : {}),
...(params.environment?.environment.package?.publicRoot ? {
publicRoot: params.environment.environment.package.publicRoot,
} : {}),
...(params.runnerTransport ? { runnerTransport: params.runnerTransport } : {}),
...(params.webappRoute ? { webapp: params.webappRoute } : {}),
...hostPlacementMetadata(params.hostPlacement),
},
};
}
function hasIntrospectChildren(component: MatrixActor): boolean {
const constructor = component.constructor as { readonly hasDynamicChildren?: unknown };
if (constructor.hasDynamicChildren === true) {
return true;
}
try {
const introspection = component.on$introspect({ depth: 'basic' });
return Array.isArray(introspection.children) && introspection.children.length > 0;
} catch {
return false;
}
}
async function registerRuntimeInventoryClaims(
runtime: MatrixRuntime,
logicalRegistryMount: string,
manifest: ILoadedMatrixServiceManifest,
environment: ILoadedMatrixPackageEnvironment | undefined,
runtimeId: string,
runtimeWireRoot: string,
inventory: readonly IRuntimeInventoryEntry[],
_serviceMount: string | undefined,
webappRoute: IRunnerWebappRouteMetadata | undefined,
hostPlacement: IRunnerHostPlacement | undefined,
registryOptional: boolean,
): Promise<number> {
if (registryOptional) {
return 0;
}
const authorityRoot = environment?.environment.runtime.root
?? manifest.root
?? runtimeWireRoot;
const timeoutMs = controlPlaneRequestTimeoutMs(environment);
const requestOptions = controlPlaneRequestOptions(runtime, environment, timeoutMs);
let registered = 0;
for (const entry of inventory) {
const logicalMount = entry.mount.trim();
if (entry.placement.callable === false) {
continue;
}
if (!shouldRegisterRuntimeInventoryClaim(runtimeId, logicalMount)) {
continue;
}
await RequestReply.execute(
runtime.getRootContext(),
logicalRegistryMount,
'registry.claim',
{
mount: logicalMount,
kind: 'actor',
componentId: `${manifest.packageName}:inventory:${logicalMount}`,
componentType: entry.type ?? 'actor',
authorityRoot,
scope: 'identity-relative',
providerRuntimeId: runtimeId,
runtimeWireRoot,
localMount: logicalMount,
packageName: manifest.packageName,
mode: entry.placement.claimMode,
cardinality: entry.placement.cardinality,
claimMode: entry.placement.claimMode,
logicalMount: entry.placement.logicalMount,
physicalMount: entry.placement.physicalMount,
callable: entry.placement.callable,
...(entry.placement.standbyReason ? { standbyReason: entry.placement.standbyReason } : {}),
...(typeof entry.placement.placementEpoch === 'number' ? { placementEpoch: entry.placement.placementEpoch } : {}),
...registryClaimPlacement(hostPlacement),
ttlMs: 30_000,
metadata: {
source: 'runner-runtime-inventory',
placement: entry.placement,
...(entry.description ? { description: entry.description } : {}),
...(entry.hasChildren === true ? { hasChildren: true } : {}),
...hostPlacementMetadata(hostPlacement),
},
},
requestOptions,
);
registered++;
}
if (webappRoute) {
registered += await registerWebappSurfaceClaim(
runtime,
logicalRegistryMount,
manifest,
authorityRoot,
runtimeId,
runtimeWireRoot,
webappRoute,
hostPlacement,
requestOptions,
);
}
return registered;
}
async function registerWebappSurfaceClaim(
runtime: MatrixRuntime,
logicalRegistryMount: string,
manifest: ILoadedMatrixServiceManifest,
authorityRoot: string,
runtimeId: string,
runtimeWireRoot: string,
webappRoute: IRunnerWebappRouteMetadata,
hostPlacement: IRunnerHostPlacement | undefined,
requestOptions: IControlPlaneRequestOptions,
): Promise<number> {
const appName = webappRoute.appName.trim();
if (!appName) {
return 0;
}
const routePrefix = normalizeWebappRoutePrefix(webappRoute.routePrefix, appName);
const placement = placementForMount(
webappRoute.assetMount ?? appName,
appName,
'named-instance',
true,
);
const displayName = readOptionalString(webappRoute.displayName);
const icon = readOptionalString(webappRoute.icon);
const description = readOptionalString(webappRoute.description);
const shells = [...new Set((webappRoute.shells ?? []).map((entry) => entry.trim()).filter((entry) => entry.length > 0))];
await RequestReply.execute(
runtime.getRootContext(),
logicalRegistryMount,
'registry.claim',
{
mount: appName,
kind: 'app',
componentId: `${manifest.packageName}:webapp:${appName}`,
componentType: 'webapp',
authorityRoot,
scope: 'identity-relative',
providerRuntimeId: runtimeId,
runtimeWireRoot,
localMount: webappRoute.assetMount ?? appName,
packageName: manifest.packageName,
runtimeId,
packageId: manifest.packageName,
packageRef: manifest.packageName,
mode: placement.claimMode,
cardinality: placement.cardinality,
claimMode: placement.claimMode,
logicalMount: placement.logicalMount,
physicalMount: placement.physicalMount,
callable: placement.callable,
...registryClaimPlacement(hostPlacement),
ttlMs: 30_000,
app: {
route: routePrefix,
title: displayName ?? titleFromAppName(appName),
...(icon ? { icon } : {}),
surface: 'page',
openable: true,
appName,
},
metadata: {
source: 'runner-webapp-surface',
placement,
routeKind: webappRoute.assetMount ? 'matrix-asset-endpoint' : 'standalone-webapp',
...(description ? { description } : {}),
...(shells.length > 0 ? { shells } : {}),
...(typeof webappRoute.navOrder === 'number' && Number.isFinite(webappRoute.navOrder) ? { navOrder: webappRoute.navOrder } : {}),
...(webappRoute.assetMount ? { assetMount: webappRoute.assetMount } : {}),
...(webappRoute.origin ? { origin: webappRoute.origin } : {}),
...(typeof webappRoute.port === 'number' ? { port: webappRoute.port } : {}),
...hostPlacementMetadata(hostPlacement),
},
},
requestOptions,
);
return 1;
}
function controlPlaneRequestOptions(
runtime: MatrixRuntime,
environment: ILoadedMatrixPackageEnvironment | undefined,
timeoutMs: number,
): IControlPlaneRequestOptions {
const targetRoot = environment?.environment.runtime.root
?? readTransportRoot(runtime);
const protocolRequest = isHostManagedEnvironment(environment)
&& typeof environment?.environment.nats?.credentialsRef === 'string'
&& environment.environment.nats.credentialsRef.trim().length > 0;
return {
timeoutMs,
...(targetRoot ? { targetRoot } : {}),
...(protocolRequest ? { protocolRequest: true } : {}),
};
}
function registrationControlPlaneRequestOptions(
registration: Pick<IRunnerRuntimeRegistration | IRunnerBindingRegistration, 'controlTargetRoot' | 'protocolRequest'>,
timeoutMs: number,
): IControlPlaneRequestOptions {
return {
timeoutMs,
...(registration.controlTargetRoot ? { targetRoot: registration.controlTargetRoot } : {}),
...(registration.protocolRequest === true ? { protocolRequest: true } : {}),
};
}
function shouldPublishBindingForwards(environment: ILoadedMatrixPackageEnvironment | undefined): boolean {
return !isHostManagedEnvironment(environment);
}
function controlPlaneRequestTimeoutMs(environment: ILoadedMatrixPackageEnvironment | undefined): number {
return environment?.environment.nats?.credentialsRef
? LINKED_CONTROL_PLANE_REQUEST_TIMEOUT_MS
: LOCAL_CONTROL_PLANE_REQUEST_TIMEOUT_MS;
}
function normalizeWebappRoutePrefix(value: string | undefined, appName: string): string {
const routePrefix = value?.trim() || `/apps/${appName}/`;
return routePrefix.endsWith('/') ? routePrefix : `${routePrefix}/`;
}
function readOptionalString(value: string | undefined): string | undefined {
const trimmed = value?.trim() ?? '';
return trimmed.length > 0 ? trimmed : undefined;
}
function titleFromAppName(appName: string): string {
return appName
.split(/[-_.]/)
.filter(Boolean)
.map((segment) => `${segment.charAt(0).toUpperCase()}${segment.slice(1)}`)
.join(' ');
}
export function shouldRegisterRuntimeInventoryClaim(runtimeId: string, logicalMount: string): boolean {
const trimmedRuntimeId = runtimeId.trim();
const trimmedMount = logicalMount.trim();
if (!trimmedRuntimeId || !trimmedMount) {
return false;
}
const runtimeMount = `system.runtimes.${trimmedRuntimeId}`;
if (trimmedMount === runtimeMount || trimmedMount.startsWith(`${runtimeMount}.`)) {
return false;
}
return true;
}
function buildRuntimeClaims(
serviceMount: string | undefined,
environment: ILoadedMatrixPackageEnvironment | undefined,
): Array<{ prefix: string; kind: 'authoritative' }> {
const claims = new Set<string>();
const publicRoot = environment?.environment.package?.publicRoot?.trim();
if (publicRoot) {
claims.add(publicRoot);
}
const resolvedServiceMount = serviceMount?.trim();
if (resolvedServiceMount) {
claims.add(resolvedServiceMount);
}
return [...claims].map((prefix) => ({ prefix, kind: 'authoritative' as const }));
}
function deriveCanonicalBindings(
manifest: ILoadedMatrixServiceManifest,
environment: ILoadedMatrixPackageEnvironment | undefined,
serviceMount: string | undefined,
): ICanonicalBindingDefinition[] {
const bindings = new Map<string, ICanonicalBindingDefinition>();
const packageName = manifest.packageName;
const publicRoot = trimToUndefined(environment?.environment.package?.publicRoot)
?? trimToUndefined(manifest.packageNamespace);
const resolvedServiceMount = trimToUndefined(serviceMount)
?? trimToUndefined(manifest.mount);
const rootKind = trimToUndefined(manifest.serviceInstance.rootKind) ?? 'package-root';
if (rootKind === 'package-root' && manifest.packageRoot) {
const canonicalRoot = resolveCanonicalRootBinding(manifest.packageRoot, publicRoot);
const localRootMount = resolvedServiceMount
?? resolveLocalRootMount(manifest.packageRoot, publicRoot);
if (canonicalRoot && localRootMount) {
bindings.set(canonicalRoot, {
binding: canonicalRoot,
localMount: localRootMount,
ops: manifest.packageRoot.accepts,
packageName,
});
}
}
for (const declaration of manifest.packageComponents) {
const binding = resolveCanonicalComponentBinding(declaration, publicRoot);
const localMount = resolveLocalComponentMount(declaration, resolvedServiceMount, rootKind);
if (!binding || !localMount) {
continue;
}
bindings.set(binding, {
binding,
localMount,
ops: declaration.accepts,
packageName,
});
}
return [...bindings.values()];
}
function resolveCanonicalRootBinding(
declaration: ILoadedMatrixBindingDeclaration,
publicRoot: string | undefined,
): string | undefined {
if (isExplicitAbsoluteMount(declaration.mount)) {
return declaration.mount;
}
return publicRoot ?? declaration.mount;
}
function resolveCanonicalComponentBinding(
declaration: ILoadedMatrixBindingDeclaration,
publicRoot: string | undefined,
): string | undefined {
if (isExplicitAbsoluteMount(declaration.mount)) {
return declaration.mount;
}
if (!publicRoot) {
return undefined;
}
return `${publicRoot}.${declaration.mount}`;
}
function resolveLocalRootMount(
declaration: ILoadedMatrixBindingDeclaration,
publicRoot: string | undefined,
): string | undefined {
return declaration.mount
|| publicRoot;
}
function resolveLocalComponentMount(
declaration: ILoadedMatrixBindingDeclaration,
serviceMount: string | undefined,
rootKind: string,
): string | undefined {
if (isExplicitAbsoluteMount(declaration.mount)) {
return declaration.mount;
}
if (rootKind === 'package-root' && serviceMount) {
return `${serviceMount}.${declaration.mount}`;
}
return declaration.mount;
}
function isExplicitAbsoluteMount(mount: string): boolean {
return mount.includes('.');
}
function trimToUndefined(value: unknown): string | undefined {
if (typeof value !== 'string') {
return undefined;
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : undefined;
}
function readPlacementDeclarations(manifest: ILoadedMatrixServiceManifest): IPlacementDeclarationIndex {
const byMount = new Map<string, MatrixMountCardinality>();
const manifestRecord = readJsonObjectIfPresent(path.join(manifest.packageDir, 'matrix.json'));
if (!manifestRecord) {
return { byMount };
}
addPlacementDeclaration(byMount, manifestRecord.root);
const components = manifestRecord.components;
if (Array.isArray(components)) {
for (const component of components) {
addPlacementDeclaration(byMount, component);
}
}
return { byMount };
}
function addPlacementDeclaration(
byMount: Map<string, MatrixMountCardinality>,
value: unknown,
): void {
if (!isRecord(value)) {
return;
}
const mount = trimToUndefined(value.mount);
if (!mount) {
return;
}
const placement = isRecord(value.placement) ? value.placement : undefined;
const cardinality = readMountCardinality(placement?.cardinality);
if (cardinality) {
byMount.set(mount, cardinality);
}
}
function resolveCardinalityForMount(
declarations: IPlacementDeclarationIndex,
mount: string,
): MatrixMountCardinality {
return declarations.byMount.get(mount) ?? 'named-instance';
}
function placementForMount(
physicalMount: string,
logicalMount: string,
cardinality: MatrixMountCardinality,
callable: boolean,
): RuntimeInventoryPlacementMetadata {
return {
cardinality,
claimMode: claimModeForCardinality(cardinality),
logicalMount,
physicalMount,
callable,
};
}
function claimModeForCardinality(cardinality: MatrixMountCardinality): MatrixPlacementClaimMode {
if (cardinality === 'authority-singleton') {
return 'authoritative';
}
if (cardinality === 'queue-service') {
return 'pool-member';
}
if (cardinality === 'replicated-read') {
return 'mirror';
}
return 'local-only';
}
function readMountCardinality(value: unknown): MatrixMountCardinality | undefined {
return value === 'authority-singleton'
|| value === 'device-singleton'
|| value === 'device-local-control'
|| value === 'queue-service'
|| value === 'named-instance'
|| value === 'replicated-read'
? value
: undefined;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function readRecord(value: unknown): Record<string, unknown> | undefined {
return isRecord(value) ? value : undefined;
}
function resolveRunnerHostPlacement(
runtime: MatrixRuntime,
environment: ILoadedMatrixPackageEnvironment | undefined,
): IRunnerHostPlacement | undefined {
const hostHome = trimToUndefined(runtime.getRootContext().getService<unknown>('matrix-host-home'))
?? trimToUndefined(environment?.environment.host?.matrixDir);
if (!hostHome) {
return undefined;
}
const credentialsDir = path.join(hostHome, 'credentials');
const linkRecord = readJsonObjectIfPresent(path.join(credentialsDir, 'hivecast-link.json'));
const installRecord = readJsonObjectIfPresent(path.join(credentialsDir, 'hivecast-install.json'));
const hostId = trimToUndefined(linkRecord?.hostId) ?? trimToUndefined(installRecord?.installId);
if (!hostId) {
return undefined;
}
const hostName = trimToUndefined(linkRecord?.hostName) ?? trimToUndefined(installRecord?.hostName);
const deviceSlug = trimToUndefined(linkRecord?.deviceSlug);
return {
deviceId: hostId,
hostId,
...(hostName ? { hostName } : {}),
...(deviceSlug ? { deviceSlug } : {}),
};
}
function registryClaimPlacement(hostPlacement: IRunnerHostPlacement | undefined): Record<string, string> {
return hostPlacement
? {
deviceId: hostPlacement.deviceId,
hostId: hostPlacement.hostId,
}
: {};
}
function hostPlacementMetadata(hostPlacement: IRunnerHostPlacement | undefined): Record<string, string> {
return hostPlacement
? {
deviceId: hostPlacement.deviceId,
hostId: hostPlacement.hostId,
...(hostPlacement.hostName ? { hostName: hostPlacement.hostName } : {}),
...(hostPlacement.deviceSlug ? { deviceSlug: hostPlacement.deviceSlug } : {}),
}
: {};
}
function readJsonObjectIfPresent(filePath: string): Record<string, unknown> | undefined {
if (!fs.existsSync(filePath)) {
return undefined;
}
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '')) as unknown;
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`Expected JSON object at ${filePath}`);
}
return parsed as Record<string, unknown>;
}
async function ensureRegistryActor<TActor extends object>(
runtime: MatrixRuntime,
mount: string,
ActorClass: new () => TActor,
): Promise<boolean> {
if (runtime.hasComponent(mount)) {
return false;
}
await runtime.createSupervised(ActorClass as never, mount);
return true;
}