hypnotranz 1a0419dc0c feat: initial Matrix SDK
Full SDK workspace: core, contracts, sdk, cli, browser-host, browser-kit,
federation, omega-core, oracle, self-healing, strategies, tools, emacs.
Clean extraction from the development monorepo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:54:15 -06:00

4441 lines
168 KiB
TypeScript

// src/browser/MatrixDSLHost.ts
//
// FP-36: Declarative DSL Host — Zero-Ceremony Browser Bootstrap
//
// <matrix-dsl-host> creates its own NATS transport,
// MatrixRuntime, and context hierarchy from HTML attributes — so each
// browser app can compose Matrix actors and CQRS views with zero init.ts.
//
// Two modes:
// LOCAL: <matrix-dsl-host root="myapp">
// FEDERATED: <matrix-dsl-host root="myapp" backbone="ws://localhost:4223">
//
// Children receive contexts via explicit mounting (not AUTO-PULL,
// because this element is not a MatrixActorHtmlElement).
import { MatrixRuntime } from '@open-matrix/core';
import { BrowserDomStrategy } from '@open-matrix/core';
import { TopicRouter } from '@open-matrix/core';
import { RequestReply } from '@open-matrix/core';
import { createBrowserNatsTransport } from '@open-matrix/core/transport/createBrowserNatsTransport';
import {
ACTOR_CALL_CLIENT,
ACTOR_SERVICES,
ActorCallClient,
ActorServices,
RUNTIME_ADDRESS_CONTEXT,
SCOPED_INTENT_RESOLVER,
ScopedIntentResolver,
WELL_KNOWN_SERVICE_BINDINGS,
} from '@open-matrix/core';
import type { IMatrixContext } from '@open-matrix/core';
import type { IMatrixRuntime } from '@open-matrix/core';
import type { MatrixAuthorityContext } from '@open-matrix/core';
import type { RuntimeAddressContext } from '@open-matrix/core';
import type { ITransportAdapter } from '@open-matrix/core';
import type { MxEnvelope } from '@open-matrix/core';
import { SESSION_INFO_KEY, FEDERATION_PEER_KEY, type ISessionInfo } from '@open-matrix/core/services/SessionInfoService';
import {
HOSTED_RUNTIME_TARGET_CHANGED_EVENT,
hostedRuntimeTargetInfoEquals,
resolveHostedRuntimeTargetInfo,
type IHostedRuntimeTargetInfo,
} from './HostedRuntimeTarget.js';
import { resolveHostedRuntimeRecoveryPlan } from './HostedRuntimeRecovery.js';
import {
createBrowserProfileSpan,
logBrowserProfile,
profileBrowserSpan,
} from './BrowserProfileTrace.js';
import {
mountBrowserRuntimeControl,
type BrowserRuntimeLifecycleState,
} from './BrowserRuntimeControlActor.js';
import {
getOrCreateTabRealm,
getOrCreateIdentityTabRoot,
isValidRealm,
getBrowserRouter,
type FederationPeer,
type BrowserRouterResult,
} from '@open-matrix/federation';
import type { RootContext, FederationMode } from '@open-matrix/core/core/context/RealmContext';
import { createRootContext } from '@open-matrix/core/core/context/RealmContext';
import {
COMPONENT_RUNTIME_METADATA,
type ComponentRegistrationSource,
type ComponentSourceKind,
type IComponentRuntimeMetadata,
} from '@open-matrix/core';
import type { ComponentArtifactFreshness } from '@open-matrix/core/core/composite/IComponentRuntimeMetadata';
import type {
ServiceRegistryEntryKindV1,
ServiceRegistryEntryV1,
} from '@open-matrix/contracts/service-registry';
import type {
RuntimeInventoryEntryV1,
RuntimePresenceEventV1,
RuntimePresenceRecordV1,
} from '@open-matrix/contracts/runtime-presence';
interface IBootstrapTransportPlan {
readonly protocolVersion?: 1;
readonly kind?: 'nats';
readonly wsMode?: 'same-origin-proxy';
readonly wsPath?: string | null;
readonly authMode?: 'jwt' | 'static' | 'anonymous' | 'login-required';
readonly authEndpoint?: string | null;
readonly busAuthMode?: 'bus-token' | 'none';
readonly sessionMode?: 'session-cookie' | 'local-client' | 'anonymous' | 'device-login-required' | 'device-session';
}
interface IBootstrapValueSources {
readonly root?: string;
readonly authorityRoot?: string;
readonly assetHostRoot?: string;
readonly platformServiceRoot?: string;
readonly sessionAuthorityRoot?: string;
readonly selectedAuthorityRoot?: string;
readonly runtimeInstanceRoot?: string;
readonly platformRoot?: string;
readonly addressRoot?: string;
readonly routeKey?: string;
readonly publicNamespace?: string;
readonly workspaceRealm?: string;
readonly browserWsPath?: string;
readonly transportAuthMode?: string;
readonly busToken?: string;
readonly sessionIdentity?: string;
readonly federation?: string;
}
interface IBootstrapFailureDiagnostics {
readonly classification: string;
readonly hint: string;
readonly lines: string[];
}
interface INatsWsBootstrapPayload {
readonly natsWsUrl?: string;
readonly authenticated?: boolean;
readonly busToken?: string;
readonly authorityContext?: unknown;
}
function readBootstrapRecord(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`MatrixDSLHost: /api/bootstrap missing ${label}`);
}
return value as Record<string, unknown>;
}
function readBootstrapString(value: unknown, field: string): string {
if (typeof value !== 'string') {
throw new Error(`MatrixDSLHost: /api/bootstrap authorityContext.${field} must be a string`);
}
const trimmed = value.trim();
if (!trimmed) {
throw new Error(`MatrixDSLHost: /api/bootstrap authorityContext.${field} must not be empty`);
}
return trimmed;
}
function readBootstrapNullableString(value: unknown, field: string): string | null {
if (value === null || value === undefined) {
return null;
}
if (typeof value !== 'string') {
throw new Error(`MatrixDSLHost: /api/bootstrap authorityContext.${field} must be a string or null`);
}
const trimmed = value.trim();
return trimmed.length > 0 ? trimmed : null;
}
function readBootstrapBoolean(value: unknown, field: string): boolean {
if (typeof value !== 'boolean') {
throw new Error(`MatrixDSLHost: /api/bootstrap authorityContext.${field} must be a boolean`);
}
return value;
}
function readBootstrapStringArray(value: unknown, field: string): readonly string[] {
if (!Array.isArray(value)) {
throw new Error(`MatrixDSLHost: /api/bootstrap authorityContext.${field} must be a string array`);
}
return value.map((item) => {
if (typeof item !== 'string') {
throw new Error(`MatrixDSLHost: /api/bootstrap authorityContext.${field} must contain only strings`);
}
return item.trim();
}).filter((item) => item.length > 0);
}
function parseBootstrapAuthorityContext(value: unknown): MatrixAuthorityContext {
const record = readBootstrapRecord(value, 'authorityContext');
return {
assetHostRoot: readBootstrapString(record.assetHostRoot, 'assetHostRoot'),
platformServiceRoot: readBootstrapString(record.platformServiceRoot, 'platformServiceRoot'),
sessionAuthorityRoot: readBootstrapNullableString(record.sessionAuthorityRoot, 'sessionAuthorityRoot'),
selectedAuthorityRoot: readBootstrapNullableString(record.selectedAuthorityRoot, 'selectedAuthorityRoot'),
runtimeInstanceRoot: readBootstrapNullableString(record.runtimeInstanceRoot, 'runtimeInstanceRoot'),
isAuthenticated: readBootstrapBoolean(record.isAuthenticated, 'isAuthenticated'),
isPlatformAdmin: readBootstrapBoolean(record.isPlatformAdmin, 'isPlatformAdmin'),
selectableAuthorityRoots: readBootstrapStringArray(record.selectableAuthorityRoots, 'selectableAuthorityRoots'),
};
}
interface IRegistryQueryResponse {
readonly entries?: unknown;
}
interface IRouteKeyWebappRoute {
readonly appName: string;
readonly routeKey: string;
}
interface IBrowserRuntimeInventoryEntry {
mount: string;
type: string;
surface: string;
runtimeId: string;
claimKind: string;
description?: string;
hasChildren?: boolean;
}
interface IBrowserRuntimeRegistryState {
readonly registryTarget: string;
readonly namespaceRoot: string;
readonly runtimeId: string;
readonly runtimeRoot: string;
readonly app: string;
readonly contextMount: string;
readonly hostBootstrapMode: boolean;
readonly sessionMount: string;
readonly relativeMount: string;
readonly runtimeMount: string;
readonly controlMount: string;
readonly authorityRoot: string;
readonly sessionAuthorityRoot: string;
readonly selectedAuthorityRoot: string;
readonly runtimeInstanceRoot: string;
readonly runtimeHref: string;
readonly registeredAt: number;
}
interface IRuntimeSummaryPayload {
readonly authority?: {
readonly addressRoot?: string | null;
} | null;
readonly attachedLeafRoots?: {
readonly items?: Array<{
readonly root?: string;
readonly connectionKind?: string | null;
}>;
} | null;
}
interface IDevicesListResponse {
readonly ok?: unknown;
readonly devices?: unknown;
}
interface IDeviceInventoryRecord {
readonly authorityRoot?: unknown;
readonly status?: unknown;
readonly updatedAt?: unknown;
readonly lastHeartbeatAt?: unknown;
}
const HOSTED_SELECTED_LEAF_STORAGE_KEY = 'matrix-web.selected-leaf-root';
const HOSTED_RUNTIME_RECOVERY_NOTICE_ATTR = 'data-hosted-runtime-recovery';
const BROWSER_RUNTIME_ID_PREFIX = 'RUNTIME-BROWSER-';
const HOST_PROFILE_SCOPE = 'host';
type HostedRuntimeTargetSource = 'explicit' | 'auto' | 'none';
type HostedRuntimeTargetKind = 'leaf' | 'authority' | 'unknown';
interface IHostedRuntimeTarget {
readonly root: string | null;
readonly source: HostedRuntimeTargetSource;
readonly kind: HostedRuntimeTargetKind;
readonly requestedRoot: string | null;
readonly unavailableReason: 'missing' | 'not-live' | 'unauthorized' | 'none';
}
interface IHostedRuntimeSummaryState {
readonly authorityRoot: string | null;
readonly liveLeafRoots: string[];
readonly knownLeafRoots: string[];
}
const HOSTED_RUNTIME_TARGET_REFRESH_INTERVAL_MS = 3_000;
interface IHostRoutingPeer {
readonly hasBackbone: boolean;
readonly localRoot: string;
readonly localRealm?: string;
invoke<T>(
target: string,
opOrRequest: string | { op: string; payload?: unknown },
payloadOrTimeout?: unknown,
timeoutMs?: number,
): Promise<T>;
route(topic: string, payload: unknown): Promise<void>;
}
interface IMatrixChildElementHooks {
_receiveContext?: (ctx: IMatrixContext) => Promise<void>;
mount?: (ctx: IMatrixContext) => Promise<void>;
setContext?: (ctx: IMatrixContext) => void;
}
interface IMatrixRootContextReceiver {
_receiveContext?: (ctx: { rootContext: RootContext; realmContext: RootContext }) => void;
}
interface IMatrixActorElementVersionSource {
_actor?: {
children?: {
version?: number;
};
};
}
/**
* Options for programmatic bootstrap via `host.bootstrap()`.
*/
export interface BootstrapOptions {
/** Wrap the transport after creation but before runtime/children. */
wrapTransport?: (transport: ITransportAdapter) => ITransportAdapter;
/** Override bus root (alternative to setting attribute). */
busRoot?: string;
/** Override backbone URL. */
backbone?: string;
/** Override mount path. */
mount?: string;
}
/**
* <matrix-dsl-host> - Self-contained declarative browser bootstrap.
*
* Creates MatrixRuntime, FederationPeer, and context hierarchy from
* HTML attributes. Children (MatrixActorHtmlElement instances) receive
* derived contexts and can communicate immediately.
*
* @example Local-only (in-memory transport, no NATS)
* ```html
* <matrix-dsl-host root="myapp" mount="dashboard">
* <my-counter id="c1"></my-counter>
* <my-chart id="chart" tracks="c1"></my-chart>
* </matrix-dsl-host>
* ```
*
* @example Federated (NATS backbone for cross-root)
* ```html
* <matrix-dsl-host root="flowpad" backbone="ws://localhost:4223">
* <ralph-supervisor-view tracks="matrix-3/ralph" autoview>
* </ralph-supervisor-view>
* </matrix-dsl-host>
* ```
*
* @example Existing init.ts auto-init control
* ```html
* <matrix-dsl-host auto-init="false">
* <my-app></my-app>
* </matrix-dsl-host>
* <script>
* const host = document.querySelector('matrix-dsl-host');
* host.initialize(existingRuntime, existingContext);
* </script>
* ```
*/
export class MatrixDSLHost extends HTMLElement {
/** Matrix context for this host's children */
private _context: IMatrixContext | null = null;
/** Runtime instance (created internally or injected) */
private _runtime: IMatrixRuntime | null = null;
/** FederationPeer (only in federated mode) */
private _peer: FederationPeer | null = null;
/** Wall-clock time the splash was first shown; used to enforce a minimum
* visible duration before fade-out so the brand frame is perceivable
* even when bootstrap is sub-100ms (warm cache). */
private _splashShownAt: number | null = null;
private _splashHideScheduled = false;
/** Router-shaped service exposed to app code. */
private _routingPeer: IHostRoutingPeer | null = null;
/** Transport adapter used by the runtime */
private _transport: ITransportAdapter | null = null;
/** Initialization flag */
private _initialized = false;
/** Mutation observer for light DOM changes */
private _observer: MutationObserver | null = null;
/** Root context for federation metadata */
private _rootContext: RootContext | null = null;
/** $join subscription cleanup */
private _joinUnsub: (() => void) | null = null;
/** Transport wrapper callback (set via bootstrap() or public property) */
private _wrapTransport: ((t: ITransportAdapter) => ITransportAdapter) | null = null;
/** Bus token refresh timer (INV-ID-7: 4-min interval, token has 5-min TTL) */
private _busTokenRefreshTimer: ReturnType<typeof setInterval> | null = null;
/** Background runtime registry discovery retry timer after initial discovery timeouts. */
private _daemonDiscoveryRetryTimer: ReturnType<typeof setTimeout> | null = null;
/** Exponential backoff for background runtime registry rediscovery. */
private _daemonDiscoveryRetryDelayMs = 1_000;
/** Prevent overlapping runtime registry discovery loops. */
private _daemonDiscoveryInFlight = false;
/** Background refresh loop for hosted runtime liveness metadata. */
private _hostedRuntimeTargetRefreshTimer: ReturnType<typeof setInterval> | null = null;
/** Prevent overlapping hosted runtime summary polls. */
private _hostedRuntimeTargetRefreshInFlight = false;
/** Service Registry lease renewal timer for this browser runtime. */
private _browserRuntimeRenewTimer: ReturnType<typeof setInterval> | null = null;
/** Bound beforeunload handler for releasing this browser runtime claim. */
private _browserRuntimeReleaseHandler: (() => void) | null = null;
/** Current Service Registry claim state for this browser runtime. */
private _browserRuntimeRegistryState: IBrowserRuntimeRegistryState | null = null;
/** Component IDs currently claimed for this browser runtime in system.registry. */
private _browserRuntimeClaimedComponentIds = new Set<string>();
/** Current browser runtime id, used to refresh inventory after dynamic child changes. */
private _runtimeRegistryId = '';
/** Browser runtime control actor start time for stable runtime status. */
private _runtimeControlStartedAt = '';
/** Debounce timer for child-driven registry claim refreshes. */
private _runtimeInventoryClaimRefreshTimer: ReturnType<typeof setTimeout> | null = null;
/** Increments whenever cleanup invalidates async registration continuations. */
private _lifecycleGeneration = 0;
/** Last reported host child version for runtime inventory refreshes. */
private _runtimeInventoryVersion = -1;
/** URL param keys to read (when url-params attribute is present) */
private static readonly URL_PARAM_MAP: Record<string, string> = {
root: 'root',
realm: 'root', // ?realm= maps to root attr
backbone: 'backbone',
backboneUrl: 'backbone',
mount: 'mount',
daemonRoot: 'daemon-root',
daemonRealm: 'daemon-root',
addressRoot: 'data-address-root',
};
/** Raw adapter before wrapping (for getWireTopicInfo etc.) */
private _rawAdapter: ITransportAdapter | null = null;
/** Identity state from bootstrap */
private _principal: { principalId: string; displayName?: string; email?: string; tier?: string } | null = null;
private _activeOrg: { orgId: string; displayName: string; role: string } | null = null;
private _isAuthenticated = false;
/** Promise that resolves when initialization is complete */
readonly ready: Promise<void>;
private _resolveReady!: () => void;
private _bootstrapTransportPlan: IBootstrapTransportPlan | null = null;
static get observedAttributes(): string[] {
return ['mount', 'auto-init', 'root', 'realm', 'federation', 'backbone', 'identity-provider', 'client-id', 'auto-attach'];
}
constructor() {
super();
this.ready = new Promise(resolve => {
this._resolveReady = resolve;
});
}
private _deriveNatsWsTransportPlan(bootstrap: INatsWsBootstrapPayload): IBootstrapTransportPlan | null {
const rawWsUrl = typeof bootstrap.natsWsUrl === 'string' ? bootstrap.natsWsUrl.trim() : '';
if (!rawWsUrl) {
return null;
}
let wsPath = '/nats-ws';
try {
const parsed = new URL(rawWsUrl, window.location.href);
if (parsed.pathname && parsed.pathname !== '/') {
wsPath = parsed.pathname;
}
} catch {
const match = rawWsUrl.match(/^wss?:\/\/[^/]+(\/.*)$/i);
if (match?.[1]) {
wsPath = match[1];
}
}
return {
kind: 'nats',
wsMode: 'same-origin-proxy',
wsPath,
busAuthMode: bootstrap.busToken ? 'bus-token' : 'none',
sessionMode: bootstrap.authenticated ? 'session-cookie' : 'anonymous',
};
}
// ═══════════════════════════════════════════════════════════════════════════
// DOM Lifecycle
// ═══════════════════════════════════════════════════════════════════════════
connectedCallback(): void {
const autoInit = this.getAttribute('auto-init');
if (autoInit !== 'false') {
this._bootstrapAsync();
}
}
disconnectedCallback(): void {
this._cleanup();
}
attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {
if (oldValue === newValue) return;
if (name === 'mount' && this._initialized) {
this._cleanup();
this._bootstrapAsync();
}
}
// ═══════════════════════════════════════════════════════════════════════════
// Public API
// ═══════════════════════════════════════════════════════════════════════════
/** The FederationPeer (null in local-only mode). */
get peer(): FederationPeer | null {
return this._peer;
}
/** The MatrixRuntime instance. */
get runtime(): IMatrixRuntime | null {
return this._runtime;
}
/** The host's Matrix context (children derive from this). */
get context(): IMatrixContext | null {
return this._context;
}
/** The root context for federation metadata. */
get rootContext(): RootContext | null {
return this._rootContext;
}
get realmContext(): RootContext | null {
return this._rootContext;
}
/** The transport adapter (possibly wrapped). */
get transport(): ITransportAdapter | null {
return this._transport;
}
/** The raw (unwrapped) transport adapter (for getWireTopicInfo etc.). */
get rawAdapter(): ITransportAdapter | null {
return this._rawAdapter;
}
/** Whether backbone is connected (false in local-only or before bootstrap). */
get hasBackbone(): boolean {
return this._routingPeer?.hasBackbone ?? false;
}
/** Authenticated principal from bootstrap (null if anonymous). */
get principal(): { principalId: string; displayName?: string; email?: string; tier?: string } | null {
return this._principal;
}
/** Active org context (null if not in an org). */
get activeOrg(): { orgId: string; displayName: string; role: string } | null {
return this._activeOrg;
}
/** Subscription tier (from principal or 'anonymous'). */
get tier(): string {
return this._principal?.tier ?? 'anonymous';
}
/** Whether the session is authenticated. */
get isAuthenticated(): boolean {
return this._isAuthenticated;
}
/**
* Set a transport wrapper BEFORE bootstrap.
* The wrapper is applied after transport creation but before runtime/children.
* This is how apps intercept all pub/sub (e.g., for logging panels).
*/
set wrapTransport(fn: ((t: ITransportAdapter) => ITransportAdapter) | null) {
this._wrapTransport = fn;
}
/**
* Programmatic bootstrap with options.
* Use when auto-init="false" and you need to configure before bootstrap.
*/
async bootstrap(options?: BootstrapOptions): Promise<void> {
if (this._initialized) {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.warn('[MatrixDSLHost] Already initialized');
return;
}
// Apply option overrides to attributes.
if (options?.busRoot) this.setAttribute('bus-root', options.busRoot);
if (options?.backbone) this.setAttribute('backbone', options.backbone);
if (options?.mount !== undefined) this.setAttribute('mount', options.mount);
// Store transport wrapper (option overrides property setter)
if (options?.wrapTransport) {
this._wrapTransport = options.wrapTransport;
}
await this._bootstrapAsync();
}
/**
* Initialize with an externally-created runtime.
* Use this when auto-init="false" and you have an existing runtime.
*/
async initialize(runtime: IMatrixRuntime, context?: IMatrixContext): Promise<void> {
if (this._initialized) {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.warn('[MatrixDSLHost] Already initialized');
return;
}
this._runtime = runtime;
const mountPath = this.getAttribute('mount') ?? this.id ?? '';
const rootContext = runtime.getRootContext?.();
if (!rootContext && !context) {
throw new Error('[MatrixDSLHost] Runtime has no getRootContext() and no context provided');
}
this._context = context ?? (mountPath
? rootContext!.derive(mountPath)
: rootContext!);
// Install $join responder and mount children
this._installJoinResponder();
await this._mountChildren();
this._setupMutationObserver();
this._parseRootAttributes();
this._propagateRootToChildren();
this._initialized = true;
this._resolveReady();
this._fireReadyEvent();
}
// ═══════════════════════════════════════════════════════════════════════════
// Self-Bootstrap (FP-36 Phase 1)
// ═══════════════════════════════════════════════════════════════════════════
/**
* Async self-bootstrap: creates runtime + transport from attributes.
*/
private async _bootstrapAsync(): Promise<void> {
const explicitBusRootAttr = this.getAttribute('bus-root');
let hostBootstrapMode = this._isHostBootstrapMode();
const routeKeyRoute = this._parseRouteKeyWebappRouteFromLocation();
const requireDaemonBootstrap = this._shouldRequireDaemonBootstrap()
|| routeKeyRoute !== null;
delete this.dataset.explicitAuthorityRoot;
const hadExplicitDaemonTarget = this.hasAttribute('daemon-root')
|| this.hasAttribute('daemon-realm')
|| routeKeyRoute !== null;
const initialUrlParams = this.hasAttribute('url-params')
? new URLSearchParams(window.location.search)
: null;
const hasExplicitDaemonTargetParam = initialUrlParams?.has('daemonRoot') || initialUrlParams?.has('daemonRealm') || false;
const hostedAppName = this._deriveHostedAppName();
const finishBootstrapProfile = createBrowserProfileSpan(HOST_PROFILE_SCOPE, 'bootstrapAsync', {
hostedApp: hostedAppName ?? '',
requireDaemonBootstrap,
hasExplicitDaemonTarget: hadExplicitDaemonTarget || hasExplicitDaemonTargetParam,
});
try {
this.dataset.status = 'initializing';
this._manageLoadingElement('initializing');
let discoveredNats = false;
let bootstrapAuthorityRoot = '';
let bootstrapPublicNamespace = '';
let bootstrapRouteKey = '';
let renderNamespaceSetupLocally = false;
let loginRequiredBootstrap = false;
// Phase A: Transport coordinate discovery.
// Daemon-served apps must declare their path through /api/bootstrap.
let usedBootstrap = false;
try {
const bootstrapEndpoint = this._bootstrapEndpointForRoute(routeKeyRoute, hostedAppName);
const bootstrapResp = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'api.bootstrap',
async () => fetch(bootstrapEndpoint, { credentials: 'same-origin' }),
{
hostedApp: hostedAppName ?? '',
required: requireDaemonBootstrap,
},
);
if (!bootstrapResp.ok) {
if (requireDaemonBootstrap) {
throw new Error(`Hosted app requires /api/bootstrap (HTTP ${bootstrapResp.status})`);
}
} else {
const bootstrap = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'api.bootstrap.parse',
async () => bootstrapResp.json() as Promise<{
natsWsUrl?: string; root?: string; natsToken?: string; busToken?: string;
authenticated?: boolean; principal?: { principalId: string; displayName?: string; email?: string; tier?: string };
activeOrg?: { orgId: string; displayName: string; role: string };
addressRoot?: string;
authorityRoot?: string;
assetHostRoot?: string;
platformServiceRoot?: string;
sessionAuthorityRoot?: string;
selectedAuthorityRoot?: string;
runtimeInstanceRoot?: string;
authorityContext?: unknown;
selectableAuthorityRoots?: string[];
isPlatformAdmin?: boolean;
routeKey?: string;
publicNamespace?: string;
spaceId?: string;
platformRoot?: string;
busRoot?: string;
appName?: string;
role?: string;
workspaceRealm?: string;
routeKind?: string;
login?: { provider?: string; startUrl?: string };
sources?: IBootstrapValueSources;
transportPlan?: IBootstrapTransportPlan;
topologyKind?: string;
nodeRoles?: unknown;
federation?: {
attached?: unknown;
hubUrl?: unknown;
hubKind?: unknown;
};
shellAccessMode?: string;
appLinkMode?: string;
}>,
{
hostedApp: hostedAppName ?? '',
},
);
const authorityContext = parseBootstrapAuthorityContext(bootstrap.authorityContext);
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info(`[MatrixDSLHost] /api/bootstrap → selectedAuthorityRoot="${authorityContext.selectedAuthorityRoot ?? ''}", runtimeInstanceRoot="${authorityContext.runtimeInstanceRoot ?? ''}", wsUrl="${bootstrap.natsWsUrl}", authenticated=${authorityContext.isAuthenticated}`);
// Store identity state from bootstrap
this._isAuthenticated = authorityContext.isAuthenticated;
this.dataset.authenticated = String(this._isAuthenticated);
if (bootstrap.principal) {
this._principal = bootstrap.principal;
this.dataset.principalId = bootstrap.principal.principalId;
if (bootstrap.principal.displayName) {
this.dataset.displayName = bootstrap.principal.displayName;
}
}
if (bootstrap.activeOrg) {
this._activeOrg = bootstrap.activeOrg;
}
const platformServiceRoot = authorityContext.platformServiceRoot;
const assetHostRoot = authorityContext.assetHostRoot;
const sessionAuthorityRoot = authorityContext.sessionAuthorityRoot ?? '';
const selectedAuthorityRoot = authorityContext.selectedAuthorityRoot ?? '';
const runtimeInstanceRoot = authorityContext.runtimeInstanceRoot ?? '';
this._setOptionalDatasetValue('assetHostRoot', assetHostRoot);
this._setOptionalDatasetValue('platformServiceRoot', platformServiceRoot);
this._setOptionalDatasetValue('sessionAuthorityRoot', sessionAuthorityRoot);
this._setOptionalDatasetValue('selectedAuthorityRoot', selectedAuthorityRoot);
this._setOptionalDatasetValue('runtimeInstanceRoot', runtimeInstanceRoot);
delete this.dataset.addressRoot;
const selectableAuthorityRoots = authorityContext.selectableAuthorityRoots;
this._setOptionalDatasetValue('selectableAuthorityRoots', selectableAuthorityRoots.join(','));
this.dataset.isPlatformAdmin = authorityContext.isPlatformAdmin ? 'true' : 'false';
if (authorityContext.isAuthenticated && sessionAuthorityRoot) {
this.dataset.principalAuthorityRoot = sessionAuthorityRoot;
} else {
delete this.dataset.principalAuthorityRoot;
}
bootstrapAuthorityRoot = selectedAuthorityRoot;
bootstrapPublicNamespace = (bootstrap.publicNamespace || '').trim();
bootstrapRouteKey = (bootstrap.routeKey || routeKeyFromPublicNamespace(bootstrapPublicNamespace) || '').trim();
const bootstrapSpaceId = (bootstrap.spaceId || '').trim();
this._setOptionalDatasetValue('routeKey', bootstrapRouteKey);
this._setOptionalDatasetValue('publicNamespace', bootstrapPublicNamespace);
this._setOptionalDatasetValue('spaceId', bootstrapSpaceId);
const bootstrapAppName = typeof bootstrap.appName === 'string'
? bootstrap.appName.trim()
: '';
if (bootstrapAppName) {
this.setAttribute('app-name', bootstrapAppName);
}
const bootstrapAttributeAuthorityRoot = sessionAuthorityRoot || selectedAuthorityRoot;
if (bootstrapAttributeAuthorityRoot) {
this.setAttribute('authority-root', bootstrapAttributeAuthorityRoot);
}
const bootstrapPlatformRoot = platformServiceRoot;
renderNamespaceSetupLocally = hostedAppName === 'web'
&& authorityContext.isAuthenticated
&& !bootstrapPublicNamespace
&& !bootstrapRouteKey
&& bootstrapAuthorityRoot === bootstrapPlatformRoot;
if (renderNamespaceSetupLocally) {
this.dataset.namespaceSetupMode = 'local';
} else {
delete this.dataset.namespaceSetupMode;
}
if (bootstrap.workspaceRealm) {
this.dataset.workspaceRealm = bootstrap.workspaceRealm;
}
this._setOptionalDatasetValue('routeKind', typeof bootstrap.routeKind === 'string' ? bootstrap.routeKind : '');
this._setOptionalDatasetValue('topologyKind', typeof bootstrap.topologyKind === 'string' ? bootstrap.topologyKind : '');
const authorityCoordinatorActive = bootstrapNodeRoleActive(bootstrap.nodeRoles, 'authorityCoordinator');
if (typeof authorityCoordinatorActive === 'boolean') {
this.dataset.authorityCoordinatorActive = String(authorityCoordinatorActive);
} else {
delete this.dataset.authorityCoordinatorActive;
}
this._setOptionalDatasetValue('shellAccessMode', typeof bootstrap.shellAccessMode === 'string' ? bootstrap.shellAccessMode : '');
this._setOptionalDatasetValue('appLinkMode', typeof bootstrap.appLinkMode === 'string' ? bootstrap.appLinkMode : '');
if (bootstrap.federation && typeof bootstrap.federation === 'object') {
this.dataset.federationAttached = bootstrap.federation.attached === true ? 'true' : 'false';
this._setOptionalDatasetValue(
'federationHubUrl',
typeof bootstrap.federation.hubUrl === 'string' ? bootstrap.federation.hubUrl : '',
);
this._setOptionalDatasetValue(
'federationHubKind',
typeof bootstrap.federation.hubKind === 'string' ? bootstrap.federation.hubKind : '',
);
} else {
delete this.dataset.federationAttached;
delete this.dataset.federationHubUrl;
delete this.dataset.federationHubKind;
}
this._setOptionalDatasetValue('loginProvider', bootstrap.login?.provider ?? '');
this._setOptionalDatasetValue('loginStartUrl', bootstrap.login?.startUrl ?? '');
const transportPlan = bootstrap.transportPlan ?? this._deriveNatsWsTransportPlan(bootstrap);
this._bootstrapTransportPlan = transportPlan ?? null;
loginRequiredBootstrap = transportPlan?.authMode === 'login-required';
if (loginRequiredBootstrap) {
this.dataset.status = 'login-required';
this.dataset.daemonStatus = 'skipped';
}
usedBootstrap = true;
const bootstrapRole = typeof bootstrap.role === 'string' ? bootstrap.role.trim().toLowerCase() : '';
const isLocalClientSession = transportPlan?.sessionMode === 'local-client'
|| bootstrap.sources?.sessionIdentity === 'local-client';
if (isLocalClientSession && !hostBootstrapMode) {
hostBootstrapMode = true;
this.setAttribute('bootstrap-mode', 'host');
}
if (hostBootstrapMode && bootstrapRole === 'platform' && hostedAppName && hostedAppName !== 'web') {
hostBootstrapMode = false;
this.setAttribute('bootstrap-mode', 'hosted');
}
// NATS subject scope = busRoot (Host's transport.root).
// Ambiguous bootstrap fields are intentionally not read here.
const busRoot = (bootstrap.busRoot || platformServiceRoot).trim();
if (busRoot) {
this.setAttribute('bus-root', busRoot);
if (renderNamespaceSetupLocally) {
this.removeAttribute('daemon-root');
this.removeAttribute('daemon-realm');
} else {
this.setAttribute('daemon-realm', busRoot);
}
if (hostBootstrapMode) {
this._ensureHostRuntimeMount(busRoot, hostedAppName);
}
if (!renderNamespaceSetupLocally && !hadExplicitDaemonTarget && !hasExplicitDaemonTargetParam) {
this.setAttribute('daemon-root', busRoot);
}
this.dataset.platformRoot = platformServiceRoot || busRoot;
}
discoveredNats = true;
this.setAttribute('transport', 'nats');
if (transportPlan) {
const wsPath = transportPlan.wsPath ?? '/nats-ws';
const localOnlyAnonymousSession = transportPlan.authMode === 'anonymous'
&& transportPlan.sessionMode === 'anonymous';
if (transportPlan.authMode === 'login-required' || localOnlyAnonymousSession) {
this.removeAttribute('nats-ws-url');
this.removeAttribute('backbone');
delete this.dataset.natsWsUrl;
delete this.dataset.natsAuthEndpoint;
delete this.dataset.busToken;
} else {
const sameOriginWsUrl = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}${wsPath}`;
this.setAttribute('nats-ws-url', sameOriginWsUrl);
this.setAttribute('backbone', sameOriginWsUrl);
}
if (transportPlan.authMode) {
this.dataset.natsAuthMode = transportPlan.authMode;
}
if (transportPlan.authEndpoint && transportPlan.authMode !== 'login-required') {
this.dataset.natsAuthEndpoint = transportPlan.authEndpoint;
} else {
delete this.dataset.natsAuthEndpoint;
}
if (transportPlan.busAuthMode) {
this.dataset.busAuthMode = transportPlan.busAuthMode;
}
if (transportPlan.sessionMode) {
this.dataset.sessionMode = transportPlan.sessionMode;
}
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info(
`[MatrixDSLHost] transport plan → wsMode="${transportPlan.wsMode ?? 'same-origin-proxy'}", wsPath="${wsPath ?? 'none'}", authMode="${transportPlan.authMode ?? 'unknown'}", busAuthMode="${transportPlan.busAuthMode ?? 'unknown'}", sessionMode="${transportPlan.sessionMode ?? 'unknown'}"`,
);
} else {
throw new Error('Hosted bootstrap did not declare transportPlan');
}
if (bootstrap.sources) {
this.dataset.rootSource = bootstrap.sources.root ?? '';
this.dataset.addressRootSource = bootstrap.sources.addressRoot ?? '';
this.dataset.selectedAuthorityRootSource = bootstrap.sources.selectedAuthorityRoot ?? '';
this.dataset.runtimeInstanceRootSource = bootstrap.sources.runtimeInstanceRoot ?? '';
this.dataset.transportAuthSource = bootstrap.sources.transportAuthMode ?? '';
this.dataset.busTokenSource = bootstrap.sources.busToken ?? '';
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info(
`[MatrixDSLHost] bootstrap sources → root="${bootstrap.sources.root ?? 'unknown'}", addressRoot="${bootstrap.sources.addressRoot ?? 'unknown'}", wsPath="${bootstrap.sources.browserWsPath ?? 'unknown'}", auth="${bootstrap.sources.transportAuthMode ?? 'unknown'}", busToken="${bootstrap.sources.busToken ?? 'unknown'}", identity="${bootstrap.sources.sessionIdentity ?? 'unknown'}"`,
);
}
if (bootstrap.natsToken) {
this.dataset.natsToken = bootstrap.natsToken;
}
// Store bus token in memory only (INV-ID-7: never localStorage/cookies)
if (bootstrap.busToken && !renderNamespaceSetupLocally) {
this.dataset.busToken = bootstrap.busToken;
}
}
} catch (error) {
if (requireDaemonBootstrap) {
throw error;
}
}
// 0b. URL param overrides (when url-params attribute is present)
// URL params take highest priority, overriding even Host bootstrap config.
if (this.hasAttribute('url-params')) {
const urlParams = new URLSearchParams(window.location.search);
for (const [paramKey, attrName] of Object.entries(MatrixDSLHost.URL_PARAM_MAP)) {
const value = urlParams.get(paramKey);
if (value) this.setAttribute(attrName, value);
}
}
if (
usedBootstrap
&& hostedAppName
&& hostedAppName !== 'web'
&& routeKeyRoute !== null
&& !this._isAuthenticated
&& !loginRequiredBootstrap
) {
this._redirectHostedAppToLogin();
return;
}
if (
usedBootstrap
&& hostedAppName
&& hostedAppName !== 'web'
&& !hostBootstrapMode
&& hasExplicitDaemonTargetParam
) {
await this._applyExplicitHostedRuntimeTarget('bootstrap-explicit');
}
if (routeKeyRoute && !hasExplicitDaemonTargetParam) {
const routeKeyMatchesBootstrap = !bootstrapRouteKey
|| bootstrapRouteKey === routeKeyRoute.routeKey;
const authorityRoot = routeKeyMatchesBootstrap
? bootstrapAuthorityRoot || (this.dataset.selectedAuthorityRoot ?? '').trim()
: '';
if (authorityRoot) {
this.setAttribute('daemon-root', authorityRoot);
this.setAttribute('daemon-realm', authorityRoot);
this.dataset.selectedAuthorityRoot = authorityRoot;
this.dataset.explicitAuthorityRoot = authorityRoot;
this._applyHostedRuntimeTargetMetadata(
authorityRoot,
'explicit',
'authority',
authorityRoot,
);
}
}
if (
usedBootstrap
&& hostedAppName
&& hostedAppName !== 'web'
&& !hostBootstrapMode
&& routeKeyRoute === null
) {
if (!this._isAuthenticated && !loginRequiredBootstrap) {
this._redirectHostedAppToLogin();
return;
}
if (hadExplicitDaemonTarget && !hasExplicitDaemonTargetParam) {
await this._applyExplicitHostedRuntimeTarget('bootstrap-explicit');
}
}
if (
usedBootstrap
&& hostedAppName
&& hostedAppName !== 'web'
&& !hostBootstrapMode
&& !hadExplicitDaemonTarget
&& !hasExplicitDaemonTargetParam
) {
const runtimeTarget = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'runtimeTarget.auto',
async () => this._resolveHostedUserRuntimeTarget(),
{
hostedApp: hostedAppName ?? '',
},
);
this._applyHostedRuntimeTargetMetadata(
runtimeTarget.root,
runtimeTarget.source,
runtimeTarget.kind,
runtimeTarget.requestedRoot,
runtimeTarget.unavailableReason,
);
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info(
`[MatrixDSLHost] hosted app runtime target → ${runtimeTarget.root || '(none)'} ` +
`(source=${runtimeTarget.source}, kind=${runtimeTarget.kind}, unavailable=${runtimeTarget.unavailableReason})`,
);
}
// 1. Determine mode from explicit four-roots attributes.
const busRootAttr = this.getAttribute('bus-root');
const backboneUrl = this.getAttribute('backbone');
const natsWsAttr = this.getAttribute('nats-ws-url');
const natsWsUrl = natsWsAttr ?? (!discoveredNats ? backboneUrl : null);
const mountPath = this.getAttribute('mount') ?? this.id ?? '';
const hasNatsBackbone = Boolean(busRootAttr && natsWsUrl);
const anonymousLocalBootstrap = this._bootstrapTransportPlan?.authMode === 'anonymous'
&& this._bootstrapTransportPlan.sessionMode === 'anonymous';
if (requireDaemonBootstrap
&& !renderNamespaceSetupLocally
&& !loginRequiredBootstrap
&& !anonymousLocalBootstrap
&& (!usedBootstrap || !hasNatsBackbone)) {
throw new Error('Hosted app bootstrap did not provide complete transport coordinates');
}
let selectedBackboneUrl: string | undefined;
// 2. Create transport + optional peer
if (busRootAttr && natsWsUrl) {
// FEDERATED MODE: Connect to the NATS backbone when transport coordinates exist.
// _bootstrapFederated() already handles both authenticated JWT flows and
// unauthenticated direct websocket connections.
await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'bootstrapFederated',
async () => this._bootstrapFederated(busRootAttr, natsWsUrl, mountPath),
{
root: busRootAttr,
mountPath,
},
);
selectedBackboneUrl = natsWsUrl;
} else if (busRootAttr) {
// ROOT-LOCAL MODE: Anonymous users or no backbone URL.
// Uses in-memory transport — actor lifecycle works normally,
// onReady fires, pages render. No NATS permission errors.
this._bootstrapRootLocal(busRootAttr, mountPath);
} else {
// LOCAL MODE: plain InMemoryBroker, no federation
this._bootstrapLocal(mountPath);
}
const rootCtx = this._runtime!.getRootContext!();
// 2b. Register context services for child components
const routingPeer = this._routingPeer;
const sessionRoot = (this._transport?.root ?? this._transport?.realm) ?? busRootAttr ?? '';
const hasBackbone = routingPeer?.hasBackbone ?? false;
this.dataset.sessionRoot = sessionRoot;
this.dataset.runtimeRoot = sessionRoot;
this.dataset.runtimeInstanceRoot = sessionRoot;
rootCtx.setService('runtime-instance-root', sessionRoot);
if (routingPeer) {
rootCtx.setService(SESSION_INFO_KEY, {
mode: 'router',
root: sessionRoot,
realm: sessionRoot,
hasBackbone,
backboneUrl: selectedBackboneUrl,
backboneError: hasBackbone ? null : 'Connection failed or not attempted',
} as ISessionInfo);
rootCtx.setService(FEDERATION_PEER_KEY, routingPeer);
} else {
rootCtx.setService(SESSION_INFO_KEY, {
mode: 'local',
root: sessionRoot,
realm: sessionRoot,
hasBackbone: false,
} as ISessionInfo);
}
// 2c. Register identity services on root context for child actors
const daemonRoot = this.getAttribute('daemon-root') ?? this.getAttribute('daemon-realm');
if (daemonRoot) {
rootCtx.setService('daemon-root', daemonRoot);
}
if (this.dataset.platformRoot) {
rootCtx.setService('platform-root', this.dataset.platformRoot);
}
if (this.dataset.assetHostRoot) {
rootCtx.setService('asset-host-root', this.dataset.assetHostRoot);
}
if (this.dataset.platformServiceRoot) {
rootCtx.setService('platform-service-root', this.dataset.platformServiceRoot);
}
if (this.dataset.sessionAuthorityRoot) {
rootCtx.setService('session-authority-root', this.dataset.sessionAuthorityRoot);
}
if (this.dataset.selectedAuthorityRoot) {
rootCtx.setService('selected-authority-root', this.dataset.selectedAuthorityRoot);
}
rootCtx.setService('is-platform-admin', this.dataset.isPlatformAdmin === 'true');
const authorityRootService = this.dataset.selectedAuthorityRoot
|| this.dataset.sessionAuthorityRoot;
if (authorityRootService) {
rootCtx.setService('authority-root', authorityRootService);
}
if (this.dataset.principalId) {
rootCtx.setService('principal-id', this.dataset.principalId);
}
if (this.dataset.busToken) {
rootCtx.setService('bus-token', this.dataset.busToken);
}
this._installScopedActorServices(rootCtx, sessionRoot);
if (this.dataset.orgId) {
rootCtx.setService('org-id', this.dataset.orgId);
}
if (this.dataset.workspaceId) {
rootCtx.setService('workspace-id', this.dataset.workspaceId);
}
// 3. Install $join responder so children can join
this._installJoinResponder();
// 4. Mount all light DOM children with derived contexts
await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'mountChildren',
async () => this._mountChildren(),
{
childCount: this.children.length,
},
);
// 5. Set up mutation observer for dynamic children
this._setupMutationObserver();
// 6. Parse root attributes for RootContext propagation
this._parseRootAttributes();
this._propagateRootToChildren();
// 7. Done
this._initialized = true;
const status = loginRequiredBootstrap
? 'login-required'
: (routingPeer?.hasBackbone || hasNatsBackbone) ? 'connected' : 'local';
this.dataset.status = status;
this.dataset.daemonStatus = loginRequiredBootstrap
? 'skipped'
: this.getAttribute('daemon-root') ? 'discovering' : 'skipped';
this._manageLoadingElement(status);
this._resolveReady();
this._fireReadyEvent();
const skipHostedShellDaemonDiscovery = this._shouldSkipHostedShellDaemonDiscovery(
hadExplicitDaemonTarget || hasExplicitDaemonTargetParam,
);
// Dispatch auth state event for app-level code
this.dispatchEvent(new CustomEvent(
this._isAuthenticated ? 'matrix-dsl-authenticated' : 'matrix-dsl-anonymous',
{ bubbles: true, detail: { principal: this._principal, activeOrg: this._activeOrg } },
));
if (loginRequiredBootstrap) {
this.dispatchEvent(new CustomEvent('matrix-host-login-required', {
bubbles: true,
composed: true,
detail: {
routeKind: this.dataset.routeKind ?? '',
sessionMode: this.dataset.sessionMode ?? '',
provider: this.dataset.loginProvider ?? '',
startUrl: this.dataset.loginStartUrl ?? '',
},
}));
if (this._shouldRedirectToDeviceLogin(hostedAppName)) {
this._redirectToDeviceLogin();
} else if (hostedAppName !== 'web') {
this._redirectToPlatformLogin();
}
return;
}
// Non-blocking — children are already mounted and can observe late identity resolution.
logBrowserProfile(HOST_PROFILE_SCOPE, 'phaseB.scheduled', {
usedBootstrap,
skipped: renderNamespaceSetupLocally,
});
if (!renderNamespaceSetupLocally) {
void this._runPhaseBBusDiscovery(usedBootstrap);
} else {
console.info('[MatrixDSLHost] Phase B service probe skipped for namespace setup — awaiting route-key claim');
}
// Non-blocking — must run after _fireReadyEvent() so child app subscriptions are live
logBrowserProfile(HOST_PROFILE_SCOPE, 'daemonDiscovery.scheduled', {
daemonRoot: this.getAttribute('daemon-root') ?? '',
skipped: renderNamespaceSetupLocally || skipHostedShellDaemonDiscovery,
});
if (!renderNamespaceSetupLocally && !skipHostedShellDaemonDiscovery) {
void this._discoverDaemon();
} else {
this.dataset.daemonStatus = 'skipped';
const skippedDaemonRoot = this.getAttribute('daemon-root') ?? this.getAttribute('daemon-realm') ?? '';
rootCtx.transport.publish(TopicRouter.events(''), {
op: 'daemonReady',
payload: {
root: skippedDaemonRoot,
realm: skippedDaemonRoot,
skipped: true,
namespaceSetup: renderNamespaceSetupLocally,
platformShell: skipHostedShellDaemonDiscovery,
},
});
}
if (this._shouldRefreshHostedRuntimeTarget()) {
this._startHostedRuntimeTargetRefresh();
void this._refreshHostedRuntimeTargetMetadata();
}
} catch (err) {
const diagnostics = await this._diagnoseBootstrapFailure(err);
const error = err instanceof Error ? err : new Error(String(err));
error.message = [error.message, '', ...diagnostics.lines].join('\n');
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.error(
requireDaemonBootstrap
? '[MatrixDSLHost] Hosted bootstrap failed — refusing local substitution:'
: '[MatrixDSLHost] Bootstrap failed — offline local substitution disabled:',
error,
);
this.dataset.errorClass = diagnostics.classification;
this.dataset.status = 'disconnected';
this.dataset.daemonStatus = 'error';
this.dataset.status = 'error';
this._manageLoadingElement('error', error);
this.dispatchEvent(new CustomEvent('matrix-dsl-error', {
bubbles: true,
detail: { error, classification: diagnostics.classification, hint: diagnostics.hint },
}));
} finally {
finishBootstrapProfile({
status: this.dataset.status ?? '',
daemonStatus: this.dataset.daemonStatus ?? '',
});
}
}
private _readHostedSelectedLeafRoot(): string | null {
if (typeof window === 'undefined') {
return null;
}
const stores: Array<Storage | undefined> = [window.sessionStorage, window.localStorage];
for (const store of stores) {
const raw = store?.getItem(HOSTED_SELECTED_LEAF_STORAGE_KEY);
if (typeof raw !== 'string') {
continue;
}
const normalized = raw.trim();
if (normalized.length > 0) {
return normalized;
}
}
return null;
}
private _sortRuntimeRootsForDefault(authorityRoot: string | null, roots: string[]): string[] {
const normalizedAuthority = typeof authorityRoot === 'string' && authorityRoot.trim().length > 0
? authorityRoot.trim()
: null;
const filtered = normalizedAuthority
? roots.filter((root) => root === normalizedAuthority || root.startsWith(`${normalizedAuthority}.`))
: roots.slice();
const candidates = normalizedAuthority ? filtered : roots.slice();
return candidates.sort((left, right) => {
const leftSegments = left.split('.').length;
const rightSegments = right.split('.').length;
if (leftSegments !== rightSegments) {
return leftSegments - rightSegments;
}
return left.localeCompare(right);
});
}
private _classifyHostedRuntimeTarget(
root: string | null,
authorityRoot: string | null,
attachedLeafRoots: string[],
): HostedRuntimeTargetKind {
if (!root) {
return 'unknown';
}
if (authorityRoot && root === authorityRoot) {
return 'authority';
}
if (attachedLeafRoots.includes(root)) {
return 'leaf';
}
if (authorityRoot && root.startsWith(`${authorityRoot}.`)) {
return 'leaf';
}
return 'unknown';
}
private _authenticatedPrincipalAuthorityRoot(): string | null {
if (!this._isAuthenticated) {
return null;
}
const principalRoot = (this.dataset.principalAuthorityRoot ?? '').trim();
if (principalRoot) {
return principalRoot;
}
const sessionRoot = (this.dataset.sessionAuthorityRoot ?? '').trim();
return sessionRoot || null;
}
private _hostedAuthorityRootFromBootstrap(): string {
const selectedRoot = (this.dataset.selectedAuthorityRoot ?? '').trim();
const principalRoot = this._authenticatedPrincipalAuthorityRoot();
return selectedRoot || principalRoot || '';
}
private _parseHostedRuntimeSummary(summary: IRuntimeSummaryPayload): IHostedRuntimeSummaryState {
const authorityRoot = this._hostedAuthorityRootFromBootstrap();
const items = Array.isArray(summary.attachedLeafRoots?.items)
? summary.attachedLeafRoots.items
: [];
const knownLeafRoots = [...new Set(
items
.map((item) => typeof item?.root === 'string' ? item.root.trim() : '')
.filter((root) => root.length > 0),
)];
const liveLeafRoots = [...new Set(
items
.filter((item) => {
const connectionKind = typeof item?.connectionKind === 'string' ? item.connectionKind.trim() : '';
return connectionKind.length === 0 || connectionKind !== 'registered';
})
.map((item) => typeof item?.root === 'string' ? item.root.trim() : '')
.filter((root) => root.length > 0),
)];
return {
authorityRoot: authorityRoot || null,
liveLeafRoots,
knownLeafRoots,
};
}
private _readHostedRuntimeTargetInfo(): IHostedRuntimeTargetInfo {
return resolveHostedRuntimeTargetInfo(this);
}
private _setOptionalDatasetValue(name: string, value: string): void {
if (value) {
this.dataset[name] = value;
} else {
delete this.dataset[name];
}
}
private _initialRuntimeSummaryState(): IHostedRuntimeSummaryState {
return {
authorityRoot: typeof this.dataset.selectedAuthorityRoot === 'string'
? this.dataset.selectedAuthorityRoot.trim() || null
: null,
liveLeafRoots: [],
knownLeafRoots: [],
};
}
private async _applyExplicitHostedRuntimeTarget(reason: string): Promise<void> {
const explicitRuntimeTarget = (
this.getAttribute('daemon-root')
?? this.getAttribute('daemon-realm')
?? ''
).trim();
if (!explicitRuntimeTarget) {
return;
}
if (!this._context) {
const summaryState = this._initialRuntimeSummaryState();
const runtimeTarget = this._resolveHostedRequestedRuntimeTarget(
explicitRuntimeTarget,
summaryState,
'explicit',
);
const authorizedInitialRoot = runtimeTarget.root
?? this._authorizedExplicitRootBeforeRuntimeSummary(
explicitRuntimeTarget,
summaryState.authorityRoot,
runtimeTarget.unavailableReason,
);
this._applyHostedRuntimeTargetMetadata(
authorizedInitialRoot,
runtimeTarget.source,
authorizedInitialRoot
? this._classifyHostedRuntimeTarget(
authorizedInitialRoot,
summaryState.authorityRoot,
summaryState.liveLeafRoots,
)
: runtimeTarget.kind,
runtimeTarget.requestedRoot,
authorizedInitialRoot ? 'none' : runtimeTarget.unavailableReason,
);
return;
}
let summaryState = this._initialRuntimeSummaryState();
try {
const summary = await this._loadHostedRuntimeSummaryFromBus(reason);
if (summary) {
summaryState = this._parseHostedRuntimeSummary(summary);
}
} catch {
// Preserve the explicit requested target in metadata, but do not promote
// it to the actual runtime root when live runtime inventory is missing.
}
const runtimeTarget = this._resolveHostedRequestedRuntimeTarget(
explicitRuntimeTarget,
summaryState,
'explicit',
);
this._applyHostedRuntimeTargetMetadata(
runtimeTarget.root,
runtimeTarget.source,
runtimeTarget.kind,
runtimeTarget.requestedRoot,
runtimeTarget.unavailableReason,
);
}
private _applyHostedRuntimeTargetMetadata(
root: string | null,
source: HostedRuntimeTargetSource,
kind: HostedRuntimeTargetKind,
requestedRoot: string | null = null,
unavailableReason: 'missing' | 'not-live' | 'unauthorized' | 'none' = 'none',
): void {
if (root) {
this.setAttribute('daemon-root', root);
this.setAttribute('daemon-realm', root);
} else {
this.removeAttribute('daemon-root');
this.removeAttribute('daemon-realm');
}
if (root) {
this.dataset.runtimeTargetRoot = root;
this.dataset.runtimeTargetSource = source;
this.dataset.runtimeTargetKind = kind;
} else {
delete this.dataset.runtimeTargetRoot;
this.dataset.runtimeTargetSource = source;
this.dataset.runtimeTargetKind = kind;
}
if (requestedRoot) {
this.dataset.runtimeTargetRequestedRoot = requestedRoot;
} else {
delete this.dataset.runtimeTargetRequestedRoot;
}
if (unavailableReason !== 'none') {
this.dataset.runtimeTargetUnavailableReason = unavailableReason;
} else {
delete this.dataset.runtimeTargetUnavailableReason;
}
this._renderHostedRuntimeRecoveryNotice(root, requestedRoot, unavailableReason);
}
private _renderHostedRuntimeRecoveryNotice(
root: string | null,
requestedRoot: string | null,
unavailableReason: 'missing' | 'not-live' | 'unauthorized' | 'none',
): void {
const existing = this._hostedRuntimeRecoveryNotice();
const plan = resolveHostedRuntimeRecoveryPlan({
runtimeRoot: root,
authorityRoot: this._initialRuntimeSummaryState().authorityRoot,
requestedRoot,
unavailableReason,
currentHref: typeof window !== 'undefined' ? window.location.href : null,
});
if (!plan) {
existing?.remove();
return;
}
const notice = existing ?? document.createElement('div');
notice.setAttribute(HOSTED_RUNTIME_RECOVERY_NOTICE_ATTR, 'true');
notice.setAttribute('role', 'status');
notice.style.display = 'flex';
notice.style.gap = '8px';
notice.style.alignItems = 'center';
notice.style.padding = '8px 12px';
notice.style.background = '#fff4e5';
notice.style.color = '#4a2a00';
notice.style.border = '1px solid #ffcc80';
notice.style.font = '13px system-ui, sans-serif';
notice.textContent = '';
const summary = document.createElement('span');
summary.textContent = plan.summary;
notice.appendChild(summary);
for (const action of plan.actions) {
const link = document.createElement('a');
link.href = action.href;
link.textContent = action.label;
link.title = action.title;
link.style.color = '#6b3900';
link.style.fontWeight = '600';
notice.appendChild(link);
}
if (!existing) {
this.insertBefore(notice, this.firstChild);
}
}
private _hostedRuntimeRecoveryNotice(): HTMLElement | null {
for (const child of Array.from(this.children)) {
if (
child instanceof HTMLElement
&& child.getAttribute(HOSTED_RUNTIME_RECOVERY_NOTICE_ATTR) === 'true'
) {
return child;
}
}
return null;
}
private _resolveHostedRequestedRuntimeTarget(
requestedRoot: string | null,
summary: IHostedRuntimeSummaryState,
preferredSource: HostedRuntimeTargetSource,
): IHostedRuntimeTarget {
const target = typeof requestedRoot === 'string' && requestedRoot.trim().length > 0
? requestedRoot.trim()
: null;
const authorityRoot = summary.authorityRoot;
const liveLeafRoots = summary.liveLeafRoots;
const knownLeafRoots = summary.knownLeafRoots;
const chooseDefault = (): string | null => {
const candidates = this._sortRuntimeRootsForDefault(authorityRoot, liveLeafRoots);
return candidates[0] ?? null;
};
if (target && liveLeafRoots.includes(target)) {
return {
root: target,
source: preferredSource,
kind: this._classifyHostedRuntimeTarget(target, authorityRoot, liveLeafRoots),
requestedRoot: target,
unavailableReason: 'none',
};
}
if (target && authorityRoot && target === authorityRoot) {
return {
root: authorityRoot,
source: preferredSource,
kind: this._classifyHostedRuntimeTarget(authorityRoot, authorityRoot, liveLeafRoots),
requestedRoot: target,
unavailableReason: 'none',
};
}
if (target) {
if (this._isUnauthorizedHostedRuntimeTarget(target, authorityRoot, liveLeafRoots, knownLeafRoots)) {
return {
root: null,
source: preferredSource,
kind: 'unknown',
requestedRoot: target,
unavailableReason: 'unauthorized',
};
}
const unavailableReason = knownLeafRoots.includes(target) ? 'not-live' : 'missing';
return {
root: null,
source: preferredSource,
kind: this._classifyHostedRuntimeTarget(target, authorityRoot, liveLeafRoots),
requestedRoot: target,
unavailableReason,
};
}
const automaticRoot = chooseDefault();
if (automaticRoot) {
return {
root: automaticRoot,
source: 'auto',
kind: this._classifyHostedRuntimeTarget(automaticRoot, authorityRoot, liveLeafRoots),
requestedRoot: null,
unavailableReason: 'none',
};
}
if (authorityRoot) {
return {
root: authorityRoot,
source: 'auto',
kind: 'authority',
requestedRoot: null,
unavailableReason: 'none',
};
}
return {
root: null,
source: 'none',
kind: 'unknown',
requestedRoot: target,
unavailableReason: 'none',
};
}
private _isUnauthorizedHostedRuntimeTarget(
target: string,
authorityRoot: string | null,
liveLeafRoots: readonly string[],
knownLeafRoots: readonly string[],
): boolean {
if (!authorityRoot || target === authorityRoot) {
return false;
}
if (liveLeafRoots.includes(target) || knownLeafRoots.includes(target)) {
return false;
}
if (target.startsWith(`${authorityRoot}.`)) {
return false;
}
return this._looksLikeHostedAuthorityRoot(target);
}
private _authorizedExplicitRootBeforeRuntimeSummary(
target: string,
authorityRoot: string | null,
unavailableReason: 'missing' | 'not-live' | 'unauthorized' | 'none',
): string | null {
if (unavailableReason === 'unauthorized' || !authorityRoot) {
return null;
}
if (target === authorityRoot || target.startsWith(`${authorityRoot}.`)) {
return target;
}
return null;
}
private _looksLikeHostedAuthorityRoot(root: string): boolean {
return /^SPACE-[A-Z0-9][A-Z0-9_-]*$/.test(root)
|| /^[A-Z][A-Z0-9_-]*(?:\.[A-Z0-9_-]+)+$/.test(root);
}
private _shouldRefreshHostedRuntimeTarget(): boolean {
if (!this._isAuthenticated) {
return false;
}
const pathname = typeof window !== 'undefined' && typeof window.location?.pathname === 'string'
? window.location.pathname
: '';
if (!pathname.startsWith('/apps/') && this._parseRouteKeyWebappRouteFromLocation() === null) {
return false;
}
const hostedAppName = this._deriveHostedAppName()?.trim().toLowerCase();
return Boolean(hostedAppName && hostedAppName !== 'web');
}
private _resolveHostedRuntimeTargetForActiveSession(
summary: IHostedRuntimeSummaryState,
): IHostedRuntimeTarget {
const current = this._readHostedRuntimeTargetInfo();
const preferredSource = current.targetSource === 'explicit' ? 'explicit' : 'auto';
const requestedRoot = preferredSource === 'explicit'
? current.requestedRoot || current.runtimeRoot || null
: null;
return this._resolveHostedRequestedRuntimeTarget(requestedRoot, {
authorityRoot: summary.authorityRoot ?? (current.authorityRoot || null),
liveLeafRoots: summary.liveLeafRoots,
knownLeafRoots: summary.knownLeafRoots,
}, preferredSource);
}
private async _refreshHostedRuntimeTargetMetadata(): Promise<void> {
if (!this._shouldRefreshHostedRuntimeTarget() || this._hostedRuntimeTargetRefreshInFlight) {
return;
}
this._hostedRuntimeTargetRefreshInFlight = true;
const previous = this._readHostedRuntimeTargetInfo();
try {
const summary = await this._loadHostedRuntimeSummaryFromBus('refresh');
if (!summary) {
return;
}
const parsed = this._parseHostedRuntimeSummary(summary);
const current = this._resolveHostedRuntimeTargetForActiveSession(parsed);
this._applyHostedRuntimeTargetMetadata(
current.root,
current.source,
current.kind,
current.requestedRoot,
current.unavailableReason,
);
const next = this._readHostedRuntimeTargetInfo();
if (!hostedRuntimeTargetInfoEquals(previous, next)) {
this.dispatchEvent(new CustomEvent<IHostedRuntimeTargetInfo>(
HOSTED_RUNTIME_TARGET_CHANGED_EVENT,
{
bubbles: true,
detail: next,
},
));
}
} catch {
// Keep current metadata when runtime summary is temporarily unavailable.
} finally {
this._hostedRuntimeTargetRefreshInFlight = false;
}
}
private async _resolveHostedUserRuntimeTarget(): Promise<IHostedRuntimeTarget> {
const selectedLeafRoot = this._readHostedSelectedLeafRoot();
if (!this._isAuthenticated) {
return {
root: null,
source: 'none',
kind: 'unknown',
requestedRoot: selectedLeafRoot,
unavailableReason: 'none',
};
}
if (!this._context) {
const summaryState = this._initialRuntimeSummaryState();
if (selectedLeafRoot) {
return {
root: selectedLeafRoot,
source: 'explicit',
kind: this._classifyHostedRuntimeTarget(
selectedLeafRoot,
summaryState.authorityRoot,
summaryState.liveLeafRoots,
),
requestedRoot: selectedLeafRoot,
unavailableReason: 'none',
};
}
const authorityRoot = summaryState.authorityRoot;
return {
root: authorityRoot,
source: authorityRoot ? 'auto' : 'none',
kind: authorityRoot ? 'authority' : 'unknown',
requestedRoot: selectedLeafRoot,
unavailableReason: 'none',
};
}
try {
const summary = await this._loadHostedRuntimeSummaryFromBus('bootstrap-auto');
if (!summary) {
return {
root: null,
source: 'none',
kind: 'unknown',
requestedRoot: selectedLeafRoot,
unavailableReason: 'none',
};
}
const parsed = this._parseHostedRuntimeSummary(summary);
return this._resolveHostedRequestedRuntimeTarget(selectedLeafRoot, parsed, 'explicit');
} catch {
return {
root: null,
source: 'none',
kind: 'unknown',
requestedRoot: selectedLeafRoot,
unavailableReason: 'none',
};
}
}
private async _loadHostedRuntimeSummaryFromBus(reason: string): Promise<IRuntimeSummaryPayload | null> {
if (!this._context) {
return null;
}
const authorityRoot = this._authenticatedPrincipalAuthorityRoot()
?? this._initialRuntimeSummaryState().authorityRoot;
if (!authorityRoot) {
return null;
}
const principalId = typeof this._principal?.principalId === 'string'
? this._principal.principalId.trim()
: '';
const busToken = typeof this.dataset.busToken === 'string' ? this.dataset.busToken.trim() : '';
if (!busToken) {
return null;
}
const payload: Record<string, unknown> = {
busToken,
...(principalId ? { principalId } : {}),
authorityRoot,
includeOffline: true,
};
const controlPlaneRoot = this._controlPlaneRootForSingletons(authorityRoot);
const response = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'runtimeSummary.devices.list',
async () => RequestReply.execute<Record<string, unknown>, IDevicesListResponse>(
this._context!,
`${controlPlaneRoot}/system.devices`,
'devices.list',
payload,
{ timeoutMs: 5000 },
),
{ reason },
);
if (response.ok !== true) {
return null;
}
return this._runtimeSummaryFromDeviceInventory(authorityRoot, response.devices);
}
private _runtimeSummaryFromDeviceInventory(authorityRoot: string, value: unknown): IRuntimeSummaryPayload {
const devices = Array.isArray(value)
? value
.map((entry) => this._deviceInventoryRecord(entry))
.filter((entry): entry is IDeviceInventoryRecord => entry !== null)
: [];
return {
authority: {
addressRoot: authorityRoot,
},
attachedLeafRoots: {
items: devices
.map((device) => {
const root = typeof device.authorityRoot === 'string'
? device.authorityRoot.trim()
: '';
if (!root) {
return null;
}
const status = typeof device.status === 'string'
? device.status.trim().toLowerCase()
: '';
return {
root,
connectionKind: status === 'online' ? 'Leafnode' : 'registered',
};
})
.filter((entry): entry is { readonly root: string; readonly connectionKind: string } => entry !== null),
},
};
}
private _deviceInventoryRecord(value: unknown): IDeviceInventoryRecord | null {
return value && typeof value === 'object' && !Array.isArray(value)
? value as IDeviceInventoryRecord
: null;
}
private _shouldRequireDaemonBootstrap(): boolean {
const bootstrapMode = this.getAttribute('bootstrap-mode')?.trim().toLowerCase();
const pathname = typeof window !== 'undefined' && typeof window.location?.pathname === 'string'
? window.location.pathname
: '';
if (bootstrapMode === 'local' || bootstrapMode === 'standalone') {
return false;
}
if (pathname.startsWith('/apps/')) {
return true;
}
if (this._isHostBootstrapMode() || this._isHostServiceBootstrap()) {
return true;
}
if (bootstrapMode === 'daemon' || bootstrapMode === 'hosted') {
return true;
}
return false;
}
private _redirectHostedAppToLogin(): void {
const redirectTarget = this._currentRelativeLocation();
const location = window.location;
const nextUrl = `/login?redirect=${encodeURIComponent(redirectTarget)}`;
this.dataset.status = 'redirecting-auth';
this._manageLoadingElement('redirecting-auth');
if (typeof location.assign === 'function') {
location.assign(nextUrl);
return;
}
if (typeof location.replace === 'function') {
location.replace(nextUrl);
return;
}
try {
location.href = nextUrl;
} catch {
// no-op: test doubles may expose a read-only href
}
}
private _currentRelativeLocation(): string {
const pathname = typeof window !== 'undefined' && typeof window.location?.pathname === 'string'
? window.location.pathname
: '/';
const search = typeof window !== 'undefined' && typeof window.location?.search === 'string'
? window.location.search
: '';
const hash = typeof window !== 'undefined' && typeof window.location?.hash === 'string'
? window.location.hash
: '';
return `${pathname || '/'}${search}${hash}`;
}
private _bootstrapEndpointForRoute(routeKeyRoute: IRouteKeyWebappRoute | null, appName: string | null): string {
const params = new URLSearchParams();
if (routeKeyRoute) {
params.set('routeKey', routeKeyRoute.routeKey);
}
if (appName) {
params.set('appName', appName);
}
const query = params.toString();
return query ? `/api/bootstrap?${query}` : '/api/bootstrap';
}
private _isHostBootstrapMode(): boolean {
const mode = this.getAttribute('bootstrap-mode')?.trim().toLowerCase();
return mode === 'host' || mode === 'host-service' || mode === 'matrix-host';
}
private _isHostServiceBootstrap(): boolean {
return this.dataset.rootSource === 'host-service:transport.root';
}
private _isAuthoritativeBootstrapIdentitySource(source: string): boolean {
return source === 'system-auth:principal.addressRoots'
|| source === 'system-auth:publicNamespaces';
}
private _shouldUseRuntimeRegistryDiscovery(authorityRoot: string): boolean {
const root = authorityRoot.trim();
if (!root) {
return false;
}
const hostedAppName = this._deriveHostedAppName();
if (!hostedAppName) {
return false;
}
const runtimeTargetKind = this.dataset.runtimeTargetKind ?? '';
const runtimeTargetRoot = this.dataset.runtimeTargetRoot ?? '';
if (runtimeTargetKind === 'authority' && runtimeTargetRoot === root) {
return true;
}
const selectedAuthorityRoot = (this.dataset.selectedAuthorityRoot ?? '').trim();
const rootSource = this.dataset.rootSource ?? '';
const selectedAuthorityRootSource = this.dataset.selectedAuthorityRootSource ?? '';
const rootCameFromAuthoritativeBootstrap = this._isAuthoritativeBootstrapIdentitySource(rootSource)
|| this._isAuthoritativeBootstrapIdentitySource(selectedAuthorityRootSource);
const authenticated = this.dataset.authenticated === 'true' || this._isAuthenticated === true;
// Host Service deployments discover authority runtime state through
// system.runtimes. Retired actor paths are not a product path.
return authenticated && rootCameFromAuthoritativeBootstrap && selectedAuthorityRoot === root;
}
private _shouldSkipHostedShellDaemonDiscovery(hasExplicitDaemonTarget: boolean): boolean {
if (hasExplicitDaemonTarget || this._isHostBootstrapMode() || this._isHostServiceBootstrap()) {
return false;
}
return this._deriveHostedAppName() === 'web';
}
private _shouldRetainBootstrapIdentity(authorityRoot: string): boolean {
const root = authorityRoot.trim();
const selectedAuthorityRoot = (this.dataset.selectedAuthorityRoot ?? '').trim();
const rootSource = this.dataset.rootSource ?? '';
const selectedAuthorityRootSource = this.dataset.selectedAuthorityRootSource ?? '';
const rootCameFromAuthoritativeBootstrap = this._isAuthoritativeBootstrapIdentitySource(rootSource)
|| this._isAuthoritativeBootstrapIdentitySource(selectedAuthorityRootSource);
const authenticated = this.dataset.authenticated === 'true' || this._isAuthenticated === true;
return authenticated
&& root.length > 0
&& rootCameFromAuthoritativeBootstrap
&& (!selectedAuthorityRoot || selectedAuthorityRoot === root);
}
private _ensureHostRuntimeMount(authorityRoot: string, appName: string | null): void {
const existingMount = this.getAttribute('mount')?.trim() ?? '';
if (existingMount.length > 0) {
return;
}
const runtimeId = this._getOrCreateBrowserRuntimeId(authorityRoot, appName);
this.dataset.hostRuntimeId = runtimeId;
this.setAttribute('mount', `system.runtimes.${runtimeId}`);
}
private _getOrCreateBrowserRuntimeId(authorityRoot: string, appName: string | null): string {
const normalizedAuthority = authorityRoot.trim() || 'LOCAL';
const normalizedApp = (appName ?? 'browser').trim().toUpperCase().replace(/[^A-Z0-9_-]/g, '-');
const storageKey = `matrix-browser-runtime-id:${normalizedAuthority}:${normalizedApp}`;
const existing = typeof sessionStorage !== 'undefined' ? sessionStorage.getItem(storageKey) : null;
if (existing && /^RUNTIME-BROWSER-[A-Z0-9_-]+$/.test(existing)) {
return existing;
}
const randomId = this._randomRuntimeSuffix();
const runtimeId = `${BROWSER_RUNTIME_ID_PREFIX}${randomId}`;
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem(storageKey, runtimeId);
}
return runtimeId;
}
private _randomRuntimeSuffix(): string {
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
return crypto.randomUUID().replace(/-/g, '').slice(0, 8).toUpperCase();
}
return Math.random().toString(36).slice(2, 10).toUpperCase();
}
private async _diagnoseBootstrapFailure(err: unknown): Promise<IBootstrapFailureDiagnostics> {
const rawMessage = err instanceof Error ? err.message : String(err);
const lines = [`Cause: ${rawMessage}`];
const classify = (classification: string, hint: string): IBootstrapFailureDiagnostics => ({
classification,
hint,
lines: [...lines, `Classification: ${classification}`, `Hint: ${hint}`],
});
if (/NATS JWT bootstrap failed/i.test(rawMessage)) {
let healthLine = 'HTTP /healthz: not checked';
let bootstrapLine = 'HTTP /api/bootstrap: not checked';
try {
const healthResp = await fetch('/healthz', { credentials: 'same-origin' });
healthLine = `HTTP /healthz: ${healthResp.status} ${healthResp.ok ? 'ok' : 'not ok'}`;
} catch (error) {
healthLine = `HTTP /healthz: unreachable (${error instanceof Error ? error.message : String(error)})`;
}
try {
const bootstrapResp = await fetch('/api/bootstrap', { credentials: 'same-origin' });
bootstrapLine = `HTTP /api/bootstrap: ${bootstrapResp.status} ${bootstrapResp.ok ? 'ok' : 'not ok'}`;
} catch (error) {
bootstrapLine = `HTTP /api/bootstrap: unreachable (${error instanceof Error ? error.message : String(error)})`;
}
lines.push(healthLine, bootstrapLine);
return classify(
'nats-jwt-bootstrap',
'The HiveCast HTTP surface may still be up. This usually means session-to-bus auth or first-boot provisioning failed, not that the whole Host is down.',
);
}
if (/Failed to fetch|NetworkError|fetch/i.test(rawMessage)) {
try {
const healthResp = await fetch('/healthz', { credentials: 'same-origin' });
lines.push(`HTTP /healthz: ${healthResp.status} ${healthResp.ok ? 'ok' : 'not ok'}`);
return classify(
'http-bootstrap',
'The browser reached the HiveCast origin but a bootstrap HTTP request failed. Check /api/bootstrap and auth/session routes before assuming the Host is down.',
);
} catch (error) {
lines.push(`HTTP /healthz: unreachable (${error instanceof Error ? error.message : String(error)})`);
return classify(
'host-gateway-unreachable',
'The browser cannot reach the HiveCast HTTP gateway at all. This is the closest thing to “Host is down” in the bootstrap path.',
);
}
}
if (/websocket|nats|backbone|ECONNREFUSED|timeout/i.test(rawMessage)) {
return classify(
'backbone-connect',
'HTTP bootstrap likely succeeded, but the browser failed while connecting to the NATS/websocket backbone or the routed Host path.',
);
}
return classify(
'bootstrap-runtime',
'The Host may still be running. This failure happened inside browser bootstrap logic after initial HTTP discovery.',
);
}
/**
* FEDERATED MODE: Browser NATS WebSocket transport.
*/
private async _bootstrapFederated(
busRootInput: string,
wsUrl: string,
mountPath: string,
): Promise<void> {
const hostBootstrapMode = this._isHostBootstrapMode();
const busRoot = hostBootstrapMode ? busRootInput.trim() : busRootInput.toLowerCase().trim();
const runtimeInstanceRoot = (this.dataset.runtimeInstanceRoot ?? '').trim();
const sessionAuthorityRoot = (this.dataset.sessionAuthorityRoot ?? '').trim();
const selectedAuthorityRoot = (this.dataset.selectedAuthorityRoot ?? '').trim();
const identityRoot = sessionAuthorityRoot || selectedAuthorityRoot;
const appName = this.getAttribute('app-name')?.trim() ?? '';
if (!hostBootstrapMode && !runtimeInstanceRoot && identityRoot && !appName) {
throw new Error(
'MatrixDSLHost: bootstrap did not provide app-name. '
+ 'Identity tab root requires appName; see four-roots.md.',
);
}
const tabRoot = hostBootstrapMode
? busRoot
: runtimeInstanceRoot
? runtimeInstanceRoot
: identityRoot
? getOrCreateIdentityTabRoot(appName, identityRoot)
: getOrCreateTabRealm(busRoot);
this._setOptionalDatasetValue('runtimeInstanceRoot', tabRoot);
const declaredAuthMode = this._bootstrapTransportPlan?.authMode;
// Prefer the explicit transport plan from /api/bootstrap.
// If absent, probe the NATS auth endpoint to identify the configured mode.
let jwtProvider: (() => Promise<{ jwt: string; seed: string }>) | undefined;
let cachedJwt: { jwt: string; seed: string } | null = null;
const buildNatsAuthEndpoint = (): string => {
const base = this.dataset.natsAuthEndpoint || '/api/nats-jwt';
const url = new URL(base, window.location.origin);
const platformRoot = (this.dataset.platformRoot ?? '').trim();
const requestedDaemonRoot = (this.getAttribute('daemon-root') ?? this.getAttribute('daemon-realm') ?? '').trim();
const selectedAuthorityRoot = (this.dataset.selectedAuthorityRoot ?? '').trim();
const anonymousSession = !this._isAuthenticated
&& (this._bootstrapTransportPlan?.sessionMode === 'anonymous' || this.dataset.sessionMode === 'anonymous');
const daemonRoot = anonymousSession
? (platformRoot && requestedDaemonRoot === platformRoot ? platformRoot : '')
: (selectedAuthorityRoot || requestedDaemonRoot);
if (daemonRoot) {
url.searchParams.set('daemonRoot', daemonRoot);
} else if (anonymousSession) {
url.searchParams.delete('daemonRoot');
url.searchParams.delete('root');
}
if (tabRoot) {
url.searchParams.set('clientRoot', tabRoot);
}
return url.pathname + url.search;
};
const fetchNatsAuthPayload = async (): Promise<{ jwt?: string; seed?: string; user?: string; pass?: string; mode?: string }> => {
const endpoint = buildNatsAuthEndpoint();
const response = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'natsAuth.fetch',
async () => fetch(endpoint, { method: 'POST', credentials: 'same-origin' }),
{
authMode: declaredAuthMode ?? 'auto',
endpoint,
},
);
const payload = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'natsAuth.parse',
async () => response.json().catch(() => ({})) as Promise<{ jwt?: string; seed?: string; user?: string; pass?: string; mode?: string; error?: string }>,
{
authMode: declaredAuthMode ?? 'auto',
endpoint,
},
);
if (!response.ok) {
const detail = typeof payload.error === 'string' && payload.error.length > 0
? payload.error
: `HTTP ${response.status}`;
throw new Error(`NATS auth request failed (${response.status}): ${detail}`);
}
return payload;
};
const fetchJwtCredentials = async (): Promise<{ jwt: string; seed: string } | null> => {
if (!this._isAuthenticated) {
return null;
}
let lastError: Error | null = null;
for (let attempt = 0; attempt < 8; attempt++) {
const endpoint = buildNatsAuthEndpoint();
const response = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'natsJwt.fetch',
async () => fetch(endpoint, { method: 'POST', credentials: 'same-origin' }),
{
attempt: attempt + 1,
authMode: declaredAuthMode ?? 'auto',
},
);
if (response.ok) {
const payload = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'natsJwt.parse',
async () => response.json() as Promise<{ jwt?: string; seed?: string; user?: string; pass?: string; mode?: string }>,
{
attempt: attempt + 1,
authMode: declaredAuthMode ?? 'auto',
},
);
if (payload.jwt && payload.seed) {
return { jwt: payload.jwt, seed: payload.seed };
}
// Static NATS mode — store user/pass for transport auth
if (payload.mode === 'static' && payload.user && payload.pass) {
this.dataset.natsUser = payload.user;
this.dataset.natsPass = payload.pass;
return null; // No JWT, use user/pass auth instead
}
// Anonymous mode — no credentials needed
if (payload.mode === 'anonymous') {
return null;
}
lastError = new Error('NATS JWT response missing jwt or seed');
} else {
const bodyText = await response.text().catch(() => '');
const detail = bodyText.trim();
// This deployment does not expose operator/JWT auth, so plain WS token mode is expected.
if (
response.status === 401
|| response.status === 403
|| response.status === 404
|| (response.status === 503 && /not configured|no operatorseed|no account selected|failed to create nats jwt/i.test(detail))
) {
return null;
}
// Provisioning and rate-limit windows are transient immediately after login/startup.
if (response.status === 429 || response.status >= 500 || /provisioning operation is in progress/i.test(detail)) {
lastError = new Error(`NATS JWT temporary failure (${response.status}): ${detail || 'no body'}`);
await new Promise((resolve) => setTimeout(resolve, Math.min(250 * (attempt + 1), 1000)));
continue;
}
throw new Error(`NATS JWT request failed (${response.status}): ${detail || 'no body'}`);
}
}
if (lastError) {
throw lastError;
}
throw new Error('NATS JWT request failed without a response');
};
if (declaredAuthMode === 'jwt') {
const payload = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'natsAuth.declared',
async () => fetchNatsAuthPayload(),
{ authMode: declaredAuthMode },
);
if (!payload.jwt || !payload.seed) {
throw new Error('Declared NATS authMode "jwt" but /api/nats-jwt did not return jwt+seed');
}
cachedJwt = { jwt: payload.jwt, seed: payload.seed };
jwtProvider = async () => {
if (cachedJwt) {
const next = cachedJwt;
cachedJwt = null;
return next;
}
const refreshed = await fetchNatsAuthPayload();
if (!refreshed.jwt || !refreshed.seed) {
throw new Error('Declared NATS authMode "jwt" but refresh did not return jwt+seed');
}
return { jwt: refreshed.jwt, seed: refreshed.seed };
};
} else if (declaredAuthMode === 'static') {
const payload = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'natsAuth.declared',
async () => fetchNatsAuthPayload(),
{ authMode: declaredAuthMode },
);
if (payload.mode !== 'static' || !payload.user || !payload.pass) {
throw new Error('Declared NATS authMode "static" but /api/nats-jwt did not return user/pass');
}
this.dataset.natsUser = payload.user;
this.dataset.natsPass = payload.pass;
} else if (declaredAuthMode === 'anonymous') {
// No extra auth bootstrap required.
} else {
try {
const initial = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'natsJwt.bootstrap',
async () => fetchJwtCredentials(),
{ authMode: declaredAuthMode ?? 'auto' },
);
if (initial) {
cachedJwt = initial;
jwtProvider = async () => {
if (cachedJwt) {
const next = cachedJwt;
cachedJwt = null;
return next;
}
const refreshed = await fetchJwtCredentials();
if (!refreshed) {
throw new Error('NATS JWT refresh unavailable: operator mode no longer configured');
}
return refreshed;
};
}
} catch (error) {
throw new Error(`NATS JWT bootstrap failed: ${error instanceof Error ? error.message : String(error)}`);
}
if (!jwtProvider) {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info('[MatrixDSLHost] NATS JWT unavailable — connecting with configured non-JWT transport');
}
}
if (declaredAuthMode === 'anonymous') {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info('[MatrixDSLHost] NATS auth plan → anonymous');
} else if (declaredAuthMode === 'static') {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info('[MatrixDSLHost] NATS auth plan → static credentials via /api/nats-jwt');
} else if (declaredAuthMode === 'jwt') {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info('[MatrixDSLHost] NATS auth plan → JWT via /api/nats-jwt');
} else if (!jwtProvider) {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info('[MatrixDSLHost] NATS operator mode not available — connecting without JWT auth');
}
const connectBackbone = async () => createBrowserNatsTransport({
root: tabRoot,
wsUrl,
token: this.dataset.natsToken,
user: this.dataset.natsUser,
pass: this.dataset.natsPass,
jwtProvider,
});
const shouldRetryJwtReadiness = (error: unknown): boolean => {
const message = error instanceof Error ? error.message : String(error);
return declaredAuthMode === 'jwt'
&& Boolean(jwtProvider)
&& /Authorization Violation|authentication violation/i.test(message);
};
let rawAdapter: Awaited<ReturnType<typeof connectBackbone>> | null = null;
let lastConnectError: unknown = null;
for (let attempt = 0; attempt < 4; attempt++) {
try {
rawAdapter = await profileBrowserSpan(
HOST_PROFILE_SCOPE,
'nats.connect',
async () => connectBackbone(),
{
root: tabRoot,
authMode: declaredAuthMode ?? 'auto',
attempt: attempt + 1,
},
);
lastConnectError = null;
break;
} catch (error) {
lastConnectError = error;
if (!shouldRetryJwtReadiness(error) || attempt === 3) {
throw error;
}
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.warn(`[MatrixDSLHost] NATS JWT account not ready yet; retrying backbone connect (${attempt + 2}/4)...`);
await new Promise((resolve) => setTimeout(resolve, 250 * (attempt + 1)));
}
}
if (!this._rawAdapter && lastConnectError) {
throw lastConnectError;
}
if (!rawAdapter) {
throw new Error('NATS backbone connection failed before an adapter was created');
}
this._rawAdapter = rawAdapter;
this._transport = this._rawAdapter;
this.dataset.status = 'connected';
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info(`[MatrixDSLHost] NATS backbone connected: ${wsUrl} (${tabRoot})`);
if (this._wrapTransport && this._transport) {
this._transport = this._wrapTransport(this._transport);
}
this._runtime = new MatrixRuntime({
transport: this._transport ?? undefined,
domStrategy: new BrowserDomStrategy(),
});
const rootCtx = this._runtime.getRootContext!();
this._context = mountPath ? rootCtx.derive(mountPath) : rootCtx;
await this._applyHostedWebappMetadata();
this._routingPeer = this._createTransportRoutingPeer(tabRoot);
}
private _deriveHostedAppName(): string | null {
const routeKeyRoute = this._parseRouteKeyWebappRouteFromLocation();
if (routeKeyRoute) {
return routeKeyRoute.appName;
}
const pathname = typeof window !== 'undefined' && typeof window.location?.pathname === 'string'
? window.location.pathname
: '';
const match = /^\/apps\/([^/]+)/.exec(pathname);
const rawAppName = match?.[1];
if (!rawAppName) return null;
try {
const decoded = decodeURIComponent(rawAppName).trim();
return decoded.length > 0 ? decoded : null;
} catch {
return null;
}
}
private _parseRouteKeyWebappRouteFromLocation(): IRouteKeyWebappRoute | null {
const pathname = typeof window !== 'undefined' && typeof window.location?.pathname === 'string'
? window.location.pathname
: '';
if (!pathname.startsWith('/')) {
return null;
}
const withoutLeadingSlash = pathname.slice(1);
const firstSlash = withoutLeadingSlash.indexOf('/');
if (firstSlash <= 0) {
return null;
}
let routeKey: string;
try {
routeKey = decodeURIComponent(withoutLeadingSlash.slice(0, firstSlash)).trim().toLowerCase();
} catch {
return null;
}
if (!isValidRouteKey(routeKey)) {
return null;
}
const afterRouteKey = withoutLeadingSlash.slice(firstSlash + 1);
const appPath = afterRouteKey.startsWith('apps/')
? afterRouteKey.slice('apps/'.length)
: afterRouteKey;
const appSlash = appPath.indexOf('/');
const rawAppName = appSlash === -1 ? appPath : appPath.slice(0, appSlash);
let appName: string;
try {
appName = decodeURIComponent(rawAppName).trim();
} catch {
return null;
}
if (!/^[A-Za-z0-9._-]+$/.test(appName) || appName === 'web') {
return null;
}
return {
appName,
routeKey,
};
}
private async _applyHostedWebappMetadata(): Promise<void> {
if (!this._context) return;
const appName = this._deriveHostedAppName();
if (!appName) return;
if (appName === 'web') return;
if (this._parseRouteKeyWebappRouteFromLocation()) {
return;
}
const assetHostRoot = (this.dataset.assetHostRoot ?? '').trim();
const selectedAuthorityRoot = (this.dataset.selectedAuthorityRoot ?? '').trim();
const platformServiceRoot = (this.dataset.platformServiceRoot ?? '').trim();
const isPlatformAdmin = this.dataset.isPlatformAdmin === 'true';
if (platformServiceRoot && selectedAuthorityRoot === platformServiceRoot && !isPlatformAdmin) {
return;
}
if (assetHostRoot && selectedAuthorityRoot && assetHostRoot !== selectedAuthorityRoot && !isPlatformAdmin) {
return;
}
const entry = await this._loadHostedWebappRegistryEntry(appName);
const metadataRecord = registryRecord(entry.metadata);
const artifact = registryArtifact(metadataRecord.artifact);
const packageName = registryString(entry.placement.packageRef) ?? registryString(entry.placement.packageId);
const packageVersion = registryString(metadataRecord.packageVersion);
const sourceKind = registrySourceKind(metadataRecord.sourceKind)
?? (registryString(metadataRecord.origin) ? 'external-webapp' : 'installed-webapp');
const registrationSource = registryRegistrationSource(metadataRecord.registrationSource);
const deploymentInstanceId = registryString(entry.placement.deploymentInstanceId) ?? registryString(entry.placement.runtimeId);
const distPath = registryString(metadataRecord.distPath);
const entryFile = registryString(metadataRecord.entryFile);
const buildGitSha = registryString(artifact.gitSha);
const artifactBuiltAt = registryString(artifact.builtAt);
const artifactSignature = registryString(artifact.signature);
const artifactFreshness = registryArtifactFreshness(artifact.freshness);
const artifactFreshnessReason = registryString(artifact.reason);
const metadata: IComponentRuntimeMetadata = {
versionSource: packageVersion ? 'webapp-registry' : 'undeclared',
sourceKind,
appName,
...(packageName ? { packageName } : {}),
...(packageVersion ? { version: packageVersion } : {}),
...(registrationSource ? { registrationSource } : {}),
...(deploymentInstanceId ? { deploymentInstanceId } : {}),
...(distPath ? { distPath } : {}),
...(entryFile ? { entryFile } : {}),
...(buildGitSha ? { buildGitSha } : {}),
...(artifactBuiltAt ? { artifactBuiltAt } : {}),
...(artifactSignature ? { artifactSignature } : {}),
...(artifactFreshness ? { artifactFreshness } : {}),
...(artifactFreshnessReason ? { artifactFreshnessReason } : {}),
};
if (metadata.artifactFreshness && metadata.artifactFreshness !== 'current') {
// eslint-disable-next-line no-console -- browser bootstrap visibility
console.warn(
`[MatrixDSLHost] Hosted webapp artifact for "${appName}" is ${metadata.artifactFreshness}: ${metadata.artifactFreshnessReason ?? 'no reason provided'}`
);
}
const contextual = this._context as IMatrixContext & {
setService?: (key: string | symbol, service: unknown) => void;
};
contextual.setService?.(COMPONENT_RUNTIME_METADATA, metadata);
}
private async _loadHostedWebappRegistryEntry(appName: string): Promise<ServiceRegistryEntryV1> {
if (!this._context) {
throw new Error(`system.registry lookup for hosted app '${appName}' requires a Matrix context`);
}
const namespaceRoot = this._hostedWebappRegistryNamespaceRoot();
const response = await RequestReply.execute<Record<string, unknown>, IRegistryQueryResponse>(
this._context,
`${namespaceRoot}/system.registry`,
'registry.query',
{
namespaceRoot,
mount: appName,
kind: 'app',
openable: true,
},
{ timeoutMs: 5000 },
);
const entries = Array.isArray(response.entries) ? response.entries : [];
const entry = entries
.map((candidate) => serviceRegistryEntry(candidate))
.find((candidate) => candidate !== null
&& candidate.kind === 'app'
&& candidate.app?.openable === true
&& candidate.mount === appName);
if (!entry) {
throw new Error(`app '${appName}' not registered by ${namespaceRoot}/system.registry`);
}
return entry;
}
private _hostedWebappRegistryNamespaceRoot(): string {
const assetHostRoot = (this.dataset.assetHostRoot ?? '').trim();
if (assetHostRoot) {
return assetHostRoot;
}
const busRoot = (this.getAttribute('bus-root') ?? '').trim();
if (!busRoot) {
throw new Error('Hosted webapp registry lookup requires assetHostRoot from /api/bootstrap');
}
return busRoot;
}
/**
* ROOT LOCAL MODE: FederationPeer without backbone (local pub/sub only).
*/
private _bootstrapRootLocal(busRootInput: string, mountPath: string): void {
const baseRoot = busRootInput.toLowerCase().trim();
// Create browser router (FederationPeer in local-only mode)
const result: BrowserRouterResult = getBrowserRouter({ baseRealm: baseRoot });
this._peer = result.router;
this._routingPeer = result.router;
this._rawAdapter = browserRouterTransportAdapter(result.adapter, result.router);
this._transport = this._rawAdapter;
// Apply transport wrapper if provided
if (this._wrapTransport) {
this._transport = this._wrapTransport(this._transport);
}
// Create runtime with federation adapter
this._runtime = new MatrixRuntime({
transport: this._transport,
domStrategy: new BrowserDomStrategy(),
});
const rootCtx = this._runtime.getRootContext!();
this._context = mountPath ? rootCtx.derive(mountPath) : rootCtx;
}
private _createTransportRoutingPeer(localRoot: string): IHostRoutingPeer {
return {
hasBackbone: true,
localRoot,
localRealm: localRoot,
invoke: async <T>(
target: string,
opOrRequest: string | { op: string; payload?: unknown },
payloadOrTimeout?: unknown,
timeoutMs?: number,
): Promise<T> => {
if (!this._context) {
throw new Error('MatrixDSLHost transport router is not initialized');
}
if (typeof opOrRequest === 'string') {
const op = opOrRequest;
const payload = payloadOrTimeout;
return await RequestReply.execute<unknown, T>(this._context, target, op, payload, {
timeoutMs,
});
}
const op = opOrRequest.op;
const payload = opOrRequest.payload;
const resolvedTimeoutMs = typeof payloadOrTimeout === 'number' ? payloadOrTimeout : timeoutMs;
return await RequestReply.execute<unknown, T>(this._context, target, op, payload, {
timeoutMs: resolvedTimeoutMs,
});
},
route: async (topic: string, payload: unknown): Promise<void> => {
if (!this._transport) {
throw new Error('MatrixDSLHost transport router is not initialized');
}
this._transport.publish(topic, payload);
},
};
}
/**
* LOCAL MODE: Plain in-memory transport, no federation overhead.
*/
private _bootstrapLocal(mountPath: string): void {
// No federation — just in-memory broker + transport
this._runtime = new MatrixRuntime({
domStrategy: new BrowserDomStrategy(),
});
const rootCtx = this._runtime.getRootContext!();
this._context = mountPath ? rootCtx.derive(mountPath) : rootCtx;
}
// ═══════════════════════════════════════════════════════════════════════════
// $join Responder
// ═══════════════════════════════════════════════════════════════════════════
/**
* Subscribe to this host's $inbox to respond to $join from children.
*
* When children call _receiveContext(derivedCtx), they send $join to
* parent.mount/$inbox. This responder acks them so they don't timeout.
*/
private _installJoinResponder(): void {
if (!this._context) return;
const hostMount = this._context.mount;
const hostInbox = TopicRouter.inbox(hostMount);
this._joinUnsub = this._context.transport.subscribe(
hostInbox,
(rawPayload: unknown) => {
const envelope = rawPayload as MxEnvelope;
if (!envelope || envelope.op !== '$join') return;
const joinPayload = envelope.payload as { id?: string; mount?: string } | undefined;
const childMount = joinPayload?.mount;
if (!childMount) return;
const childInbox = TopicRouter.inbox(childMount);
this._context!.transport.publish(childInbox, {
op: '$join_ack',
payload: { status: 'ok', supervisor: hostMount },
});
},
);
}
// ═══════════════════════════════════════════════════════════════════════════
// Child Mounting
// ═══════════════════════════════════════════════════════════════════════════
/**
* Mount all light DOM children that are custom elements.
* Uses explicit _receiveContext() — NOT AUTO-PULL (because this host
* is not a MatrixActorHtmlElement and won't be found by _findParentShell).
*/
private async _mountChildren(): Promise<void> {
if (!this._context) return;
const mountPromises: Promise<void>[] = [];
for (const child of Array.from(this.children)) {
if (!(child instanceof HTMLElement)) continue;
mountPromises.push(this._mountChild(child));
}
await Promise.all(mountPromises);
}
/**
* Mount a single child element with a derived context.
*/
private async _mountChild(element: HTMLElement): Promise<void> {
if (!this._context) return;
const tagName = element.tagName.toLowerCase();
// Skip non-custom elements (no hyphen = not a CE)
if (!tagName.includes('-')) return;
// Skip script/style
if (tagName === 'script' || tagName === 'style') return;
// Wait for custom element definition (handles script loading order)
await customElements.whenDefined(tagName);
// Derive child ID from name, id, or tag
const childId = (
element.getAttribute('name') ||
element.id ||
tagName
).toLowerCase();
const childContext = this._context.derive(childId);
const shell = matrixChildElementHooks(element);
if (typeof shell._receiveContext === 'function') {
await shell._receiveContext(childContext);
} else if (typeof shell.mount === 'function') {
await shell.mount(childContext);
} else if (typeof shell.setContext === 'function') {
shell.setContext(childContext);
}
// Plain HTML elements without Matrix interface are silently skipped
}
private _browserRuntimeInventory(runtimeId: string, contextMount: string, hostBootstrapMode: boolean): IBrowserRuntimeInventoryEntry[] {
const inventory: IBrowserRuntimeInventoryEntry[] = [];
const runtimeMount = `system.runtimes.${runtimeId}`;
inventory.push({
mount: runtimeMount,
type: 'BrowserRuntimeControlActor',
surface: 'runtime',
runtimeId,
claimKind: 'local-only',
description: 'Browser runtime actor for this tab.',
hasChildren: true,
});
for (const child of Array.from(this.children)) {
if (!(child instanceof HTMLElement)) continue;
const tag = child.tagName.toLowerCase();
if (!tag.includes('-')) continue;
const name = child.getAttribute('name') || child.getAttribute('id') || tag;
const localMount = hostBootstrapMode && contextMount
? `${contextMount}.${name}`
: name;
inventory.push({
mount: localMount,
type: child.constructor.name !== 'HTMLElement' ? child.constructor.name : tag,
surface: 'dom',
runtimeId,
claimKind: 'local-only',
hasChildren: true,
});
}
return inventory;
}
private async _ensureBrowserRuntimeControl(
runtimeId: string,
runtimeMount: string,
controlMount: string,
): Promise<void> {
if (!(this._runtime instanceof MatrixRuntime)) {
return;
}
if (!this._runtimeControlStartedAt) {
this._runtimeControlStartedAt = new Date().toISOString();
}
await mountBrowserRuntimeControl({
runtime: this._runtime,
runtimeId,
runtimeMount,
controlMount,
startedAt: this._runtimeControlStartedAt,
getState: (): BrowserRuntimeLifecycleState => this._initialized ? 'live' : 'closing',
getInventory: () => this._browserRuntimeInventory(
runtimeId,
this._context?.mount ?? '',
this._isHostBootstrapMode(),
),
});
}
private _runtimeInventoryVersionForHost(): number {
let version = 0;
for (const child of Array.from(this.children)) {
if (!(child instanceof HTMLElement)) continue;
const shell = matrixActorElementVersionSource(child);
const childVersion = shell._actor?.children?.version;
if (typeof childVersion === 'number' && Number.isFinite(childVersion)) {
version += childVersion;
}
}
return version;
}
private _scheduleRuntimeInventoryRefresh(): void {
if (!this._browserRuntimeRegistryState) {
return;
}
if (this._runtimeInventoryClaimRefreshTimer) {
clearTimeout(this._runtimeInventoryClaimRefreshTimer);
}
this._runtimeInventoryClaimRefreshTimer = setTimeout(() => {
this._runtimeInventoryClaimRefreshTimer = null;
void this._refreshRuntimeInventory();
}, 250);
}
private async _refreshRuntimeInventory(): Promise<void> {
const state = this._browserRuntimeRegistryState;
if (!this._context || !state) {
return;
}
const inventory = this._browserRuntimeInventory(
state.runtimeId,
state.contextMount,
state.hostBootstrapMode,
);
const inventoryVersion = this._runtimeInventoryVersionForHost();
try {
this._runtimeInventoryVersion = inventoryVersion;
await this._replaceBrowserRuntimeClaims(state, inventory, inventoryVersion);
} catch {
// Registry lease renewal keeps liveness; the next DOM mutation retries inventory claims.
}
}
// ═══════════════════════════════════════════════════════════════════════════
// Root Context
// ═══════════════════════════════════════════════════════════════════════════
/**
* Parse root and federation attributes into RootContext.
*/
private _parseRootAttributes(): void {
const rawRoot = this.getAttribute('bus-root');
if (!rawRoot) return;
const baseRoot = rawRoot.toLowerCase().trim();
if (!isValidRealm(baseRoot)) {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.error(`[MatrixDSLHost] Invalid root: "${rawRoot}"`);
return;
}
const rawFederation = this.getAttribute('federation') || 'auto';
const federation = rawFederation.toLowerCase().trim() as FederationMode;
const validModes: FederationMode[] = ['auto', 'local', 'public', 'disabled'];
if (!validModes.includes(federation)) {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.error(`[MatrixDSLHost] Invalid federation mode: "${rawFederation}"`);
return;
}
let tabRoot: string | undefined;
if (federation !== 'disabled') {
tabRoot = getOrCreateTabRealm(baseRoot);
}
this._rootContext = createRootContext(baseRoot, federation, tabRoot);
}
/**
* Propagate RootContext to child elements that support it.
*/
private _propagateRootToChildren(): void {
const rootContext = this._rootContext;
if (!rootContext) return;
const children = this.querySelectorAll('*');
children.forEach((child: Element) => {
const contextReceiver = matrixRootContextReceiver(child);
if (typeof contextReceiver._receiveContext === 'function') {
// Only propagate root context (not a full IMatrixContext)
// MatrixActorHtmlElement._receiveContext handles this special case
contextReceiver._receiveContext({ rootContext, realmContext: rootContext });
}
});
}
// ═══════════════════════════════════════════════════════════════════════════
// Mutation Observation
// ═══════════════════════════════════════════════════════════════════════════
private _setupMutationObserver(): void {
this._observer = new MutationObserver(mutations => {
for (const mutation of mutations) {
if (mutation.type === 'childList') {
for (const node of Array.from(mutation.addedNodes)) {
if (node instanceof HTMLElement) {
void this._mountChild(node).then(() => this._scheduleRuntimeInventoryRefresh());
}
}
if (mutation.removedNodes.length > 0) {
this._scheduleRuntimeInventoryRefresh();
}
}
}
});
this._observer.observe(this, { childList: true });
}
// ═══════════════════════════════════════════════════════════════════════════
// Loading Element Management
// ═══════════════════════════════════════════════════════════════════════════
/**
* Manage a sibling loading element and this host's visibility.
* Looks for element with id from `loading-el` attribute, or `#loading`.
*/
private _manageLoadingElement(status: string, error?: unknown): void {
const loadingId = this.getAttribute('loading-el') || 'loading';
const loadingEl = document.getElementById(loadingId);
if (status === 'initializing') {
if (loadingEl) loadingEl.style.display = '';
if (this._splashShownAt === null) {
this._splashShownAt = performance.now();
}
this.style.display = 'none';
} else if (status === 'connected' || status === 'local' || status === 'login-required') {
this._scheduleSplashHide(loadingEl);
this.style.display = '';
} else if (status === 'disconnected') {
// Show app in offline mode — UI loads, just no backbone
this._scheduleSplashHide(loadingEl);
this.style.display = '';
} else if (status === 'error') {
this.style.display = 'none';
if (loadingEl) {
const root = this.getAttribute('bus-root') ?? '(not set)';
const backbone = this.getAttribute('backbone') || '(none)';
const errMsg = error instanceof Error ? error.message : String(error);
const esc = (s: string) => s.replace(/</g, '&lt;').replace(/>/g, '&gt;');
loadingEl.outerHTML = `
<div style="max-width: 700px; margin: 40px auto; padding: 20px; background: #ffebee; border: 1px solid #f44336; border-radius: 8px; font-family: monospace; color: #c62828;">
<strong style="font-size: 16px;">Bootstrap Failed</strong>
<pre style="background: #fff0f0; padding: 12px; border-radius: 4px; overflow-x: auto; margin: 12px 0;">${esc(errMsg)}</pre>
<table style="width: 100%; border-collapse: collapse; font-size: 13px;">
<tr><td style="padding: 4px 8px; color: #666;">Root</td><td style="padding: 4px 8px;">${esc(root)}</td></tr>
<tr><td style="padding: 4px 8px; color: #666;">Backbone</td><td style="padding: 4px 8px;">${esc(backbone)}</td></tr>
<tr><td style="padding: 4px 8px; color: #666;">Status</td><td style="padding: 4px 8px;">${this._peer?.hasBackbone ? 'connected' : 'not connected'}</td></tr>
</table>
</div>`;
}
}
}
/**
* Hide the splash with a minimum-visible-time guard and a fade-out.
*
* Bootstrap can complete in <100ms on a warm cache, which makes the splash
* appear and disappear in a single frame — users perceive a flash rather
* than a brand frame. We enforce a 700ms minimum visible time and fade the
* last 200ms via opacity so the dismissal reads as intentional. The
* `splash-min-ms` attribute on `<matrix-dsl-host>` overrides the default;
* `splash-min-ms="0"` restores the original instant-hide behavior.
*/
private _scheduleSplashHide(loadingEl: HTMLElement | null): void {
if (!loadingEl || this._splashHideScheduled) return;
this._splashHideScheduled = true;
const minMs = (() => {
const raw = this.getAttribute('splash-min-ms');
if (raw === null) return 700;
const n = Number.parseInt(raw, 10);
return Number.isFinite(n) && n >= 0 ? n : 700;
})();
const fadeMs = Math.min(200, Math.floor(minMs / 3));
const shownAt = this._splashShownAt ?? performance.now();
const elapsed = performance.now() - shownAt;
const remaining = Math.max(0, minMs - elapsed - fadeMs);
const startFade = () => {
loadingEl.style.transition = `opacity ${fadeMs}ms ease-out`;
loadingEl.style.opacity = '0';
window.setTimeout(() => {
loadingEl.style.display = 'none';
loadingEl.style.opacity = '';
loadingEl.style.transition = '';
}, fadeMs);
};
if (remaining > 0) {
window.setTimeout(startFade, remaining);
} else {
startFade();
}
}
private _redirectToDeviceLogin(): void {
if (this.dataset.loginRedirectStarted === 'true') {
return;
}
this.dataset.loginRedirectStarted = 'true';
const startUrl = this.dataset.loginStartUrl || '/_auth/device-session/start';
const loginUrl = new URL(startUrl, window.location.origin);
loginUrl.searchParams.set('returnTo', `${window.location.pathname}${window.location.search}${window.location.hash}`);
window.location.assign(loginUrl.toString());
}
private _shouldRedirectToDeviceLogin(hostedAppName: string | null): boolean {
const normalizedAppName = hostedAppName?.trim().toLowerCase() ?? '';
return normalizedAppName === 'edge';
}
private _redirectToPlatformLogin(): void {
if (this.dataset.loginRedirectStarted === 'true') {
return;
}
this.dataset.loginRedirectStarted = 'true';
const returnTo = `${window.location.pathname}${window.location.search}${window.location.hash}`;
const loginUrl = new URL('/apps/web/', window.location.origin);
loginUrl.hash = `login?redirect=${encodeURIComponent(returnTo)}`;
window.location.assign(loginUrl.toString());
}
// ═══════════════════════════════════════════════════════════════════════════
// Events
// ═══════════════════════════════════════════════════════════════════════════
private _fireReadyEvent(): void {
this.dispatchEvent(new CustomEvent('matrix-dsl-ready', {
bubbles: true,
detail: {
peer: this._peer,
runtime: this._runtime,
context: this._context,
root: this.getAttribute('bus-root'),
realm: this.getAttribute('bus-root'),
backbone: this.getAttribute('backbone'),
hasBackbone: this._peer?.hasBackbone ?? false,
status: this.dataset.status,
},
}));
}
/**
* Async runtime registry discovery — fires after full bootstrap (children already mounted).
*
* Confirms the runtime registry actor is reachable, then publishes readiness on the root
* $events topic so child apps receive it through their transport subscriptions.
* No DOM events — pure transport pub/sub so timing is deterministic.
*
* Publishes:
* { op: 'daemonReady', payload: { root } } — on success
* { op: 'daemonError', payload: { root, error } } — on failure
* { op: 'daemonReady', payload: { root: '', skipped: true } } — no explicit runtime target set
*/
private async _discoverDaemon(): Promise<void> {
if (!this._context || !this._runtime) return;
const rootCtx = this._runtime.getRootContext!();
const rootEventsTopic = TopicRouter.events('');
const daemonRoot = this.getAttribute('daemon-root') ?? this.getAttribute('daemon-realm');
if (this._isHostBootstrapMode() || this._isHostServiceBootstrap()) {
const hostRoot = daemonRoot || this.getAttribute('bus-root') || '';
if (!hostRoot) {
this.dataset.daemonStatus = 'skipped';
rootCtx.transport.publish(rootEventsTopic, {
op: 'daemonReady',
payload: { root: '', realm: '', skipped: true, hostMode: true },
});
return;
}
this.dataset.daemonStatus = 'connected';
rootCtx.transport.publish(rootEventsTopic, {
op: 'daemonReady',
payload: { root: hostRoot, realm: hostRoot, hostMode: true },
});
rootCtx.transport.publish(rootEventsTopic, {
op: 'hostReady',
payload: { root: hostRoot },
});
void this._registerWithRuntimeRegistry(hostRoot);
return;
}
if (!daemonRoot) {
// No explicit runtime target configured — signal immediately so children enable without waiting
this.dataset.daemonStatus = 'skipped';
rootCtx.transport.publish(rootEventsTopic, {
op: 'daemonReady',
payload: { root: '', realm: '', skipped: true },
});
return;
}
if (this._daemonDiscoveryInFlight) {
return;
}
const controlPlaneRoot = this._controlPlaneRootForSingletons(daemonRoot);
const runtimeRoot = transportRootForContext(this._context) ?? '(unknown)';
const natsWsUrl = this.getAttribute('nats-ws-url') ?? this.getAttribute('backbone') ?? '(none)';
// First attempt uses a shorter timeout (500ms) — if the registry is ready,
// it responds in <10ms. If it's still loading, we fail fast and retry
// rather than wasting 2.5s on the first attempt.
const firstAttemptTimeoutMs = 500;
const attemptTimeoutMs = 2_500;
const retryDelaysMs = [100, 250, 500, 1_000, 2_000] as const;
const finishDiscoveryProfile = createBrowserProfileSpan(HOST_PROFILE_SCOPE, 'daemonDiscovery', {
daemonRoot: daemonRoot ?? '',
controlPlaneRoot,
runtimeRoot,
});
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info(`[MatrixDSLHost] runtime registry discovery: targetRoot="${controlPlaneRoot}", authorityRoot="${daemonRoot}", runtimeRoot="${runtimeRoot}", natsWsUrl="${natsWsUrl}"`);
const runtimeRegistryTarget = this._runtimeRegistryTargetForRoot(controlPlaneRoot);
const discoveryTargets: Array<{
readonly target: string;
readonly kind: 'runtime-registry';
}> = [{ target: runtimeRegistryTarget, kind: 'runtime-registry' }];
const discoveryOp = '$ping';
let lastError = 'Unknown error';
this._daemonDiscoveryInFlight = true;
this._stopDaemonDiscoveryRetry();
try {
for (let attempt = 0; attempt <= retryDelaysMs.length; attempt += 1) {
let attemptSucceeded = false;
for (const discoveryTarget of discoveryTargets) {
try {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info(
`[MatrixDSLHost] Sending ${discoveryOp} to "${discoveryTarget.target}" ` +
`(attempt ${attempt + 1}/${retryDelaysMs.length + 1}, timeout ${attemptTimeoutMs}ms)...`,
);
await RequestReply.execute<Record<string, unknown>, unknown>(
this._context,
discoveryTarget.target,
discoveryOp,
{},
{ timeoutMs: attempt === 0 ? firstAttemptTimeoutMs : attemptTimeoutMs },
);
this.dataset.daemonStatus = 'connected';
this._daemonDiscoveryRetryDelayMs = 1_000;
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.info(
`[MatrixDSLHost] ${discoveryOp} success — system.runtimes at "${controlPlaneRoot}" is reachable`,
);
rootCtx.transport.publish(rootEventsTopic, {
op: 'daemonReady',
payload: {
root: daemonRoot,
realm: daemonRoot,
authorityRuntime: true,
registryTarget: runtimeRegistryTarget,
},
});
// Register this browser tab as a runtime in the namespace registry.
void this._registerWithRuntimeRegistry(controlPlaneRoot);
finishDiscoveryProfile({
result: 'connected',
attempts: attempt + 1,
targetKind: discoveryTarget.kind,
});
attemptSucceeded = true;
return;
} catch (err) {
lastError = err instanceof Error ? err.message : String(err);
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.warn(
`[MatrixDSLHost] Host Service runtime discovery failed at "${discoveryTarget.target}": ${lastError}.`,
);
}
}
if (!attemptSucceeded) {
const retryDelayMs = retryDelaysMs[attempt];
if (retryDelayMs === undefined) break;
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.warn(
`[MatrixDSLHost] Runtime registry discovery attempt ${attempt + 1}/${retryDelaysMs.length + 1} failed: ` +
`${lastError}. Retrying in ${retryDelayMs}ms...`,
);
await new Promise<void>((resolve) => setTimeout(resolve, retryDelayMs));
}
}
const backgroundRetryDelayMs = this._daemonDiscoveryRetryDelayMs;
this._daemonDiscoveryRetryDelayMs = Math.min(this._daemonDiscoveryRetryDelayMs * 2, 10_000);
this.dataset.daemonStatus = 'retrying';
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.warn(
`[MatrixDSLHost] Runtime registry discovery failed: root="${controlPlaneRoot}", authorityRoot="${daemonRoot}", runtimeRoot="${runtimeRoot}", ` +
`natsWsUrl="${natsWsUrl}", error="${lastError}". Background retry in ${backgroundRetryDelayMs}ms.`,
);
rootCtx.transport.publish(rootEventsTopic, {
op: 'daemonError',
payload: {
root: daemonRoot,
realm: daemonRoot,
error: lastError,
retryInMs: backgroundRetryDelayMs,
},
});
this._scheduleDaemonDiscoveryRetry(backgroundRetryDelayMs);
finishDiscoveryProfile({
result: 'retrying',
retryInMs: backgroundRetryDelayMs,
});
} finally {
this._daemonDiscoveryInFlight = false;
}
}
/**
* Claim this browser tab's runtime and mounted child actors in system.registry.
*/
private async _registerWithRuntimeRegistry(controlPlaneRoot: string): Promise<void> {
if (!this._context || !this._runtime) return;
const registrationGeneration = this._lifecycleGeneration;
const finishRegistrationProfile = createBrowserProfileSpan(HOST_PROFILE_SCOPE, 'runtimeRegistry.claim', {
controlPlaneRoot,
});
if (!this.hasBackbone) {
finishRegistrationProfile({
result: 'skipped-no-backbone',
controlPlaneRoot,
});
return;
}
if (this._shouldSkipBrowserRuntimeRegistryClaim(controlPlaneRoot)) {
finishRegistrationProfile({
result: 'skipped-no-registry-authority',
controlPlaneRoot,
});
return;
}
const rootCtx = this._runtime.getRootContext?.();
if (!rootCtx) {
finishRegistrationProfile({ result: 'skipped-no-root-context' });
return;
}
const hostBootstrapMode = this._isHostBootstrapMode();
const contextMount = this._context.mount ?? '';
const runtimeRoot = transportRootForContext(rootCtx) ?? '';
const app = this.getAttribute('app-name') || document.title.split(/\s*[—–-]\s*/)[0]?.trim() || 'browser';
const registryTarget = this._serviceRegistryTargetForRoot(controlPlaneRoot);
const selectedAuthorityRoot = (this.dataset.selectedAuthorityRoot ?? '').trim();
const sessionAuthorityRoot = (this.dataset.sessionAuthorityRoot ?? '').trim();
const authorityRoot = selectedAuthorityRoot || sessionAuthorityRoot;
const runtimeInstanceRoot = (this.dataset.runtimeInstanceRoot ?? '').trim() || runtimeRoot;
const runtimeId = this.dataset.hostRuntimeId
|| this._runtimeIdFromMount(contextMount)
|| this._getOrCreateBrowserRuntimeId(authorityRoot || controlPlaneRoot || runtimeRoot, app);
this.dataset.hostRuntimeId = runtimeId;
const runtimeWireRoot = runtimeRoot || controlPlaneRoot;
const runtimeMount = `system.runtimes.${runtimeId}`;
const controlMount = runtimeMount;
const contextIsRuntimeMount = contextMount === runtimeMount || contextMount.startsWith(`${runtimeMount}.`);
const sessionMount = hostBootstrapMode
? contextIsRuntimeMount
? runtimeWireRoot || rootCtx.mount || 'unknown'
: [runtimeWireRoot || controlPlaneRoot, contextMount].filter(Boolean).join('.')
: runtimeRoot || rootCtx.mount || 'unknown';
await this._ensureBrowserRuntimeControl(runtimeId, runtimeMount, controlMount);
const inventory = this._browserRuntimeInventory(runtimeId, contextMount, hostBootstrapMode);
// Strip namespace root from sessionMount to get relative prefix for claims
const relativeMount = hostBootstrapMode && contextMount
? contextMount
: sessionMount.startsWith(controlPlaneRoot + '.')
? sessionMount.slice(controlPlaneRoot.length + 1)
: sessionMount;
const runtimeHref = typeof globalThis.location?.href === 'string'
? globalThis.location.href
: (typeof document !== 'undefined' && typeof document.baseURI === 'string' ? document.baseURI : '');
const state: IBrowserRuntimeRegistryState = {
registryTarget,
namespaceRoot: controlPlaneRoot,
runtimeId,
runtimeRoot: runtimeWireRoot,
app,
contextMount,
hostBootstrapMode,
sessionMount,
relativeMount,
runtimeMount,
controlMount,
authorityRoot,
sessionAuthorityRoot,
selectedAuthorityRoot,
runtimeInstanceRoot,
runtimeHref,
registeredAt: Date.now(),
};
try {
this._runtimeInventoryVersion = this._runtimeInventoryVersionForHost();
await this._replaceBrowserRuntimeClaims(state, inventory, this._runtimeInventoryVersion);
// eslint-disable-next-line no-console -- browser bootstrap
console.info(`[MatrixDSLHost] Claimed browser runtime "${runtimeId}" in system.registry`);
if (registrationGeneration !== this._lifecycleGeneration || !this._context || !this._runtime) {
finishRegistrationProfile({
result: 'skipped-after-cleanup',
runtimeId,
});
return;
}
this._browserRuntimeRegistryState = state;
this._runtimeRegistryId = runtimeId;
this._publishBrowserRuntimePresence(state, inventory, 'announce');
finishRegistrationProfile({
result: 'ok',
runtimeId,
});
} catch (err) {
// eslint-disable-next-line no-console -- browser bootstrap
console.warn(`[MatrixDSLHost] Browser runtime registry claim failed (non-fatal):`, err);
finishRegistrationProfile({
result: 'error',
error: err instanceof Error ? err.message : String(err),
});
return;
}
this._browserRuntimeRenewTimer = setInterval(() => {
const currentState = this._browserRuntimeRegistryState;
if (!currentState || !this._context) {
return;
}
RequestReply.execute(
this._context,
currentState.registryTarget,
'registry.claim.renew',
{
providerRuntimeId: currentState.runtimeId,
ttlMs: 45_000,
health: { status: 'ok' },
},
{ timeoutMs: 5000 },
).catch(() => { /* lease renewal failure is non-fatal */ });
this._publishBrowserRuntimePresence(
currentState,
this._browserRuntimeInventory(
currentState.runtimeId,
currentState.contextMount,
currentState.hostBootstrapMode,
),
'heartbeat',
);
}, 15_000);
this._browserRuntimeReleaseHandler = () => {
const context = this._context;
const currentState = this._browserRuntimeRegistryState;
if (!context || !currentState) return;
try {
RequestReply.sendToInbox(context, currentState.registryTarget, 'registry.release', {
providerRuntimeId: currentState.runtimeId,
});
} catch {
// Best-effort unload cleanup; the registry lease expires missed releases.
}
this._publishBrowserRuntimePresence(
currentState,
this._browserRuntimeInventory(
currentState.runtimeId,
currentState.contextMount,
currentState.hostBootstrapMode,
),
'departed',
);
};
window.addEventListener('beforeunload', this._browserRuntimeReleaseHandler);
}
/**
* Publish browser runtime presence to the runtime-directory authority
* (`system.runtimes`). Browser tabs claim child actors in `system.registry`
* via `registry.claim`, but `system.runtimes.runtimes.list` is fed by
* runtime presence — without this call, browser tabs are invisible to
* Director Topology, Host Ops Workloads, and any other consumer of the
* runtime directory.
*
* We route through the explicit `runtimes.presence.update` op via
* `RequestReply.execute("${controlPlaneRoot}/system.runtimes", ...)`, NOT through
* `publishRuntimePresence(ctx, record, event)`. The pub/sub path uses bare
* `$runtime.*` topics on the local transport — in federated HiveCast mode
* the tab realm is a synthetic per-tab root (`s-b-root-*`) so the publish
* lands on a NATS subject prefix the `RuntimeManagerActor` at the
* authority root never subscribes to. Routed RequestReply.execute uses
* the same federation-peer cross-routing that `registry.claim` already
* relies on.
*
* Best-effort: presence publication failures are non-fatal because the
* registry claim already carries the authoritative liveness of the tab.
*/
private _publishBrowserRuntimePresence(
state: IBrowserRuntimeRegistryState,
inventory: readonly IBrowserRuntimeInventoryEntry[],
event: RuntimePresenceEventV1,
): void {
const context = this._context;
if (!context) return;
try {
const presenceInventory = mapBrowserInventoryToPresence(inventory);
const now = Date.now();
const record: RuntimePresenceRecordV1 = {
version: '1',
namespaceRoot: state.namespaceRoot,
runtimeId: state.runtimeId,
runtimeType: 'browser',
runtimeRoot: state.runtimeRoot,
...(state.authorityRoot.length > 0 ? { authorityRoot: state.authorityRoot } : {}),
app: state.app,
claims: [{ prefix: state.runtimeMount, kind: 'local-only' }],
inventoryMode: 'embedded',
...(presenceInventory.length > 0 ? { inventory: presenceInventory } : {}),
controlMount: state.controlMount,
heartbeatTtlMs: 45_000,
registeredAt: state.registeredAt,
lastHeartbeat: now,
health: { status: 'ok', updatedAt: now },
metadata: {
source: 'browser-runtime',
surface: 'webapp',
ownerKind: 'client-session',
packageRef: state.app,
runtimeInstanceRoot: state.runtimeInstanceRoot,
// Render as "<App Name> (browser)" so it's distinguishable from
// the server-side runtime sharing the same packageRef.
displayName: `${state.app} (browser)`,
...(state.sessionAuthorityRoot.length > 0 ? { sessionAuthorityRoot: state.sessionAuthorityRoot } : {}),
...(state.selectedAuthorityRoot.length > 0 ? { selectedAuthorityRoot: state.selectedAuthorityRoot } : {}),
...(state.runtimeHref.length > 0 ? { href: state.runtimeHref.slice(0, 200) } : {}),
...(typeof navigator !== 'undefined' ? { userAgent: navigator.userAgent.slice(0, 120) } : {}),
},
};
const target = this._runtimeRegistryTargetForRoot(state.namespaceRoot);
RequestReply.execute(
context,
target,
'runtimes.presence.update',
{ record, event },
{ timeoutMs: 5000 },
).catch((err) => {
// eslint-disable-next-line no-console -- browser bootstrap
console.warn(`[MatrixDSLHost] Browser runtime presence (${event}) failed (non-fatal):`, err);
});
} catch (err) {
// eslint-disable-next-line no-console -- browser bootstrap
console.warn(`[MatrixDSLHost] Browser runtime presence publish (${event}) failed (non-fatal):`, err);
}
}
private async _replaceBrowserRuntimeClaims(
state: IBrowserRuntimeRegistryState,
inventory: readonly IBrowserRuntimeInventoryEntry[],
inventoryVersion: number,
): Promise<void> {
if (!this._context) {
return;
}
const nextComponentIds = new Set(inventory.map((entry) => browserInventoryComponentId(state, entry)));
for (const entry of inventory) {
await this._claimBrowserRuntimeEntry(state, entry, inventory.length, inventoryVersion);
}
for (const componentId of this._browserRuntimeClaimedComponentIds) {
if (nextComponentIds.has(componentId)) {
continue;
}
await RequestReply.execute(
this._context,
state.registryTarget,
'registry.release',
{
componentId,
providerRuntimeId: state.runtimeId,
},
{ timeoutMs: 5000 },
);
}
this._browserRuntimeClaimedComponentIds = nextComponentIds;
}
private async _claimBrowserRuntimeEntry(
state: IBrowserRuntimeRegistryState,
entry: IBrowserRuntimeInventoryEntry,
actorCount: number,
inventoryVersion: number,
): Promise<void> {
if (!this._context) {
return;
}
const kind = browserInventoryRegistryKind(entry);
await RequestReply.execute(
this._context,
state.registryTarget,
'registry.claim',
{
namespaceRoot: state.namespaceRoot,
mount: entry.mount,
kind,
componentId: browserInventoryComponentId(state, entry),
componentType: entry.type,
providerRuntimeId: state.runtimeId,
runtimeWireRoot: state.runtimeRoot,
localMount: entry.mount,
packageName: state.app,
runtimeId: state.runtimeId,
packageId: state.app,
mode: 'local-only',
ttlMs: 45_000,
health: { status: 'ok' },
metadata: {
source: 'browser-runtime-registry-claim',
runtimeType: 'browser',
ownerKind: 'client-session',
runtimeRoot: state.runtimeRoot,
runtimeInstanceRoot: state.runtimeInstanceRoot,
sessionMount: state.sessionMount,
relativeMount: state.relativeMount,
controlMount: state.controlMount,
actorCount,
inventoryVersion,
surface: entry.surface,
...(entry.description ? { description: entry.description } : {}),
...(state.authorityRoot.length > 0 ? {
addressRoot: state.authorityRoot,
authorityRoot: state.authorityRoot,
} : {}),
...(state.sessionAuthorityRoot.length > 0 ? { sessionAuthorityRoot: state.sessionAuthorityRoot } : {}),
...(state.selectedAuthorityRoot.length > 0 ? { selectedAuthorityRoot: state.selectedAuthorityRoot } : {}),
...(state.runtimeHref.length > 0 ? { href: state.runtimeHref.slice(0, 200) } : {}),
...(typeof navigator !== 'undefined' ? { userAgent: navigator.userAgent.slice(0, 120) } : {}),
},
},
{ timeoutMs: 5000 },
);
}
private _runtimeRegistryTargetForRoot(root: string): string {
const trimmed = root.trim();
return trimmed ? `${trimmed}/system.runtimes` : 'system.runtimes';
}
private _serviceRegistryTargetForRoot(root: string): string {
const trimmed = root.trim();
return trimmed ? `${trimmed}/system.registry` : 'system.registry';
}
private _shouldSkipBrowserRuntimeRegistryClaim(controlPlaneRoot: string): boolean {
if (this.dataset.authorityCoordinatorActive === 'false') {
return true;
}
const targetRoot = controlPlaneRoot.trim();
const platformRoot = (
this.dataset.platformServiceRoot
?? this.dataset.platformRoot
?? ''
).trim();
if (!targetRoot || !platformRoot || targetRoot !== platformRoot) {
return false;
}
if (this.dataset.isPlatformAdmin === 'true') {
return false;
}
if (this.dataset.authenticated !== 'true' && this._isAuthenticated !== true) {
return false;
}
const candidateAuthorityRoots = [
this.dataset.selectedAuthorityRoot,
this.dataset.sessionAuthorityRoot,
this.dataset.principalAuthorityRoot,
].map((root) => (root ?? '').trim());
return !candidateAuthorityRoots.some((root) => root.length > 0 && root !== platformRoot);
}
private _controlPlaneRootForSingletons(authorityRoot: string | null | undefined): string {
const selectedRoot = typeof this.dataset.selectedAuthorityRoot === 'string'
? this.dataset.selectedAuthorityRoot.trim()
: '';
if (selectedRoot) {
return selectedRoot;
}
const principalRoot = typeof this.dataset.principalAuthorityRoot === 'string'
? this.dataset.principalAuthorityRoot.trim()
: '';
if (principalRoot) {
return principalRoot;
}
return typeof authorityRoot === 'string' ? authorityRoot.trim() : '';
}
private _runtimeIdFromMount(mount: string): string {
const parts = mount.split('.').filter(Boolean);
return parts.find((part) => part.startsWith(BROWSER_RUNTIME_ID_PREFIX)) ?? '';
}
/**
* Phase B bus discovery resolves identity and service metadata after first render.
*
* This must not block child mounting. Apps already tolerate late identity
* attributes on the host and should render while the bus discovery completes.
*/
private async _runPhaseBBusDiscovery(usedBootstrap: boolean): Promise<void> {
if (!usedBootstrap || !this._context || !this._transport || !this._runtime) return;
const finishPhaseBProfile = createBrowserProfileSpan(HOST_PROFILE_SCOPE, 'phaseB.discovery', {
usedBootstrap,
});
const daemonRoot = this.getAttribute('daemon-root') ?? '';
const serviceRoot = this.dataset.platformServiceRoot ?? this.dataset.platformRoot ?? daemonRoot;
const explicitAuthorityRoot = this.dataset.explicitAuthorityRoot;
if (explicitAuthorityRoot) {
// Explicit authority URLs already declare the target identity in the
// path. Do not probe platform-local actors such as system.auth or
// system.directory here; that turns a shareable user URL into a hidden
// HiveCast-local lookup and can wait on unrelated platform timeouts.
console.info('[MatrixDSLHost] Phase B platform probe skipped for explicit authority URL - retaining explicit authority root');
finishPhaseBProfile({
result: 'skipped-explicit-authority',
daemonRoot,
serviceRoot,
authorityRoot: explicitAuthorityRoot,
});
return;
}
const authTarget = serviceRoot ? `${serviceRoot}/system.auth` : 'system.auth';
const dirTarget = serviceRoot ? `${serviceRoot}/system.directory` : 'system.directory';
const busTokenPayload = this.dataset.busToken ? { busToken: this.dataset.busToken } : {};
const rootCtx = this._runtime.getRootContext!();
const bootstrapAuthenticated = (this.dataset.authenticated === 'true') || this._isAuthenticated === true;
if (!bootstrapAuthenticated && !this.dataset.busToken) {
console.info('[MatrixDSLHost] Phase B platform probe skipped for anonymous bootstrap');
finishPhaseBProfile({
result: 'skipped-anonymous',
daemonRoot,
serviceRoot,
});
return;
}
const isHostedLeafTarget = this.dataset.runtimeTargetKind === 'leaf'
&& daemonRoot.length > 0
&& serviceRoot.length > 0
&& daemonRoot !== serviceRoot;
const selectedAuthorityRoot = (this.dataset.selectedAuthorityRoot ?? '').trim();
const userAuthorityRoot = selectedAuthorityRoot
|| (this.dataset.principalAuthorityRoot ?? '').trim();
const isPlatformAdmin = this.dataset.isPlatformAdmin === 'true';
const isNormalUserAuthoritySession = bootstrapAuthenticated
&& userAuthorityRoot.length > 0
&& serviceRoot.length > 0
&& userAuthorityRoot !== serviceRoot
&& !isPlatformAdmin;
if (isNormalUserAuthoritySession) {
console.info('[MatrixDSLHost] Phase B platform probe skipped for user authority session — retaining bootstrap identity');
finishPhaseBProfile({
result: 'skipped-user-authority',
daemonRoot,
serviceRoot,
authorityRoot: userAuthorityRoot,
});
return;
}
if (
bootstrapAuthenticated
&& (
this._isHostBootstrapMode()
|| this._isHostServiceBootstrap()
|| this._shouldRetainBootstrapIdentity(daemonRoot)
|| this._shouldUseRuntimeRegistryDiscovery(daemonRoot)
)
) {
console.info('[MatrixDSLHost] Phase B service probe skipped for Host bootstrap — retaining bootstrap identity');
finishPhaseBProfile({
result: 'skipped-host-bootstrap',
daemonRoot,
serviceRoot,
});
return;
}
if (bootstrapAuthenticated && isHostedLeafTarget) {
// Leaf-targeted hosted apps already have HTTP-session identity from
// bootstrap. The browser transport is scoped to the leaf account, so a
// platform Phase B RR would require a cross-account reply path and only
// produces a non-fatal timeout.
console.info('[MatrixDSLHost] Phase B platform probe skipped for hosted leaf runtime — retaining bootstrap identity');
finishPhaseBProfile({
result: 'skipped-leaf-runtime',
daemonRoot,
serviceRoot,
});
return;
}
// Phase B: auth + directory in PARALLEL (saves ~400ms vs sequential)
const authPromise = this._executePhaseBRequest<Record<string, unknown>, {
ok: boolean; principalId?: string; displayName?: string; root?: string; tier?: string; authenticated?: boolean;
}>(authTarget, 'auth.whoami', busTokenPayload).catch((err) => {
if (bootstrapAuthenticated) {
console.info('[MatrixDSLHost] Phase B auth probe deferred — retaining bootstrap identity:', err);
} else {
console.warn('[MatrixDSLHost] Phase B auth probe failed:', err);
}
return null;
});
const dirPromise = this._executePhaseBRequest<Record<string, unknown>, {
ok: boolean; root?: string; services?: Array<{ name: string; mount: string }>;
parentRoots?: unknown[];
}>(dirTarget, 'directory.whoami', busTokenPayload).catch((err) => {
console.info('[MatrixDSLHost] Phase B directory probe deferred (non-fatal):', err);
return null;
});
const [authResult, dirResult] = await Promise.all([authPromise, dirPromise]);
if (authResult?.ok) {
console.info(`[MatrixDSLHost] Phase B: ${authTarget} → principalId="${authResult.principalId}", authenticated=${authResult.authenticated}, root="${authResult.root}"`);
if (authResult.principalId) {
this.dataset.principalId = authResult.principalId;
rootCtx.setService('principal-id', authResult.principalId);
}
const orgRoot = this.dataset.platformRoot || serviceRoot || authResult.root || '';
if (orgRoot) {
this.dataset.orgId = orgRoot;
rootCtx.setService('org-id', orgRoot);
}
if (authResult.root) {
this.dataset.workspaceId = authResult.root;
rootCtx.setService('workspace-id', authResult.root);
}
if (authResult.displayName) this.dataset.displayName = authResult.displayName;
this.dataset.authenticated = String(authResult.authenticated ?? false);
}
if (dirResult?.ok) {
console.info(`[MatrixDSLHost] Phase B: ${dirTarget} → root="${dirResult.root}", services=${dirResult.services?.length ?? 0}`);
if (dirResult.parentRoots) {
this.setAttribute('parent-roots', JSON.stringify(dirResult.parentRoots));
this.setAttribute('parent-realms', JSON.stringify(dirResult.parentRoots));
}
}
if (this.dataset.busToken) {
this._startBusTokenRefresh(authTarget);
}
finishPhaseBProfile({
authOk: authResult?.ok === true,
dirOk: dirResult?.ok === true,
daemonRoot,
serviceRoot,
});
}
private async _executePhaseBRequest<TPayload extends Record<string, unknown>, TResult>(
target: string,
op: string,
payload: TPayload,
): Promise<TResult> {
const retryDelaysMs = [0, 200, 500] as const;
let lastError: unknown;
for (const delayMs of retryDelaysMs) {
if (delayMs > 0) {
await new Promise<void>((resolve) => setTimeout(resolve, delayMs));
}
try {
return await RequestReply.execute<TPayload, TResult>(
this._context!,
target,
op,
payload,
{ timeoutMs: 5000 },
);
} catch (error) {
lastError = error;
}
}
throw lastError instanceof Error ? lastError : new Error(`Phase B request failed: ${String(lastError)}`);
}
// ═══════════════════════════════════════════════════════════════════════════
// Cleanup
// ═══════════════════════════════════════════════════════════════════════════
/**
* Start a 4-minute interval to refresh the bus token before its 5-min expiry.
* Calls system.auth auth.refresh on the bus with the current token.
*/
private _startBusTokenRefresh(authTarget: string): void {
this._stopBusTokenRefresh();
if (this._shouldSkipBusTokenRefreshForTarget(authTarget)) {
console.info('[MatrixDSLHost] Bus token refresh skipped for platform auth target in user authority session');
return;
}
const REFRESH_INTERVAL_MS = 4 * 60 * 1000; // 4 minutes
this._busTokenRefreshTimer = setInterval(async () => {
const currentToken = this.dataset.busToken;
if (!currentToken || !this._context) {
this._stopBusTokenRefresh();
return;
}
try {
const result = await RequestReply.execute<Record<string, unknown>, {
ok: boolean; busToken?: string; expiresAt?: number; principalId?: string;
}>(this._context, authTarget, 'auth.refresh', { busToken: currentToken }, { timeoutMs: 5000 });
if (result?.ok && result.busToken) {
this.dataset.busToken = result.busToken;
this._context.setService('bus-token', result.busToken);
this._installScopedActorServices(this._context, this.dataset.runtimeRoot ?? this.dataset.sessionRoot ?? null);
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.debug(`[MatrixDSLHost] Bus token refreshed for ${result.principalId}`);
} else {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.warn('[MatrixDSLHost] Bus token refresh failed — clearing token');
delete this.dataset.busToken;
this.dataset.authenticated = 'false';
this._stopBusTokenRefresh();
}
} catch (err) {
// eslint-disable-next-line no-console -- browser bootstrap, not an actor
console.warn('[MatrixDSLHost] Bus token refresh error:', err);
}
}, REFRESH_INTERVAL_MS);
}
private _shouldSkipBusTokenRefreshForTarget(authTarget: string): boolean {
const targetRoot = authTarget.includes('/')
? authTarget.slice(0, authTarget.indexOf('/')).trim()
: '';
if (!targetRoot) {
return false;
}
const userAuthorityRoot = (this.dataset.selectedAuthorityRoot ?? '').trim()
|| (this.dataset.principalAuthorityRoot ?? '').trim();
return (this.dataset.authenticated === 'true' || this._isAuthenticated === true)
&& this.dataset.isPlatformAdmin !== 'true'
&& userAuthorityRoot.length > 0
&& targetRoot !== userAuthorityRoot;
}
private _installScopedActorServices(context: IMatrixContext, runtimeInstanceRoot: string | null): void {
const platformRoot = (
this.dataset.platformServiceRoot
?? this.dataset.platformRoot
?? ''
).trim();
const userSpaceRoot = (
this.dataset.selectedAuthorityRoot
?? this.dataset.sessionAuthorityRoot
?? this.dataset.principalAuthorityRoot
?? ''
).trim();
const accountRoot = (
this.dataset.sessionAuthorityRoot
?? this.dataset.principalAuthorityRoot
?? this.dataset.selectedAuthorityRoot
?? ''
).trim();
const addressContext: RuntimeAddressContext = {
runtimeInstanceRoot: runtimeInstanceRoot && runtimeInstanceRoot.trim().length > 0
? runtimeInstanceRoot.trim()
: null,
accountRoot: accountRoot.length > 0 ? accountRoot : null,
userSpaceRoot: userSpaceRoot.length > 0 ? userSpaceRoot : null,
platformRoot,
isPlatformAdmin: this.dataset.isPlatformAdmin === 'true',
principalId: this.dataset.principalId ?? null,
busToken: this.dataset.busToken ?? null,
};
const callClient = new ActorCallClient(context, addressContext);
const resolver = new ScopedIntentResolver(callClient, WELL_KNOWN_SERVICE_BINDINGS);
const services = new ActorServices(resolver, addressContext);
context.setService(RUNTIME_ADDRESS_CONTEXT, addressContext);
context.setService(ACTOR_CALL_CLIENT, callClient);
context.setService(SCOPED_INTENT_RESOLVER, resolver);
context.setService(ACTOR_SERVICES, services);
}
private _stopBusTokenRefresh(): void {
if (this._busTokenRefreshTimer) {
clearInterval(this._busTokenRefreshTimer);
this._busTokenRefreshTimer = null;
}
}
private _startHostedRuntimeTargetRefresh(): void {
this._stopHostedRuntimeTargetRefresh();
this._hostedRuntimeTargetRefreshTimer = setInterval(() => {
void this._refreshHostedRuntimeTargetMetadata();
}, HOSTED_RUNTIME_TARGET_REFRESH_INTERVAL_MS);
}
private _stopHostedRuntimeTargetRefresh(): void {
if (this._hostedRuntimeTargetRefreshTimer) {
clearInterval(this._hostedRuntimeTargetRefreshTimer);
this._hostedRuntimeTargetRefreshTimer = null;
}
this._hostedRuntimeTargetRefreshInFlight = false;
}
private _scheduleDaemonDiscoveryRetry(delayMs: number): void {
if (this._daemonDiscoveryRetryTimer || !this._context || !this._runtime) {
return;
}
const retryTimer = setTimeout(() => {
this._daemonDiscoveryRetryTimer = null;
void this._discoverDaemon();
}, delayMs);
if (typeof retryTimer === 'object' && retryTimer !== null && 'unref' in retryTimer) {
const unref = retryTimer.unref;
if (typeof unref === 'function') {
unref.call(retryTimer);
}
}
this._daemonDiscoveryRetryTimer = retryTimer;
}
private _stopDaemonDiscoveryRetry(): void {
if (this._daemonDiscoveryRetryTimer) {
clearTimeout(this._daemonDiscoveryRetryTimer);
this._daemonDiscoveryRetryTimer = null;
}
this._daemonDiscoveryRetryDelayMs = 1_000;
}
private _cleanup(): void {
this._lifecycleGeneration += 1;
const transportToDisconnect = this._transport;
this._stopBusTokenRefresh();
this._stopHostedRuntimeTargetRefresh();
this._stopDaemonDiscoveryRetry();
delete this.dataset.sessionRoot;
delete this.dataset.runtimeRoot;
delete this.dataset.daemonStatus;
if (this._observer) {
this._observer.disconnect();
this._observer = null;
}
if (transportToDisconnect) {
void transportToDisconnect.disconnect().catch((error) => {
// eslint-disable-next-line no-console -- browser host teardown has no actor context
console.warn('[MatrixDSLHost] Transport disconnect during cleanup failed:', error);
});
}
if (this._joinUnsub) {
this._joinUnsub();
this._joinUnsub = null;
}
if (this._browserRuntimeRenewTimer) {
clearInterval(this._browserRuntimeRenewTimer);
this._browserRuntimeRenewTimer = null;
}
if (this._runtimeInventoryClaimRefreshTimer) {
clearTimeout(this._runtimeInventoryClaimRefreshTimer);
this._runtimeInventoryClaimRefreshTimer = null;
}
if (this._browserRuntimeRegistryState && this._runtime) {
this._publishBrowserRuntimePresence(
this._browserRuntimeRegistryState,
this._browserRuntimeInventory(
this._browserRuntimeRegistryState.runtimeId,
this._browserRuntimeRegistryState.contextMount,
this._browserRuntimeRegistryState.hostBootstrapMode,
),
'departed',
);
}
this._browserRuntimeRegistryState = null;
this._browserRuntimeClaimedComponentIds.clear();
this._runtimeRegistryId = '';
this._runtimeControlStartedAt = '';
if (this._browserRuntimeReleaseHandler) {
if (typeof window !== 'undefined') {
window.removeEventListener('beforeunload', this._browserRuntimeReleaseHandler);
}
this._browserRuntimeReleaseHandler = null;
}
if (this._peer) {
this._peer.stop();
this._peer = null;
}
this._context = null;
this._runtime = null;
this._transport = null;
this._rawAdapter = null;
this._routingPeer = null;
this._wrapTransport = null;
this._initialized = false;
}
}
const ROUTE_KEY_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
const RESERVED_ROUTE_KEYS = new Set([
'api',
'auth',
'login',
'logout',
'signup',
'setup',
'settings',
'apps',
'app',
'assets',
'static',
'branding',
'nats-ws',
'matrix',
'system',
'admin',
'root',
'support',
'docs',
'help',
'billing',
'status',
'healthz',
'well-known',
'favicon.ico',
'manifest.json',
'hivecast',
'open-matrix',
'me',
]);
function isValidRouteKey(routeKey: string): boolean {
return ROUTE_KEY_PATTERN.test(routeKey)
&& !RESERVED_ROUTE_KEYS.has(routeKey);
}
function routeKeyFromPublicNamespace(publicNamespace: string): string | null {
const normalized = publicNamespace.trim().toLowerCase();
if (!normalized.startsWith('space.')) {
return null;
}
const routeKey = normalized.slice('space.'.length);
return isValidRouteKey(routeKey) ? routeKey : null;
}
function matrixChildElementHooks(element: HTMLElement): IMatrixChildElementHooks {
return element as IMatrixChildElementHooks;
}
function matrixActorElementVersionSource(element: HTMLElement): IMatrixActorElementVersionSource {
return element as IMatrixActorElementVersionSource;
}
function matrixRootContextReceiver(element: Element): IMatrixRootContextReceiver {
return element as IMatrixRootContextReceiver;
}
function transportRootForContext(context: IMatrixContext): string | undefined {
return context.transport.root || context.transport.realm;
}
function browserInventoryRegistryKind(entry: IBrowserRuntimeInventoryEntry): ServiceRegistryEntryKindV1 {
return entry.surface === 'runtime' ? 'runtime' : 'actor';
}
function browserInventoryComponentId(
state: IBrowserRuntimeRegistryState,
entry: IBrowserRuntimeInventoryEntry,
): string {
return browserInventoryRegistryKind(entry) === 'runtime'
? `runtime:${state.runtimeId}`
: `runtime:${state.runtimeId}:actor:${entry.mount}`;
}
function mapBrowserInventoryToPresence(
inventory: readonly IBrowserRuntimeInventoryEntry[],
): RuntimeInventoryEntryV1[] {
return inventory.map((entry) => ({
mount: entry.mount,
type: entry.type,
surface: entry.surface === 'dom' ? 'dom' : 'webapp',
runtimeId: entry.runtimeId,
claimKind: 'local-only',
...(entry.description ? { description: entry.description } : {}),
}));
}
function browserRouterTransportAdapter(
adapter: BrowserRouterResult['adapter'],
peer: FederationPeer,
): ITransportAdapter {
return {
get root() {
return adapter.root ?? peer.localRoot;
},
get realm() {
return adapter.realm ?? peer.localRoot;
},
publish: adapter.publish,
subscribe: adapter.subscribe,
getWireTopicInfo: adapter.getWireTopicInfo,
getUnderlyingClient: () => adapter.getUnderlyingClient?.() ?? peer,
disconnect: async () => {
peer.stop();
},
};
}
function serviceRegistryEntry(value: unknown): ServiceRegistryEntryV1 | null {
const record = registryRecord(value);
const kind = serviceRegistryEntryKind(record.kind);
const placement = registryPlacement(record.placement);
const claim = registryClaim(record.claim);
const health = registryHealth(record.health);
if (
record.version !== '1'
|| typeof record.namespaceRoot !== 'string'
|| typeof record.mount !== 'string'
|| kind === null
|| placement === null
|| claim === null
|| health === null
|| typeof record.ttlMs !== 'number'
) {
return null;
}
return {
version: '1',
namespaceRoot: record.namespaceRoot,
mount: record.mount,
kind,
...(registryService(record.service) ? { service: registryService(record.service) } : {}),
...(registryApp(record.app) ? { app: registryApp(record.app) } : {}),
placement,
claim,
health,
ttlMs: record.ttlMs,
...(isRegistryRecord(record.metadata) ? { metadata: record.metadata } : {}),
};
}
function serviceRegistryEntryKind(value: unknown): ServiceRegistryEntryKindV1 | null {
return value === 'actor'
|| value === 'service'
|| value === 'app'
|| value === 'feed'
|| value === 'runtime'
|| value === 'gateway'
|| value === 'connector'
|| value === 'resource'
? value
: null;
}
function registryService(value: unknown): ServiceRegistryEntryV1['service'] | undefined {
const record = registryRecord(value);
if (Object.keys(record).length === 0) {
return undefined;
}
return {
accepts: registryStringArray(record.accepts),
emits: registryStringArray(record.emits),
streams: registryStringArray(record.streams),
capabilities: [],
intents: [],
};
}
function registryApp(value: unknown): ServiceRegistryEntryV1['app'] | undefined {
const record = registryRecord(value);
const route = registryString(record.route);
const title = registryString(record.title);
if (!route || !title) {
return undefined;
}
return {
route,
title,
...(registryString(record.icon) ? { icon: registryString(record.icon) } : {}),
surface: registryAppSurface(record.surface),
openable: record.openable !== false,
...(registryString(record.appName) ? { appName: registryString(record.appName) } : {}),
};
}
function registryPlacement(value: unknown): ServiceRegistryEntryV1['placement'] | null {
if (!isRegistryRecord(value)) {
return null;
}
return {
...(registryString(value.deviceId) ? { deviceId: registryString(value.deviceId) } : {}),
...(registryString(value.hostId) ? { hostId: registryString(value.hostId) } : {}),
...(registryString(value.runtimeId) ? { runtimeId: registryString(value.runtimeId) } : {}),
...(registryString(value.packageId) ? { packageId: registryString(value.packageId) } : {}),
...(registryString(value.actorId) ? { actorId: registryString(value.actorId) } : {}),
...(registryString(value.deploymentInstanceId) ? { deploymentInstanceId: registryString(value.deploymentInstanceId) } : {}),
...(registryString(value.instanceName) ? { instanceName: registryString(value.instanceName) } : {}),
...(registryString(value.packageRef) ? { packageRef: registryString(value.packageRef) } : {}),
};
}
function registryClaim(value: unknown): ServiceRegistryEntryV1['claim'] | null {
if (!isRegistryRecord(value)) {
return null;
}
const mode = registryClaimMode(value.mode);
const result = registryClaimResult(value.result);
const ownerComponentId = registryString(value.ownerComponentId);
const loadBalancing = registryLoadBalancing(value.loadBalancing);
if (
mode === null
|| result === null
|| !ownerComponentId
|| loadBalancing === null
|| typeof value.claimedAt !== 'number'
|| typeof value.renewedAt !== 'number'
|| typeof value.expiresAt !== 'number'
) {
return null;
}
return {
mode,
result,
ownerComponentId,
...(registryString(value.electionGroup) ? { electionGroup: registryString(value.electionGroup) } : {}),
...(registryString(value.leaderId) ? { leaderId: registryString(value.leaderId) } : {}),
loadBalancing,
claimedAt: value.claimedAt,
renewedAt: value.renewedAt,
expiresAt: value.expiresAt,
};
}
function registryHealth(value: unknown): ServiceRegistryEntryV1['health'] | null {
if (!isRegistryRecord(value)) {
return null;
}
const status = registryHealthStatus(value.status);
if (status === null || typeof value.updatedAt !== 'number') {
return null;
}
return {
status,
...(registryString(value.reason) ? { reason: registryString(value.reason) } : {}),
updatedAt: value.updatedAt,
};
}
function registryRecord(value: unknown): Record<string, unknown> {
return value && typeof value === 'object' && !Array.isArray(value)
? value as Record<string, unknown>
: {};
}
function bootstrapNodeRoleActive(value: unknown, roleName: string): boolean | undefined {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return undefined;
}
const role = (value as Record<string, unknown>)[roleName];
if (!role || typeof role !== 'object' || Array.isArray(role)) {
return undefined;
}
const active = (role as Record<string, unknown>).active;
return typeof active === 'boolean' ? active : undefined;
}
function isRegistryRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function registryArtifact(value: unknown): Record<string, unknown> {
return registryRecord(value);
}
function registryString(value: unknown): string | undefined {
return typeof value === 'string' && value.trim().length > 0
? value.trim()
: undefined;
}
function registryStringArray(value: unknown): string[] {
return Array.isArray(value)
? value.filter((item): item is string => typeof item === 'string')
.map((item) => item.trim())
.filter(Boolean)
: [];
}
function registryAppSurface(value: unknown): NonNullable<ServiceRegistryEntryV1['app']>['surface'] {
return value === 'panel' || value === 'modal' || value === 'widget'
? value
: 'page';
}
function registryClaimMode(value: unknown): ServiceRegistryEntryV1['claim']['mode'] | null {
return value === 'authoritative'
|| value === 'proxy'
|| value === 'mirror'
|| value === 'local-only'
|| value === 'pool-member'
? value
: null;
}
function registryClaimResult(value: unknown): ServiceRegistryEntryV1['claim']['result'] | null {
return value === 'accepted'
|| value === 'rejected'
|| value === 'collision'
|| value === 'joined-pool'
|| value === 'standby'
|| value === 'leader'
|| value === 'mirror'
|| value === 'local-only'
? value
: null;
}
function registryLoadBalancing(value: unknown): ServiceRegistryEntryV1['claim']['loadBalancing'] | null {
return value === 'none'
|| value === 'round-robin'
|| value === 'least-loaded'
|| value === 'broadcast'
? value
: null;
}
function registryHealthStatus(value: unknown): ServiceRegistryEntryV1['health']['status'] | null {
return value === 'ok'
|| value === 'degraded'
|| value === 'error'
|| value === 'unknown'
|| value === 'starting'
|| value === 'stopped'
? value
: null;
}
function registrySourceKind(value: unknown): ComponentSourceKind | undefined {
const sourceKind = registryString(value);
return sourceKind === 'installed-webapp' || sourceKind === 'external-webapp'
? sourceKind
: undefined;
}
function registryRegistrationSource(value: unknown): ComponentRegistrationSource | undefined {
const registrationSource = registryString(value);
return registrationSource === 'package-manifest'
|| registrationSource === 'daemon-config'
|| registrationSource === 'deployment-spec'
? registrationSource
: undefined;
}
function registryArtifactFreshness(value: unknown): ComponentArtifactFreshness | undefined {
const freshness = registryString(value);
return freshness === 'current'
|| freshness === 'stale'
|| freshness === 'incomplete'
|| freshness === 'missing-manifest'
|| freshness === 'invalid-manifest'
|| freshness === 'unverifiable'
|| freshness === 'missing-output'
? freshness
: undefined;
}
// ═══════════════════════════════════════════════════════════════════════════
// Custom Element Registration
// ═══════════════════════════════════════════════════════════════════════════
if (typeof customElements !== 'undefined' && !customElements.get('matrix-dsl-host')) {
customElements.define('matrix-dsl-host', MatrixDSLHost);
}