# Target Architecture ## Objective The target architecture keeps the application small and explicit. Electron should remain a thin shell that owns desktop lifecycle, launches NodeCG, loads local dashboards, manages updates, and shuts down cleanly. The target is not a full rewrite. It is a gradual extraction of responsibilities from `main.ts` into testable modules. ## Target Structure ```text src/main/ app/ bootstrap.ts application-controller.ts paths.ts shutdown-service.ts config/ runtime-config.ts windows/ window-service.ts navigation-policy.ts nodecg/ runtime-provisioner.ts nodecg-process-service.ts platform-process-killer.ts updates/ update-service.ts update-config.ts update-download.ts update-schema.ts logging/ logger.ts shared/ result.ts src/shared/ types/ config.ts ``` `shared/result.ts` is optional and should be added only if it removes repeated error-handling noise. `src/shared/types/config.ts` should contain only types that are genuinely shared across process boundaries or packages. It should not become a dumping ground. ## Runtime Flow ```text Electron entrypoint -> bootstrap app identity, paths, lock, config -> create ApplicationController -> prepare managed NodeCG runtime -> relaunch if first install requires it -> create loading window -> start NodeCG process -> wait for HTTP readiness -> create or show main window -> load NodeCG dashboard URL -> close loading window -> schedule update checks ``` Shutdown flow: ```text quit requested -> mark controller stopping -> stop update work if needed -> stop NodeCG process tree -> close windows -> allow app exit ``` Activation flow: ```text activate requested -> if ready, create or show main window -> if not ready, route through readiness-aware startup -> never load dashboard before NodeCG readiness ``` ## Module Responsibilities ### `app/bootstrap.ts` Owns Electron entrypoint side effects: - Set app name. - Set app paths. - Acquire single-instance lock. - Register Electron app event handlers. - Instantiate services. - Delegate startup and shutdown to `ApplicationController`. Rules: - Keep this file thin. - Do not put business logic here. - Do not make it responsible for update parsing, process killing, or window option construction. ### `app/application-controller.ts` Owns high-level lifecycle state. States: ```text idle preparing starting ready stopping stopped failed ``` Responsibilities: - Coordinate runtime preparation. - Coordinate NodeCG startup and readiness. - Coordinate loading and main windows. - Coordinate update scheduling. - Coordinate activation. - Coordinate shutdown. Rules: - State transitions must be explicit. - Shutdown must be idempotent. - Activation must not bypass readiness. - Controller tests should not require real Electron where avoidable. ### `app/paths.ts` Owns pure path construction: - Electron `userData` derived paths. - Managed runtime path. - Safe temp locations. - Any path constants shared by startup and provisioning. Rules: - No Electron side effects. - No filesystem writes. - Pure functions only. ### `config/runtime-config.ts` Owns runtime configuration: - Parse environment variables. - Parse static config. - Define defaults. - Return typed runtime config. Rules: - Parse once. - Validate early. - Do not read environment variables throughout the application. ### `windows/window-service.ts` Owns Electron window creation and window lifecycle. Responsibilities: - Create loading window. - Create main window. - Apply secure `webPreferences`. - Apply devtools policy. - Register permission handlers. - Delegate navigation decisions to `navigation-policy`. Rules: - No NodeCG process logic. - No updater logic. - No runtime provisioning logic. ### `windows/navigation-policy.ts` Owns pure navigation decisions. Responsibilities: - Allow approved local NodeCG origins. - Block external navigation. - Block unexpected new-window attempts. - Normalize URL parsing. Rules: - Prefer `URL` parsing. - Keep policy testable without Electron. - Do not use broad string-prefix allow checks as the primary control. ### `nodecg/runtime-provisioner.ts` Owns managed runtime installation and replacement. Responsibilities: - Validate bundled runtime source. - Install runtime into `userData`. - Replace managed runtime safely. - Preserve `cfg`, `db`, and `logs`. - Report whether relaunch is needed. Rules: - User-owned data must not be deleted. - Runtime replacement must be predictable and test-covered. ### `nodecg/nodecg-process-service.ts` Owns NodeCG process lifecycle. Responsibilities: - Validate runtime before launch. - Start NodeCG with `ELECTRON_RUN_AS_NODE`. - Capture process output. - Wait for HTTP readiness. - Stop the process. - Delegate platform-specific process-tree termination. Rules: - Do not build platform kill commands here. - Do not create windows here. - Do not schedule updates here. ### `nodecg/platform-process-killer.ts` Owns OS-specific process-tree termination. Responsibilities: - Terminate a process tree on Windows. - Terminate a process tree on POSIX systems. - Validate process IDs before command construction. - Normalize process-kill errors. Rules: - Keep platform branches here. - Test command construction. - Keep inputs narrow and typed. ### `updates/update-service.ts` Owns update orchestration. Responsibilities: - Schedule update checks. - Fetch update metadata through a helper. - Validate metadata through schema helpers. - Select the correct platform asset. - Ask the user before installing. - Delegate download work. - Start installer only after validation and download success. Rules: - Do not trust remote metadata. - Do not mix dialogs with JSON parsing. - Do not mix installer execution with download streaming. ### `updates/update-config.ts` Owns update settings: - Feed URL. - Current app version. - Platform selection. - Development-mode behavior. Rules: - Keep production and development behavior explicit. - Do not silently downgrade security in production. ### `updates/update-download.ts` Owns download behavior: - Validate URL protocol. - Download to a safe temp path. - Write atomically. - Return a typed result. Rules: - No dialogs. - No installer execution. - No remote metadata interpretation. ### `updates/update-schema.ts` Owns runtime validation: - Update metadata shape. - Asset shape. - Version field presence. - URL field validity before download selection. Rules: - Unknown remote JSON must be validated before use. - Invalid metadata must fail closed. ### `logging/logger.ts` Optional thin logging boundary. Rules: - Add only if it improves consistency. - Do not introduce a large logging framework. - Keep logs useful for startup, process, update, and shutdown diagnostics. ## Electron Decisions - Keep Electron main as the only privileged process. - Keep Node.js unavailable to web content. - Keep custom renderer absent unless a concrete feature requires one. - Keep preload absent unless a desktop API must cross into web content. - Treat windows as untrusted web surfaces even when loading local NodeCG dashboards. Required `BrowserWindow` security posture: ```text nodeIntegration: false contextIsolation: true sandbox: true webSecurity: true ``` Additional decisions: - Deny permissions by default. - Control devtools by environment. - Block external navigation by default. - Block unexpected new windows. - Review CSP options for NodeCG-hosted content, but do not break dashboards to satisfy theoretical policy. ## IPC Decisions Current target: - No IPC. - No preload. - No exposed desktop API. Future IPC, if needed: ```text src/main/ipc/ channels.ts register-handlers.ts validators.ts src/shared/ipc/ types.ts ``` IPC must be: - Explicitly justified by a product requirement. - Channel allowlisted. - Payload validated at runtime. - Typed at compile time. - Narrow in capability. IPC must not expose: - Raw `ipcRenderer`. - Filesystem primitives. - Process primitives. - Shell execution. - Update installation primitives. - Arbitrary NodeCG process controls. ## Security Decisions Security controls to preserve: - No Node.js in web content. - Context isolation enabled. - Sandbox enabled. - No IPC surface by default. - Local navigation only. Security controls to add: - Explicit `webSecurity: true`. - Permission handler that denies by default. - Explicit devtools policy. - Strong update metadata validation. - Strong update asset URL validation. - Safe temporary download paths. - Atomic download finalization. - Platform process-kill isolation. ## What This Architecture Should Feel Like Future maintainers should be able to answer these questions quickly: - Where does startup happen? - Where is NodeCG launched? - Where is readiness checked? - Where are windows created? - Where is navigation allowed or denied? - Where are updates validated? - Where is shutdown coordinated? - Where does platform-specific process killing live? If a future change makes those answers harder, it is moving against the target architecture.