Files
scoreko-electron-dev/docs/refactor/ARCHITECTURE_AUDIT.md
T

6.9 KiB

Architecture Audit

Scope

This audit documents the current architecture of the Electron wrapper and the refactor boundaries agreed for future work. It is based on the previous architectural analysis and should be treated as the source of truth for refactor sessions.

The project is not being redesigned from scratch. The goal is to preserve the current working model, reduce lifecycle risk, and make Electron startup, NodeCG process management, updates, and security easier to test and maintain.

Current Mental Model

The application is an Electron wrapper that lives almost entirely in the main process. There is no custom renderer, no preload script, and no meaningful IPC layer.

Electron starts a local NodeCG runtime and loads HTTP dashboards from localhost or 127.0.0.1. The main process owns startup, provisioning, window creation, navigation policy, update checks, and shutdown.

Existing Strengths

  • The project already uses TypeScript strictness and has passing tests.
  • NodeCG process handling is encapsulated in a dedicated module.
  • Runtime provisioning preserves user-owned directories: cfg, db, and logs.
  • Navigation security is separated from window creation.
  • Packaging has a clear dependency on lib/nodecg as an Electron resource.
  • There is no accidental IPC surface.
  • Browser security defaults are mostly strong: nodeIntegration: false, contextIsolation: true, and sandbox: true.

Current Architecture

src/main/
  main.ts
  nodecg/
    process-manager.ts
    runtime-provisioner.ts
  windows/
    window-factory.ts
    navigation-security.ts
  updates/
    update-manager.ts
scripts/
  build-scoreko-bundle.mjs

Main Process Responsibilities

src/main/main.ts currently performs several roles:

  • Configures Electron app metadata and paths.
  • Sets userData.
  • Acquires the single-instance lock.
  • Prepares the managed NodeCG runtime.
  • Relaunches after first runtime installation.
  • Creates loading and main windows.
  • Starts NodeCG.
  • Waits for HTTP readiness.
  • Loads dashboards.
  • Schedules updates.
  • Handles activation and shutdown.

This is not unmanageable, but it is the main coupling point and should be split carefully.

NodeCG Runtime

The current runtime strategy should be preserved:

  • Install the managed NodeCG runtime under Electron userData.
  • Preserve user data directories across managed runtime replacement.
  • Run NodeCG through the Electron binary using ELECTRON_RUN_AS_NODE.
  • Wait for the local HTTP endpoint before loading dashboards.
  • Stop the process tree on shutdown.

This model is central to the product and should not be replaced during the refactor.

Lifecycle Risks

Import-Time Side Effects

main.ts performs work at import time, including app configuration, path setup, lock acquisition, URL calculation, and global state initialization.

Risk:

  • Startup behavior is harder to test without real Electron.
  • Unit tests must work around global mutable state.
  • Future lifecycle changes are more likely to produce hidden side effects.

Decision:

  • Move side-effect-free calculation into pure functions.
  • Keep Electron side effects inside the entrypoint or an explicit bootstrap layer.

Mixed Orchestration

main.ts combines orchestration with concrete runtime, window, updater, delay, and shutdown behavior.

Risk:

  • Adding lifecycle features increases coupling.
  • Startup and shutdown behavior is hard to cover end to end.
  • Error paths become difficult to reason about.

Decision:

  • Extract a small ApplicationController.
  • Give it explicit lifecycle states.
  • Keep behavior equivalent before moving folders.

macOS Activation

The activate flow can recreate the main window and load the dashboard without guaranteeing NodeCG is alive and ready.

Risk:

  • On macOS, restoring the app after all windows are closed can race with NodeCG readiness.

Decision:

  • Route activation through the same readiness-aware controller used by initial startup.

Process Management Risks

NodeCG process management is generally well isolated, but process tree shutdown is a sensitive boundary.

Current concern:

  • Windows shutdown uses taskkill with shell: true.
  • The PID is numeric, so command injection risk is low, but platform process control should be isolated and tested.

Decision:

  • Move Windows and POSIX process termination into a platform adapter.
  • Keep process manager focused on NodeCG lifecycle.
  • Test process-kill command construction separately.

Updater Risks

The updater is the clearest controlled rewrite candidate.

Current concerns:

  • Remote JSON is trusted without strong schema validation.
  • Asset URLs need stricter protocol and host validation.
  • Installer integrity is not verified.
  • Download behavior should use safe temporary paths and atomic finalization.
  • User-facing Spanish text contains visible encoding corruption.

Decision:

  • Rewrite the updater in small modules.
  • Validate remote update metadata before use.
  • Validate download URLs.
  • Keep dialogs and installation side effects separate from fetch and download logic.
  • Fix encoding as part of the updater cleanup.

Electron Security Audit

Current good defaults:

  • nodeIntegration: false
  • contextIsolation: true
  • sandbox: true
  • No preload script
  • No exposed IPC bridge

Security gaps to address:

  • No explicit permission handler.
  • No explicit devtools policy by environment.
  • webSecurity should be explicitly set.
  • Navigation should remain restricted to allowed local origins.
  • CSP is constrained by NodeCG, but should be reviewed where feasible.

Decision:

  • Keep the browser surface minimal.
  • Do not add preload or IPC unless a clear product need appears.
  • Harden Electron defaults explicitly even when current defaults already behave safely.

Script And Packaging Risks

The build scripts depend on the parent repository layout.

Risk:

  • CI or external workspaces may fail if the expected sibling project is missing.

Decision:

  • Do not introduce a larger build framework.
  • Normalize path validation and shared constants.
  • Make script assumptions explicit and fail with clear errors.

Refactor Boundary

Allowed controlled rewrites:

  • Updater internals.
  • Application lifecycle controller.
  • Platform process termination adapter.
  • Path and URL calculation helpers.

Not allowed:

  • Replacing the Electron plus NodeCG runtime model.
  • Adding a renderer architecture without a product requirement.
  • Adding IPC only for architectural symmetry.
  • Moving folders before behavior is protected by tests.
  • Introducing broad framework abstractions.

Conclusion

The project has a healthier base than a typical Electron wrapper at this stage. The refactor should make the main process boring, explicit, and testable while preserving the current NodeCG runtime model.

The target is not an enterprise architecture. The target is a small Electron shell with clear lifecycle ownership, hardened update and process boundaries, and minimal browser privileges.