Files
LanMountainDesktop/docs/EXTERNAL_IPC_ARCHITECTURE.md
lincube 927dc8d1fd Add launcher coordinator IPC and startup reservation
Introduce a launcher coordinator to reserve startup ownership and prevent duplicate host launches. Adds a NamedPipe-based IPC server/client (LauncherCoordinatorIpcServer/Client), coordinator messages/models, and PublicShellStatus/activation types for richer shell reporting. Enhances StartupAttemptRecord and StartupAttemptRegistry to track coordinator pid/pipe, heartbeat, reserved-before-host-start, and public IPC status, plus new reservation/heartbeat APIs and takeover logic. Wire coordinator into App and LauncherFlowCoordinator to attach secondary launchers, publish coordinator status, probe existing hosts, and include more detailed launch result details. Also adds unit tests and docs describing coordinator and startup visuals behavior.
2026-04-23 09:45:05 +08:00

5.7 KiB

External IPC Architecture

Scope

This document defines the current external integration IPC baseline for LanMountainDesktop.

  • The delivery focus is external application integration, not plugin process isolation.
  • dotnetCampus.Ipc is the single IPC foundation for Host public APIs, Launcher/OOBE startup notifications, and plugin-contributed external services.
  • Process isolation remains a future track and stays documented in docs/PLUGIN_PROCESS_ISOLATION_ARCHITECTURE.md.

Design Summary

The public IPC stack is split into two complementary layers:

  1. Strongly typed public services
    • Contracts are marked with [IpcPublic].
    • Host exposes service instances through CreateIpcJoint<TContract>(instance).
    • .NET clients connect once and obtain strong typed proxies through CreateIpcProxy<TContract>(peer).
  2. Routed notifications
    • JsonIpcDirectRoutedProvider.NotifyAsync is used for one-way event delivery.
    • Startup progress, loading-state updates, catalog changed events, and plugin live events all use routed notify IDs.

This keeps command/query calls explicit and strongly typed while still giving plugins and Launcher a lightweight event channel.

Projects

  • LanMountainDesktop.Shared.IPC
    • Public IPC constants, routed notify IDs, DTOs, strong-typed public service contracts, host/client helpers, and DI registration helpers.
  • LanMountainDesktop
    • Runs PublicIpcHostService, exposes built-in public services, and folds plugin-contributed services into one external catalog.
  • LanMountainDesktop.Launcher
    • Connects to the Host public pipe and listens for startup and loading-state notifications instead of running a custom length-prefixed IPC server.
  • LanMountainDesktop.PluginSdk
    • Adds IPluginPublicIpcContributor, IPluginPublicIpcBuilder, and AddPluginPublicIpc(...).

Built-in Public Services

Current built-in [IpcPublic] contracts:

  • IPublicAppInfoService
    • Returns application metadata such as version, codename, process id, pipe name, and startup time.
  • IPublicShellControlService
    • Allows external .NET clients to query shell status, activate the shell, repair tray readiness, repair taskbar entry visibility, open settings, request restart, and request exit.
  • IPublicPluginCatalogService
    • Returns the merged public IPC catalog snapshot exposed by Host.

Routed Notify IDs

Current fixed routed notify IDs:

  • lanmountain.catalog.changed
  • lanmountain.launcher.startup-progress
  • lanmountain.launcher.loading-state

The fixed routed surface is intentionally small. Runtime variation happens in the service catalog and in plugin-contributed service instances, not in ad-hoc top-level route registration after startup.

Host Lifecycle

PublicIpcHostService is started during Host application startup and remains the single external IPC entry point.

Responsibilities:

  • Start a named dotnetCampus.Ipc provider.
  • Register fixed request routes before StartServer().
  • Expose built-in strong-typed public services.
  • Maintain the merged service catalog.
  • Publish startup and loading-state notifications to connected clients.
  • Accept plugin-contributed public services after plugin load.

Launcher / OOBE Migration

Launcher no longer depends on the previous custom named-pipe length-prefixed protocol as the primary path.

  • Host publishes StartupProgressMessage through lanmountain.launcher.startup-progress.
  • Host publishes LoadingStateMessage through lanmountain.launcher.loading-state.
  • Launcher connects as a normal public IPC client and subscribes to those routed notifications.

This means Splash/OOBE is now just another IPC consumer on the same base transport used by external integrators.

Launcher-to-launcher de-duplication is intentionally separate from Host Public IPC. The active Launcher coordinator uses a per-user local pipe and startup-attempt.json heartbeat so secondary Launchers attach to the coordinator before any host process can be started twice.

Plugin Public IPC Contribution Model

Plugins can contribute new external IPC services in two ways:

  1. Declarative registration
    • services.AddPluginPublicIpc<TContract, TImplementation>(...)
  2. Advanced contributor
    • Register IPluginPublicIpcContributor
    • Use IPluginPublicIpcBuilder to contribute services from plugin DI

At plugin load time the Host runtime:

  • discovers PluginPublicIpcServiceRegistration
  • executes IPluginPublicIpcContributor
  • validates that contributed contracts are [IpcPublic] interfaces
  • registers the resolved instances into PublicIpcHostService
  • emits lanmountain.catalog.changed

Plugins can also inject IExternalIpcNotificationPublisher and translate internal DI/message-bus events into routed notifications such as:

  • lanmountain.plugin.{pluginId}.attendance.updated
  • lanmountain.plugin.{pluginId}.status.changed

Service Catalog

The public catalog is represented by PublicIpcCatalogSnapshot and includes:

  • built-in and plugin-provided public services
  • contract type metadata
  • optional object id
  • owning pluginId for plugin services
  • declared notify IDs
  • current loaded/enabled plugin list

This catalog is available through:

  • strong-typed public service IPublicPluginCatalogService
  • fixed request route lanmountain.catalog.get
  • routed notify lanmountain.catalog.changed

Current Limitations

  • Strong-typed proxy/joint support is .NET-first.
  • Plugin service removal is still restart-bound. New services can be added at runtime, but service removal is not yet modeled as a live unload contract.
  • Cross-language clients still need a .NET bridge or sidecar if they want to consume [IpcPublic] contracts directly.
  • Plugin process isolation is not part of this delivery. That remains future work.