From d3eb242ebe66f51400b5901c978fd3eb41634567 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Fri, 29 May 2026 16:21:25 +0700 Subject: [PATCH] docs: Enhance documentation across multiple modules for clarity and structure - Updated CONFIGURATION.md to improve navigation and added mermaid diagrams for better visualization of processes. - Revised IPC_ARCHITECTURE.md to clarify the security model and added diagrams to illustrate the architecture. - Improved README.md files in core-api, core-events, core-i18n, and core-storage for consistency and clarity, including better descriptions and structural enhancements. --- apps/desktop/docs/AUTO_UPDATER.md | 198 ++++++++++++-------------- apps/desktop/docs/CONFIGURATION.md | 187 ++++++++++++++---------- apps/desktop/docs/IPC_ARCHITECTURE.md | 137 ++++++++++-------- packages/core-api/README.md | 4 +- packages/core-events/README.md | 140 +++++++++++------- packages/core-i18n/README.md | 4 +- packages/core-storage/README.md | 4 +- 7 files changed, 373 insertions(+), 301 deletions(-) diff --git a/apps/desktop/docs/AUTO_UPDATER.md b/apps/desktop/docs/AUTO_UPDATER.md index 2f1ade4..686a629 100644 --- a/apps/desktop/docs/AUTO_UPDATER.md +++ b/apps/desktop/docs/AUTO_UPDATER.md @@ -1,56 +1,56 @@ -# Auto-Update System +[← Back to Root](../../../README.md) -The Unified Update Lifecycle. +# Desktop Auto-Update System -> This document defines the strategic implementation of our cross-platform auto-update system. Powered by **electron-updater**, this architecture ensures seamless, background delivery of application patches, maintaining version parity across our global user base. +`apps/desktop` utilizes a unified update lifecycle powered by **electron-updater**. This architecture ensures seamless, background delivery of application patches, maintaining version parity across our global user base. --- -## Table of Contents - -- [Reactive Update Flow](#reactive-update-flow) -- [Current Provider: GitHub Releases](#current-provider-github-releases) -- [Release Workflow: The Deterministic Pipeline](#release-workflow-the-deterministic-pipeline) -- [CI/CD Environment Variables](#cicd-environment-variables) -- [Deployment Strategies](#deployment-strategies) -- [Code Signing: The Trust Boundary](#code-signing-the-trust-boundary) -- [Testing Updates in Development](#testing-updates-in-development) -- [Diagnostic Runbook](#diagnostic-runbook) - ---- - -## Reactive Update Flow +## πŸ— Reactive Update Flow The following diagram illustrates the **Reactive Update Flow**, bridging the Node.js Main Process with the React UI layer through a secure IPC event stream. -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ Main Process (src/main/index.ts) β”‚ -β”‚ β”‚ -β”‚ autoUpdater.checkForUpdatesAndNotify() β”‚ -β”‚ β”‚ β”‚ -β”‚ β”œβ”€β†’ 'checking-for-update' β”‚ -β”‚ β”œβ”€β†’ 'update-available' β†’ { version, releaseDate } β”‚ -β”‚ β”œβ”€β†’ 'download-progress' β†’ { percent, bytesPerSecond } β”‚ -β”‚ β”œβ”€β†’ 'update-downloaded' β†’ { version, releaseNotes } β”‚ -β”‚ └─→ 'error' β†’ { message } β”‚ -β”‚ β”‚ -β”‚ sendToRenderer('updater:*', payload) β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ IPC Bridge ─────────────────────────────── -β”‚ Preload (src/preload/index.ts) β”‚ -β”‚ β”‚ -β”‚ contextBridge.exposeInMainWorld('electronAPI', { β”‚ -β”‚ onUpdateAvailable, onDownloadProgress, β”‚ -β”‚ onUpdateDownloaded, onUpdateError, β”‚ -β”‚ checkForUpdates, installUpdate β”‚ -β”‚ }) β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Renderer ───────────────────────────────── -β”‚ React App (apps/web) β”‚ -β”‚ β”‚ -β”‚ useElectronUpdater() hook β”‚ -β”‚ β†’ Reactive state: status, progress, updateInfo, errorMessage β”‚ -β”‚ β†’ Actions: checkForUpdates(), installUpdate() β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +```mermaid +graph TD + %% ─── Styling Definitions (Dark-Mode Friendly Enterprise Palette) ─── + classDef appEntity fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a + classDef coreEntity fill:#e2e8f0,stroke:#64748b,stroke-width:2px,color:#0f172a + classDef ipcBridge fill:#10b981,stroke:#047857,stroke-width:2px,color:#ffffff + + %% ─── Subgraphs ─── + subgraph MainProcess ["Main Process (src/main/index.ts)"] + AUTO[autoUpdater.checkForUpdatesAndNotify] + EVENTS{{Update Events: progress, downloaded, error}} + SENDER[sendToRenderer] + end + + subgraph Preload ["IPC Bridge (src/preload/index.ts)"] + EXPOSE{contextBridge.exposeInMainWorld} + end + + subgraph Renderer ["Renderer (React App - apps/web)"] + HOOK([useElectronUpdater Hook]) + ACTIONS[UI Actions: Install, Check] + end + + %% ─── Flow & Relationships ─── + AUTO ---> EVENTS + EVENTS ---> SENDER + SENDER ===>|'updater:*' Event Stream| EXPOSE + EXPOSE ===>|electronAPI window object| HOOK + HOOK -.->|Reactive State Status and Progress| ACTIONS + ACTIONS -.->|ipcRenderer.invoke| EXPOSE + EXPOSE -.->|Trigger Update or Install| AUTO + + %% ─── Apply Styles ─── + class AUTO,EVENTS,SENDER coreEntity; + class EXPOSE ipcBridge; + class HOOK,ACTIONS appEntity; + + %% ─── Subgraph Backgrounds (Transparent for Native GitHub Support) ─── + style MainProcess fill:transparent,stroke:#94a3b8,stroke-width:2px,stroke-dasharray: 5 5 + style Preload fill:transparent,stroke:#10b981,stroke-width:2px,stroke-dasharray: 5 5 + style Renderer fill:transparent,stroke:#3b82f6,stroke-width:2px,stroke-dasharray: 5 5 ``` ### Lifecycle Sequence @@ -63,7 +63,7 @@ The following diagram illustrates the **Reactive Update Flow**, bridging the Nod --- -## Current Provider: GitHub Releases +## 🌐 Current Provider: GitHub Releases The update provider is declared in `electron-builder.yml`: @@ -77,6 +77,7 @@ publish: ### Operational Mechanics 1. When `electron-builder --publish always` executes, it: + - Compiles the application for the target platform. - Uploads the installer(s) to a **GitHub Release** tagged with the version from `package.json`. - Generates and uploads the platform-specific manifest: `latest.yml` (Windows), `latest-mac.yml` (macOS), or `latest-linux.yml` (Linux). @@ -88,7 +89,7 @@ publish: --- -## Release Workflow: The Deterministic Pipeline +## πŸš€ Release Workflow: The Deterministic Pipeline To maintain release integrity, follow this deterministic pipeline to synchronize web assets and native binaries. @@ -109,8 +110,7 @@ pnpm run prebuild GH_TOKEN=your_token electron-builder --publish always --config electron-builder.yml ``` -> [!CAUTION] -> **Treat `GH_TOKEN` as a critical secret.** It grants write access to your repository's release assets. Never commit it to version control, never log it in CI output, and always inject it via encrypted secrets or a vault. +> [!CAUTION] > **Treat `GH_TOKEN` as a critical secret.** It grants write access to your repository's release assets. Never commit it to version control, never log it in CI output, and always inject it via encrypted secrets or a vault. ### Automated Release (GitHub Actions) @@ -155,18 +155,18 @@ jobs: --- -## CI/CD Environment Variables +## πŸ” CI/CD Environment Variables -| Variable | Required | Platform | Description | -|---|---|---|---| -| `GH_TOKEN` | Yes | All | GitHub personal access token with `repo` scope. Authorizes release artifact uploads. | -| `CSC_LINK` | macOS/Windows | macOS, Windows | Base64-encoded `.p12` code signing certificate. Generate with: `base64 -i cert.p12 \| pbcopy` | -| `CSC_KEY_PASSWORD` | macOS/Windows | macOS, Windows | Passphrase for the `.p12` certificate. | -| `APPLE_ID` | macOS only | macOS | Apple ID email for notarization submission. | -| `APPLE_APP_SPECIFIC_PASSWORD` | macOS only | macOS | App-specific password generated at [appleid.apple.com](https://appleid.apple.com). | -| `APPLE_TEAM_ID` | macOS only | macOS | Your Apple Developer Team ID. | -| `WIN_CSC_LINK` | Windows only | Windows | Separate Windows code signing certificate (if different from `CSC_LINK`). | -| `WIN_CSC_KEY_PASSWORD` | Windows only | Windows | Passphrase for the Windows certificate. | +| Variable | Required | Platform | Description | +| ----------------------------- | ------------- | -------------- | --------------------------------------------------------------------------------------------- | +| `GH_TOKEN` | Yes | All | GitHub personal access token with `repo` scope. Authorizes release artifact uploads. | +| `CSC_LINK` | macOS/Windows | macOS, Windows | Base64-encoded `.p12` code signing certificate. Generate with: `base64 -i cert.p12 \| pbcopy` | +| `CSC_KEY_PASSWORD` | macOS/Windows | macOS, Windows | Passphrase for the `.p12` certificate. | +| `APPLE_ID` | macOS only | macOS | Apple ID email for notarization submission. | +| `APPLE_APP_SPECIFIC_PASSWORD` | macOS only | macOS | App-specific password generated at [appleid.apple.com](https://appleid.apple.com). | +| `APPLE_TEAM_ID` | macOS only | macOS | Your Apple Developer Team ID. | +| `WIN_CSC_LINK` | Windows only | Windows | Separate Windows code signing certificate (if different from `CSC_LINK`). | +| `WIN_CSC_KEY_PASSWORD` | Windows only | Windows | Passphrase for the Windows certificate. | ### Configuring Secrets @@ -176,7 +176,7 @@ jobs: --- -## Deployment Strategies +## ☁️ Deployment Strategies ### AWS S3 (Private Infrastructure) @@ -195,13 +195,14 @@ publish: **Additional environment variables:** -| Variable | Description | -|---|---| -| `AWS_ACCESS_KEY_ID` | IAM access key with S3 `PutObject` and `GetObject` permissions | -| `AWS_SECRET_ACCESS_KEY` | IAM secret key | +| Variable | Description | +| ----------------------- | -------------------------------------------------------------- | +| `AWS_ACCESS_KEY_ID` | IAM access key with S3 `PutObject` and `GetObject` permissions | +| `AWS_SECRET_ACCESS_KEY` | IAM secret key | **Bucket structure:** -``` + +```text your-bucket/desktop-releases/ β”œβ”€β”€ latest.yml (Windows manifest) β”œβ”€β”€ latest-mac.yml (macOS manifest) @@ -222,13 +223,12 @@ For self-hosted infrastructure (Nginx, Caddy, etc.): ```yaml publish: provider: generic - url: https://updates.your-domain.com/desktop + url: [https://updates.your-domain.com/desktop](https://updates.your-domain.com/desktop) ``` Your server must host the same directory structure as the S3 layout above. -> [!IMPORTANT] -> **MIME Type Configuration**: Ensure your file server correctly serves `.yml` files with `text/yaml` and installer binaries with `application/octet-stream`. Incorrect MIME types will cause download corruption or silent update failures. +> [!IMPORTANT] > **MIME Type Configuration**: Ensure your file server correctly serves `.yml` files with `text/yaml` and installer binaries with `application/octet-stream`. Incorrect MIME types will cause download corruption or silent update failures. **Nginx reference:** @@ -253,10 +253,9 @@ server { --- -## Code Signing: The Trust Boundary +## πŸ›‘οΈ Code Signing: The Trust Boundary -> [!WARNING] -> **Code signing is not merely a requirement β€” it is the Trust Boundary established by the operating system.** macOS Gatekeeper will explicitly terminate unsigned applications or refuse background updates to maintain system integrity. Windows SmartScreen will display alarming warnings to users. Without valid signatures, `electron-updater` will **reject update payloads entirely**. +> [!WARNING] > **Code signing is not merely a requirement β€” it is the Trust Boundary established by the operating system.** macOS Gatekeeper will explicitly terminate unsigned applications or refuse background updates to maintain system integrity. Windows SmartScreen will display alarming warnings to users. Without valid signatures, `electron-updater` will **reject update payloads entirely**. ### macOS @@ -273,7 +272,7 @@ server { ```xml + "[http://www.apple.com/DTDs/PropertyList-1.0.dtd](http://www.apple.com/DTDs/PropertyList-1.0.dtd)"> com.apple.security.cs.allow-jit @@ -300,7 +299,7 @@ server { --- -## Testing Updates in Development +## πŸ§ͺ Testing Updates in Development > [!NOTE] > The auto-updater is **intentionally disabled** in development mode to prevent runtime crashes. Setting `forceDevUpdateConfig` requires a `dev-app-update.yml` file, which introduces unnecessary complexity during local development. @@ -317,6 +316,7 @@ if (IS_DEV) { ``` This means: + - No update check is performed on startup. - No `electron-updater` events are emitted. - The `useElectronUpdater()` hook will remain in `idle` status. @@ -347,52 +347,34 @@ python3 -m http.server 8080 --directory . --- -## Diagnostic Runbook +## ⚠️ Diagnostic Runbook ### Issue: "Update check failed" on startup -| | | -|---|---| -| **Symptom** | Console logs `[AutoUpdater] Startup check failed (possibly offline)` | -| **Root Cause** | The machine is offline, or the update server (GitHub/S3/generic) is unreachable. | -| **Resolution** | No action required. The error is caught in a `try/catch` block, logged to the console, and the application continues to function normally. The next check will occur on the next app launch. | - ---- +| Symptom | Root Cause | Resolution | +| -------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Console logs `[AutoUpdater] Startup check failed (possibly offline)` | The machine is offline, or the update server (GitHub/S3/generic) is unreachable. | No action required. The error is caught in a `try/catch` block, logged to the console, and the application continues to function normally. The next check will occur on the next app launch. | ### Issue: `app-update.yml` not found in production build -| | | -|---|---| -| **Symptom** | `electron-updater` throws "Cannot find app-update.yml" immediately after launch. | -| **Root Cause** | The `publish` block in `electron-builder.yml` is missing or misconfigured. `electron-builder` generates `app-update.yml` only when a valid provider is declared. | -| **Resolution** | Verify the `publish` block exists in `electron-builder.yml`. Run `electron-builder --publish never` and inspect `release/*/resources/app-update.yml` to confirm generation. | - ---- +| Symptom | Root Cause | Resolution | +| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `electron-updater` throws "Cannot find app-update.yml" immediately after launch. | The `publish` block in `electron-builder.yml` is missing or misconfigured. `electron-builder` generates `app-update.yml` only when a valid provider is declared. | Verify the `publish` block exists in `electron-builder.yml`. Run `electron-builder --publish never` and inspect `release/*/resources/app-update.yml` to confirm generation. | ### Issue: "Cannot update: code signature is invalid" (macOS) -| | | -|---|---| -| **Symptom** | The updater downloads a new version but refuses to apply it, logging a signature validation error. | -| **Root Cause** | The application was not signed, or the signing certificate has expired / been revoked. | -| **Resolution** | Ensure `CSC_LINK` and `CSC_KEY_PASSWORD` are correctly set in CI. Verify the packaged app with: `codesign --verify --deep --strict release/mac*/Desktop.app`. Re-sign and re-publish if the certificate was rotated. | - ---- +| Symptom | Root Cause | Resolution | +| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| The updater downloads a new version but refuses to apply it, logging a signature validation error. | The application was not signed, or the signing certificate has expired / been revoked. | Ensure `CSC_LINK` and `CSC_KEY_PASSWORD` are correctly set in CI. Verify the packaged app with: `codesign --verify --deep --strict release/mac*/Desktop.app`. Re-sign and re-publish if the certificate was rotated. | ### Issue: Updates work on Windows/Linux but not macOS -| | | -|---|---| -| **Symptom** | Windows and Linux users receive updates, but macOS users see no update prompt. | -| **Root Cause** | macOS requires **both** a valid code signature AND Apple notarization. Without notarization, Gatekeeper silently quarantines the update payload. | -| **Resolution** | Provide all Apple credential environment variables (`APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`) and ensure `hardenedRuntime: true` is set in `electron-builder.yml`. Re-package and re-publish. | - ---- +| Symptom | Root Cause | Resolution | +| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Windows and Linux users receive updates, but macOS users see no update prompt. | macOS requires **both** a valid code signature AND Apple notarization. Without notarization, Gatekeeper silently quarantines the update payload. | Provide all Apple credential environment variables (`APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`) and ensure `hardenedRuntime: true` is set in `electron-builder.yml`. Re-package and re-publish. | ### Issue: S3/Generic provider returns corrupted downloads -| | | -|---|---| -| **Symptom** | Users report that the update downloads but fails to install, or the downloaded file is 0 bytes. | -| **Root Cause** | The file server is serving update manifests or binaries with incorrect MIME types, or a CDN is caching stale `latest*.yml` files. | -| **Resolution** | Verify MIME types: `.yml` β†’ `text/yaml`, `.exe`/`.dmg`/`.AppImage`/`.zip` β†’ `application/octet-stream`. Add `Cache-Control: no-cache` headers to `latest*.yml` responses. Invalidate CDN cache after publishing a new release. | +| Symptom | Root Cause | Resolution | +| ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Users report that the update downloads but fails to install, or the downloaded file is 0 bytes. | The file server is serving update manifests or binaries with incorrect MIME types, or a CDN is caching stale `latest*.yml` files. | Verify MIME types: `.yml` β†’ `text/yaml`, `.exe`/`.dmg`/`.AppImage`/`.zip` β†’ `application/octet-stream`. Add `Cache-Control: no-cache` headers to `latest*.yml` responses. Invalidate CDN cache after publishing a new release. | diff --git a/apps/desktop/docs/CONFIGURATION.md b/apps/desktop/docs/CONFIGURATION.md index b2f3533..208eb23 100644 --- a/apps/desktop/docs/CONFIGURATION.md +++ b/apps/desktop/docs/CONFIGURATION.md @@ -1,4 +1,6 @@ -# Configuration Guide +[← Back to Root](../../../README.md) + +# Desktop Configuration Guide The Blueprint for Runtime Control. @@ -17,7 +19,7 @@ The Blueprint for Runtime Control. --- -## Target App Orchestration +## 🎯 Target App Orchestration The Desktop Wrapper is architected to embed **any** web application within the monorepo ecosystem. The target application is resolved at build time through a declarative configuration surface in `apps/desktop/.env`. @@ -37,6 +39,7 @@ DESKTOP_DEV_SERVER_URL=http://localhost:5173 To redirect the wrapper to a different application β€” for example, `apps/admin` β€” modify the configuration and re-execute the build pipeline: 1. **Update the `.env` declaration:** + ```env DESKTOP_TARGET_APP=admin DESKTOP_DEV_SERVER_URL=http://localhost:3001 @@ -51,61 +54,71 @@ To redirect the wrapper to a different application β€” for example, `apps/admin` ### The Deployment Bridge -The `prebuild` hook invokes `scripts/copy-web-dist.ts`, which serves as the **Deployment Bridge** between the web workspace and the native container. It reads `DESKTOP_TARGET_APP`, resolves the corresponding `apps//dist/` directory, and synchronizes the contents into `apps/desktop/web-dist/`. This bridge directory is then ingested by `electron-builder` via both the `files` and `extraResources` declarations in `electron-builder.yml`. +The `prebuild` hook invokes `scripts/copy-web-dist.ts`, which serves as the **Deployment Bridge** between the web workspace and the native container. It reads `DESKTOP_TARGET_APP`, resolves the corresponding `apps//dist/` directory, and synchronizes the contents into `apps/desktop/web-dist/`. This bridge directory is then ingested by `electron-builder`. -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” Deployment Bridge β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ apps//dist/ β”‚ ─── copy-web-dist.ts ────────→ β”‚ apps/desktop/web-dist/ β”‚ -β”‚ (Vite build output) β”‚ prebuild hook β”‚ (Native container) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β–Ό - electron-builder - files + extraResources - β”‚ - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Packaged .app/.exe β”‚ - β”‚ resources/web-dist/ β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +```mermaid +graph TD + %% ─── Styling Definitions (Dark-Mode Friendly Enterprise Palette) ─── + classDef webApp fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a + classDef bridge fill:#10b981,stroke:#047857,stroke-width:2px,color:#ffffff + classDef electronApp fill:#e2e8f0,stroke:#64748b,stroke-width:2px,color:#0f172a + classDef finalArtifact fill:#6366f1,stroke:#4338ca,stroke-width:2px,color:#ffffff + + %% ─── Nodes ─── + SOURCE[(apps/TARGET/dist/)] + SCRIPT{copy-web-dist.ts} + DEST[(apps/desktop/web-dist/)] + BUILDER(electron-builder) + OUTPUT([Packaged .app / .exe]) + + %% ─── Flow ─── + SOURCE ===>|Vite Build Output| SCRIPT + SCRIPT ===>|Deployment Bridge Prebuild Hook| DEST + DEST -.->|files and extraResources| BUILDER + BUILDER ===> OUTPUT + + %% ─── Apply Styles ─── + class SOURCE webApp; + class SCRIPT bridge; + class DEST,BUILDER electronApp; + class OUTPUT finalArtifact; ``` --- -## Environment Variables Registry +## πŸ” Environment Variables Registry -| Variable | Default | Security Scope | Consumer | Description | -|---|---|---|---|---| -| `DESKTOP_TARGET_APP` | `web` | Build-time | `copy-web-dist.ts` | Workspace identifier of the web app to embed | -| `DESKTOP_DEV_SERVER_URL` | `http://localhost:5173` | Runtime (dev) | `src/main/index.ts` | Dev server URL loaded in the Electron window during development | -| `GH_TOKEN` | β€” | CI/CD | `electron-builder` | GitHub personal access token for publishing releases | -| `CSC_LINK` | β€” | CI/CD | `electron-builder` | Base64-encoded `.p12` code signing certificate | -| `CSC_KEY_PASSWORD` | β€” | CI/CD | `electron-builder` | Passphrase for the `.p12` certificate | -| `APPLE_ID` | β€” | CI/CD (macOS) | `electron-builder` | Apple ID email for notarization submission | -| `APPLE_APP_SPECIFIC_PASSWORD` | β€” | CI/CD (macOS) | `electron-builder` | App-specific password for notarization | -| `APPLE_TEAM_ID` | β€” | CI/CD (macOS) | `electron-builder` | Apple Developer Team ID | +| Variable | Default | Security Scope | Consumer | Description | +| ----------------------------- | ----------------------- | -------------- | ------------------- | --------------------------------------------------------------- | +| `DESKTOP_TARGET_APP` | `web` | Build-time | `copy-web-dist.ts` | Workspace identifier of the web app to embed | +| `DESKTOP_DEV_SERVER_URL` | `http://localhost:5173` | Runtime (dev) | `src/main/index.ts` | Dev server URL loaded in the Electron window during development | +| `GH_TOKEN` | β€” | CI/CD | `electron-builder` | GitHub personal access token for publishing releases | +| `CSC_LINK` | β€” | CI/CD | `electron-builder` | Base64-encoded `.p12` code signing certificate | +| `CSC_KEY_PASSWORD` | β€” | CI/CD | `electron-builder` | Passphrase for the `.p12` certificate | +| `APPLE_ID` | β€” | CI/CD (macOS) | `electron-builder` | Apple ID email for notarization submission | +| `APPLE_APP_SPECIFIC_PASSWORD` | β€” | CI/CD (macOS) | `electron-builder` | App-specific password for notarization | +| `APPLE_TEAM_ID` | β€” | CI/CD (macOS) | `electron-builder` | Apple Developer Team ID | -> [!IMPORTANT] -> **Build-time** variables are consumed during the `prebuild` phase and baked into the artifact. **Runtime** variables are read by the Electron main process at launch. **CI/CD** variables are secrets injected exclusively in the deployment environment β€” they must never appear in source control or local `.env` files. +> [!IMPORTANT] > **Build-time** variables are consumed during the `prebuild` phase and baked into the artifact. **Runtime** variables are read by the Electron main process at launch. **CI/CD** variables are secrets injected exclusively in the deployment environment β€” they must never appear in source control or local `.env` files. --- -## Deterministic Path Resolution +## πŸ“‚ Deterministic Path Resolution The following matrix defines how the target app's static assets are resolved across every phase of the application lifecycle. Each path is **deterministic** β€” there is no runtime ambiguity. -| Phase | Resolution Strategy | Resolved Path | Context | -|---|---|---|---| -| **Prebuild** | `copy-web-dist.ts` reads `DESKTOP_TARGET_APP` | `monorepo-root/apps//dist/` β†’ `apps/desktop/web-dist/` | Deployment Bridge: build-time synchronization | -| **Development** | `__dirname` relative traversal from `out/main/` | `apps/desktop/out/main/` β†’ `../../` β†’ `apps/` β†’ `/dist/` | Direct filesystem access to the web app's build output | -| **Production** | `process.resourcesPath` | `Contents/Resources/web-dist/` (macOS) / `resources/web-dist/` (Windows/Linux) | OS-specific resource directory within the packaged binary | +| Phase | Resolution Strategy | Resolved Path | Context | +| --------------- | ----------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------- | +| **Prebuild** | `copy-web-dist.ts` reads `DESKTOP_TARGET_APP` | `monorepo-root/apps//dist/` β†’ `apps/desktop/web-dist/` | Deployment Bridge: build-time synchronization | +| **Development** | `__dirname` relative traversal from `out/main/` | `apps/desktop/out/main/` β†’ `../../` β†’ `apps/` β†’ `/dist/` | Direct filesystem access to the web app's build output | +| **Production** | `process.resourcesPath` | `Contents/Resources/web-dist/` (macOS) / `resources/web-dist/` (Windows/Linux) | OS-specific resource directory within the packaged binary | > [!NOTE] > The development path relies on `__dirname` pointing to `apps/desktop/out/main/` at runtime. If electron-vite's output directory is ever reconfigured, this traversal must be updated in `getWebDistPath()` within `src/main/index.ts`. --- -## Production Routing: Overcoming Protocol Constraints +## πŸš€ Production Routing: Overcoming Protocol Constraints ### The Constraint @@ -117,18 +130,34 @@ Electron's default `file://` protocol breaks this contract. Requesting `file:/// The `app://` scheme is a **Privileged Virtual File System** that resolves SPA routing conflicts by implementing a **Heuristic Resource Loader**. It operates as follows: -``` -Request: app://-/settings/profile - β”‚ - β”œβ”€ Decode URI β†’ "settings/profile" - β”‚ - β”œβ”€ Normalize + validate path (security boundary check) - β”‚ - β”œβ”€ Does web-dist/settings/profile exist as a file? - β”‚ β”œβ”€ YES β†’ Serve with correct MIME type + CSP headers - β”‚ └─ NO β†’ Heuristic Fallback: serve web-dist/index.html - β”‚ - └─ React Router resolves /settings/profile client-side +```mermaid +graph TD + %% ─── Styling Definitions ─── + classDef request fill:#f59e0b,stroke:#b45309,stroke-width:2px,color:#ffffff + classDef process fill:#e2e8f0,stroke:#64748b,stroke-width:2px,color:#0f172a + classDef decision fill:#10b981,stroke:#047857,stroke-width:2px,color:#ffffff + classDef success fill:#3b82f6,stroke:#1d4ed8,stroke-width:2px,color:#ffffff + + %% ─── Nodes ─── + REQ([Request: app://-/settings/profile]) + DECODE[Decode URI and Normalize Path] + CHECK{File exists in web-dist?} + SERVE_FILE[Serve Asset with MIME + CSP] + SERVE_FALLBACK[Heuristic Fallback: Serve index.html] + REACT([React Router Handles Route]) + + %% ─── Flow ─── + REQ ---> DECODE + DECODE ---> CHECK + CHECK ===>|YES| SERVE_FILE + CHECK -.->|NO| SERVE_FALLBACK + SERVE_FALLBACK ---> REACT + + %% ─── Apply Styles ─── + class REQ request; + class DECODE process; + class CHECK decision; + class SERVE_FILE,SERVE_FALLBACK,REACT success; ``` If a requested URI does not map to a physical asset, the handler intelligently intercepts the request to serve the `index.html` entry point, allowing React Router to maintain stateful client-side navigation. This ensures that deep links, page refreshes, and direct URL entry all function without modification to the React app's routing configuration. @@ -142,11 +171,11 @@ protocol.registerSchemesAsPrivileged([ { scheme: 'app', privileges: { - standard: true, // Enables URL parsing (host, path, query) - secure: true, // Treated as a secure origin (HTTPS equivalent) + standard: true, // Enables URL parsing (host, path, query) + secure: true, // Treated as a secure origin (HTTPS equivalent) supportFetchAPI: true, // Allows fetch() from this scheme - corsEnabled: true, // Enables CORS for cross-origin requests - stream: true, // Supports streaming responses + corsEnabled: true, // Enables CORS for cross-origin requests + stream: true, // Supports streaming responses }, }, ]); @@ -154,25 +183,24 @@ protocol.registerSchemesAsPrivileged([ --- -## Defense-in-Depth: Multi-Layered Protection +## πŸ›‘οΈ Defense-in-Depth: Multi-Layered Protection The custom protocol handler enforces a **multi-layered defense perimeter** that goes beyond standard Electron security defaults. -| Layer | Technique | Implementation | Threat Mitigated | -|---|---|---|---| -| **I/O Sanitization** | Path traversal guard | `normalize()` + `startsWith()` validation against `web-dist/` boundary | Directory traversal attacks (`../../etc/passwd`) β†’ `403 Forbidden` | -| **In-Flight Policy Injection** | CSP response headers | `Content-Security-Policy` injected as HTTP response headers on every HTML payload | XSS execution via script injection | -| **Cryptographic Isolation** | Privileged scheme registration | `app` scheme registered with `standard`, `secure`, `supportFetchAPI`, `corsEnabled` | Scheme downgrade attacks; the renderer treats `app://` identically to `https://` | -| **Resource Type Validation** | `statSync.isFile()` check | Only regular files are served; directories return the SPA fallback | Information disclosure via directory listing | -| **Origin Sanitization** | CORS bypass proxy | `webRequest.onBeforeSendHeaders` strips `app://` Origin headers on outgoing requests | Backend CORS rejection of non-standard origins | -| **Navigation Confinement** | `will-navigate` guard | Blocks navigation to URLs outside `app://` and the authorized dev server | Phishing via in-app redirect to malicious sites | +| Layer | Technique | Implementation | Threat Mitigated | +| ------------------------------ | ------------------------------ | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | +| **I/O Sanitization** | Path traversal guard | `normalize()` + `startsWith()` validation against `web-dist/` boundary | Directory traversal attacks (`../../etc/passwd`) β†’ `403 Forbidden` | +| **In-Flight Policy Injection** | CSP response headers | `Content-Security-Policy` injected as HTTP response headers on every HTML payload | XSS execution via script injection | +| **Cryptographic Isolation** | Privileged scheme registration | `app` scheme registered with `standard`, `secure`, `supportFetchAPI`, `corsEnabled` | Scheme downgrade attacks; the renderer treats `app://` identically to `https://` | +| **Resource Type Validation** | `statSync.isFile()` check | Only regular files are served; directories return the SPA fallback | Information disclosure via directory listing | +| **Origin Sanitization** | CORS bypass proxy | `webRequest.onBeforeSendHeaders` strips `app://` Origin headers on outgoing requests | Backend CORS rejection of non-standard origins | +| **Navigation Confinement** | `will-navigate` guard | Blocks navigation to URLs outside `app://` and the authorized dev server | Phishing via in-app redirect to malicious sites | --- -## Break Glass Procedure: Disaster Recovery Protocol +## ⚠️ Break Glass Procedure: Disaster Recovery Protocol -> [!CAUTION] -> **This is a formal Disaster Recovery Protocol.** Execute only if the custom `app://` protocol causes an irrecoverable failure β€” for example, a critical third-party library that refuses to operate under a non-standard URI scheme. This procedure requires coordinated changes across both the React application and the Electron main process. Estimated recovery time: **15 minutes**. +> [!CAUTION] > **This is a formal Disaster Recovery Protocol.** Execute only if the custom `app://` protocol causes an irrecoverable failure β€” for example, a critical third-party library that refuses to operate under a non-standard URI scheme. This procedure requires coordinated changes across both the React application and the Electron main process. Estimated recovery time: **15 minutes**. ### Step 1: Switch the Router β€” React Application @@ -206,6 +234,7 @@ All routes transition to hash-based addressing: `#/app/dashboard`, `#/auth/login In `apps/desktop/src/main/index.ts`, execute the following surgical removals: **a)** Remove the scheme registration block at the top of the file: + ```diff - protocol.registerSchemesAsPrivileged([ ... ]); ``` @@ -215,6 +244,7 @@ In `apps/desktop/src/main/index.ts`, execute the following surgical removals: **c)** Remove the `registerAppProtocol()` invocation inside `app.whenReady()`. **d)** Redirect production content loading in `createWindow()`: + ```diff if (IS_DEV) { mainWindow.loadURL(DEV_SERVER_URL); @@ -227,23 +257,26 @@ In `apps/desktop/src/main/index.ts`, execute the following surgical removals: ``` **e)** Inject a CSP `` tag into the web app's `index.html`, since the In-Flight Policy Injection layer is no longer available: + ```html - + font-src 'self' file: data:;" +/> ``` ### Trade-off Analysis -| Dimension | Custom `app://` Protocol | `file://` + HashRouter | -|---|---|---| -| **Aesthetic Integrity** | Clean URLs: `/app/dashboard` | Hash prefix: `#/app/dashboard` | -| **Router Compatibility** | `BrowserRouter` β€” zero changes required | Must migrate to `HashRouter` | -| **Deep Linking** | Full, native-style support | Hash-based only | -| **Protocol-Native Compatibility** | Rare edge cases with non-standard scheme detection | Maximum third-party compatibility | -| **Security Delivery Vector** | CSP via response headers (strongest enforcement) | CSP via `` tag (bypassable by early script execution) | -| **Implementation Complexity** | Higher (custom protocol handler + security layers) | Lower (no custom protocol infrastructure) | -| **Recovery Time** | β€” | ~15 minutes, 2 files | +| Dimension | Custom `app://` Protocol | `file://` + HashRouter | +| --------------------------------- | -------------------------------------------------- | ----------------------------------------------------------- | +| **Aesthetic Integrity** | Clean URLs: `/app/dashboard` | Hash prefix: `#/app/dashboard` | +| **Router Compatibility** | `BrowserRouter` β€” zero changes required | Must migrate to `HashRouter` | +| **Deep Linking** | Full, native-style support | Hash-based only | +| **Protocol-Native Compatibility** | Rare edge cases with non-standard scheme detection | Maximum third-party compatibility | +| **Security Delivery Vector** | CSP via response headers (strongest enforcement) | CSP via `` tag (bypassable by early script execution) | +| **Implementation Complexity** | Higher (custom protocol handler + security layers) | Lower (no custom protocol infrastructure) | +| **Recovery Time** | β€” | ~15 minutes, 2 files | diff --git a/apps/desktop/docs/IPC_ARCHITECTURE.md b/apps/desktop/docs/IPC_ARCHITECTURE.md index 6923f21..b36d79f 100644 --- a/apps/desktop/docs/IPC_ARCHITECTURE.md +++ b/apps/desktop/docs/IPC_ARCHITECTURE.md @@ -1,3 +1,5 @@ +[← Back to Root](../../../README.md) + # IPC Architecture & Security Model The Secure Communication Blueprint. @@ -23,44 +25,56 @@ The desktop wrapper enforces a **strict privilege separation** between three exe ### Trust Level Matrix -| Context | Trust Level | Privilege Scope | Security Guarantee | -|---|---|---|---| -| **Main Process** | Fully Trusted | Unrestricted Node.js access: filesystem, network, printers, OS APIs, child processes | Only code authored by the engineering team executes here | -| **Preload Script** | Controlled | Restricted to `ipcRenderer.invoke()` and `ipcRenderer.send()` β€” no direct Node.js access | Executes in a **Hermetically Sealed Context** β€” isolated from both the Main Process globals and the Renderer's DOM | -| **Renderer** | Untrusted | Standard browser sandbox β€” zero Node.js API surface | Designated as a **Zero-Trust Environment** β€” may execute third-party code, npm packages, or XSS payloads | +| Context | Trust Level | Privilege Scope | Security Guarantee | +| ------------------ | ------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| **Main Process** | Fully Trusted | Unrestricted Node.js access: filesystem, network, printers, OS APIs, child processes | Only code authored by the engineering team executes here | +| **Preload Script** | Controlled | Restricted to `ipcRenderer.invoke()` and `ipcRenderer.send()` β€” no direct Node.js access | Executes in a **Hermetically Sealed Context** β€” isolated from both the Main Process globals and the Renderer's DOM | +| **Renderer** | Untrusted | Standard browser sandbox β€” zero Node.js API surface | Designated as a **Zero-Trust Environment** β€” may execute third-party code, npm packages, or XSS payloads | ### Process Topology -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ MAIN PROCESS [Fully Trusted] β”‚ -β”‚ β”‚ -β”‚ UNRESTRICTED PRIVILEGES β”‚ -β”‚ Filesystem Β· Network Β· Printers Β· Native APIs Β· Child Processes β”‚ -β”‚ Auto-Updater Β· OS Integration Β· System Notifications β”‚ -β”‚ β”‚ -β”‚ ipcMain.handle('channel', handler) ← Command handlers β”‚ -β”‚ ipcMain.on('channel', handler) ← Event listeners β”‚ -β”‚ webContents.send('channel', data) ← Downstream push β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Non-Bypassable Isolation Boundary ────────────────── -β”‚ PRELOAD SCRIPT [Secure Gateway] β”‚ -β”‚ β”‚ -β”‚ HERMETICALLY SEALED CONTEXT β”‚ -β”‚ Performs Interface Narrowing: transforms broad IPC capabilities β”‚ -β”‚ into a minimal, auditable API surface. Acts as the sole β”‚ -β”‚ authorized mediator between trusted and untrusted contexts. β”‚ -β”‚ β”‚ -β”‚ contextBridge.exposeInMainWorld('electronAPI', { ... }) β”‚ -β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Non-Bypassable Isolation Boundary ────────────────── -β”‚ RENDERER [Zero-Trust Environment] β”‚ -β”‚ β”‚ -β”‚ UNTRUSTED WEB CONTENT β”‚ -β”‚ Standard browser sandbox. Zero access to: require, __dirname, β”‚ -β”‚ process, fs, child_process, net, os, ipcRenderer. β”‚ -β”‚ β”‚ -β”‚ ONLY authorized interaction vector: β”‚ -β”‚ window.electronAPI.methodName(args) β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +```mermaid +graph TD + %% ─── Styling Definitions (Dark-Mode Friendly Enterprise Palette) ─── + classDef trustedLayer fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#0f172a + classDef gatewayLayer fill:#f0fdf4,stroke:#10b981,stroke-width:2px,color:#064e3b + classDef untrustedLayer fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#7f1d1d + classDef functionNode fill:#e2e8f0,stroke:#64748b,stroke-width:2px,color:#0f172a + + %% ─── Subgraphs ─── + subgraph Main ["MAIN PROCESS [Fully Trusted]"] + M_DESC["Unrestricted Node.js Privileges"] + IPC_MAIN_H[ipcMain.handle] + IPC_MAIN_O[ipcMain.on] + end + + subgraph Preload ["PRELOAD SCRIPT [Secure Gateway]"] + P_DESC["Hermetically Sealed Context (Interface Narrowing)"] + CTX_BRIDGE{contextBridge.exposeInMainWorld} + end + + subgraph Renderer ["RENDERER [Zero-Trust Environment]"] + R_DESC["Standard Browser Sandbox (No Node.js APIs)"] + E_API([window.electronAPI]) + end + + %% ─── Flow & Relationships ─── + E_API ===>|Only Authorized Vector| CTX_BRIDGE + CTX_BRIDGE --->|Transforms to narrow IPC calls| IPC_MAIN_H + CTX_BRIDGE --->|Transforms to narrow IPC calls| IPC_MAIN_O + + %% ─── Apply Styles ─── + class Main trustedLayer; + class Preload gatewayLayer; + class Renderer untrustedLayer; + class M_DESC,P_DESC,R_DESC,IPC_MAIN_H,IPC_MAIN_O functionNode; + class CTX_BRIDGE gatewayLayer; + class E_API untrustedLayer; + + %% ─── Subgraph Backgrounds (Transparent for Native GitHub Support) ─── + style Main fill:transparent,stroke:#3b82f6,stroke-width:2px,stroke-dasharray: 5 5 + style Preload fill:transparent,stroke:#10b981,stroke-width:2px,stroke-dasharray: 5 5 + style Renderer fill:transparent,stroke:#ef4444,stroke-width:2px,stroke-dasharray: 5 5 ``` The Preload Script functions as a **Secure Gateway** that performs **Interface Narrowing** β€” it transforms the broad, unrestricted IPC capabilities of the Main Process into a deliberately narrow, type-safe API surface. The renderer communicates with native functionality **exclusively** through this gateway. There are no alternative paths, no escape hatches, and no backdoors. @@ -69,12 +83,12 @@ The Preload Script functions as a **Secure Gateway** that performs **Interface N These settings are declared in `BrowserWindow.webPreferences` and are **non-negotiable**: -| Setting | Value | Enforcement | -|---|---|---| -| `contextIsolation` | `true` | The Preload executes in a hermetically sealed V8 context. The renderer **cannot** access `require()`, Node.js globals, or any variable from the preload's scope. | -| `nodeIntegration` | `false` | **Zero** Node.js API surface in the renderer. `fs`, `child_process`, `os`, `net`, and all built-in modules are completely unavailable. | -| `sandbox` | `true` | The renderer process runs inside a **Chromium OS-level sandbox**, restricting system calls and file access at the kernel level. | -| `webSecurity` | `true` | The same-origin policy is **strictly enforced**, preventing cross-origin data exfiltration from the renderer. | +| Setting | Value | Enforcement | +| ------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `contextIsolation` | `true` | The Preload executes in a hermetically sealed V8 context. The renderer **cannot** access `require()`, Node.js globals, or any variable from the preload's scope. | +| `nodeIntegration` | `false` | **Zero** Node.js API surface in the renderer. `fs`, `child_process`, `os`, `net`, and all built-in modules are completely unavailable. | +| `sandbox` | `true` | The renderer process runs inside a **Chromium OS-level sandbox**, restricting system calls and file access at the kernel level. | +| `webSecurity` | `true` | The same-origin policy is **strictly enforced**, preventing cross-origin data exfiltration from the renderer. | --- @@ -82,8 +96,7 @@ These settings are declared in `BrowserWindow.webPreferences` and are **non-nego Every native feature in this architecture **must** follow the Three-Step Bridge β€” a Standard Operating Procedure (SOP) that ensures traceability, type-safety, and auditability across the entire IPC surface. -> [!IMPORTANT] -> **Deterministic Synchronization**: Maintaining parity between the Main Process handler, the Preload Gateway exposure, and the TypeScript interface declaration is **mandatory**. A mismatch between any two of the three layers will result in either a **Type-Safety Gap** (silent failures in development) or a **Runtime Regression** (crashes in production). +> [!IMPORTANT] > **Deterministic Synchronization**: Maintaining parity between the Main Process handler, the Preload Gateway exposure, and the TypeScript interface declaration is **mandatory**. A mismatch between any two of the three layers will result in either a **Type-Safety Gap** (silent failures in development) or a **Runtime Regression** (crashes in production). ### Step 1: Register the Handler β€” Main Process @@ -134,6 +147,7 @@ contextBridge.exposeInMainWorld('electronAPI', electronAPI); ``` **Non-negotiable rules:** + - **Never** expose `ipcRenderer` directly β€” this is a **Catastrophic Failure** pattern. - **Never** expose `ipcRenderer.on` without cleanup β€” use `createEventSubscription()`, which returns an unsubscribe function for React `useEffect` lifecycle management. - **Always** declare explicit TypeScript types for all function signatures. @@ -160,17 +174,18 @@ The following is the **complete, authoritative registry** of all authorized IPC ### Printer Subsystem -| Channel | Direction | Pattern | Payload | Access Control | -|---|---|---|---|---| -| `printer:get-list` | Renderer β†’ Main β†’ Renderer | `invoke` / `handle` | Returns `ElectronPrinterInfo[]` | Read-only hardware enumeration | -| `printer:print` | Renderer β†’ Main β†’ Renderer | `invoke` / `handle` | Accepts `ElectronPrintOptions`, returns `{ success, failureReason? }` | Controlled hardware invocation | +| Channel | Direction | Pattern | Payload | Access Control | +| ------------------ | -------------------------- | ------------------- | --------------------------------------------------------------------- | ------------------------------ | +| `printer:get-list` | Renderer β†’ Main β†’ Renderer | `invoke` / `handle` | Returns `ElectronPrinterInfo[]` | Read-only hardware enumeration | +| `printer:print` | Renderer β†’ Main β†’ Renderer | `invoke` / `handle` | Accepts `ElectronPrintOptions`, returns `{ success, failureReason? }` | Controlled hardware invocation | **Main process handler:** `setupPrinterIPC()` in `src/main/index.ts` **Preload Gateway surface:** + ```typescript -getPrinters: () => ipcRenderer.invoke('printer:get-list') -print: (options?) => ipcRenderer.invoke('printer:print', options) +getPrinters: () => ipcRenderer.invoke('printer:get-list'); +print: (options?) => ipcRenderer.invoke('printer:print', options); ``` **React consumption hook:** `useElectronPrinter()` in `apps/web/src/hooks/use-electron-printer.ts` @@ -179,16 +194,16 @@ print: (options?) => ipcRenderer.invoke('printer:print', options) ### Auto-Updater Subsystem -| Channel | Direction | Pattern | Payload | Access Control | -|---|---|---|---|---| -| `updater:check` | Renderer β†’ Main | `invoke` / `handle` | Returns update check result | Read-only version query | -| `updater:install` | Renderer β†’ Main | `send` / `on` | No payload | Privileged: quits app and installs | -| `updater:checking` | Main β†’ Renderer | `send` | No payload | Status notification | -| `updater:available` | Main β†’ Renderer | `send` | `UpdateInfo { version, releaseDate, releaseNotes }` | Status notification | -| `updater:not-available` | Main β†’ Renderer | `send` | `UpdateInfo` | Status notification | -| `updater:progress` | Main β†’ Renderer | `send` | `ProgressInfo { percent, bytesPerSecond, transferred, total }` | Progress telemetry | -| `updater:downloaded` | Main β†’ Renderer | `send` | `UpdateInfo` | Status notification | -| `updater:error` | Main β†’ Renderer | `send` | Error message string | Error telemetry | +| Channel | Direction | Pattern | Payload | Access Control | +| ----------------------- | --------------- | ------------------- | -------------------------------------------------------------- | ---------------------------------- | +| `updater:check` | Renderer β†’ Main | `invoke` / `handle` | Returns update check result | Read-only version query | +| `updater:install` | Renderer β†’ Main | `send` / `on` | No payload | Privileged: quits app and installs | +| `updater:checking` | Main β†’ Renderer | `send` | No payload | Status notification | +| `updater:available` | Main β†’ Renderer | `send` | `UpdateInfo { version, releaseDate, releaseNotes }` | Status notification | +| `updater:not-available` | Main β†’ Renderer | `send` | `UpdateInfo` | Status notification | +| `updater:progress` | Main β†’ Renderer | `send` | `ProgressInfo { percent, bytesPerSecond, transferred, total }` | Progress telemetry | +| `updater:downloaded` | Main β†’ Renderer | `send` | `UpdateInfo` | Status notification | +| `updater:error` | Main β†’ Renderer | `send` | Error message string | Error telemetry | **Main process handlers:** `setupAutoUpdaterIPC()` + `setupAutoUpdaterEvents()` in `src/main/index.ts` @@ -283,7 +298,7 @@ contextBridge.exposeInMainWorld('ipc', ipcRenderer); contextBridge.exposeInMainWorld('require', require); ``` -**Threat:** The renderer can `require('child_process').exec('rm -rf /')`. A single XSS vulnerability in *any* dependency β€” including transitive ones β€” escalates to **full filesystem access, credential theft, reverse shells, and data exfiltration**. +**Threat:** The renderer can `require('child_process').exec('rm -rf /')`. A single XSS vulnerability in _any_ dependency β€” including transitive ones β€” escalates to **full filesystem access, credential theft, reverse shells, and data exfiltration**. **Classification:** **Total System Compromise** @@ -294,7 +309,7 @@ contextBridge.exposeInMainWorld('require', require); ```typescript // VIOLATION: Catastrophic Failure β€” Complete Boundary Collapse new BrowserWindow({ - webPreferences: { nodeIntegration: true, contextIsolation: false } + webPreferences: { nodeIntegration: true, contextIsolation: false }, }); ``` diff --git a/packages/core-api/README.md b/packages/core-api/README.md index f87fc7a..41f8b47 100644 --- a/packages/core-api/README.md +++ b/packages/core-api/README.md @@ -1,7 +1,7 @@ -# Enterprise API Engine (`@repo/core-api`) - [← Back to Root](../../README.md) +# Enterprise API Engine (`@repo/core-api`) + The platform-agnostic API engine for the monorepo. Provides an isolated HTTP client factory, a unified observability pipeline (Grafana Faro + OpenTelemetry), and a generic data services engine. **This package enforces App Autonomy (IoC).** The core provides the engine and interceptor pipelines, but the consuming applications (`apps/web`, `apps/landing`) inject their own specific configurations, authentication tokens, and error handling behaviors. diff --git a/packages/core-events/README.md b/packages/core-events/README.md index 9bad4b8..6f6c22e 100644 --- a/packages/core-events/README.md +++ b/packages/core-events/README.md @@ -1,74 +1,116 @@ -# @repo/core-events - [← Back to Root](../../README.md) -## Overview +# Event Bus (`@repo/core-events`) -`@repo/core-events` is the **decoupled Nervous System** of the ERP. It provides a highly performant, strictly typed Event Bus powered by `mitt` and React hooks. +The Global Pub/Sub & Hardware Integration Blueprint. -**This package is a pure tool.** It ships zero application-specific events. Each consuming app (`apps/web`, `apps/landing`, etc.) registers its own events autonomously using TypeScript Declaration Merging β€” the same Inversion of Control pattern used by `@repo/core-api`'s `createHttpClient` factory. - -By routing communication through a centralized event bus, we achieve: -- **App Autonomy**: The core defines the bus. The app defines the contract. No circular knowledge. -- **Zero Coupling**: Publishers and subscribers don't need to import or know about each other. -- **Extreme Performance**: Components can subscribe to high-frequency data streams (like WebSockets) and update their own local state *without* triggering massive React tree re-renders. -- **Memory Safety**: The provided `useAppEvent` hook automatically handles subscription cleanup on component unmount, preventing the most common source of memory leaks in SPA architectures. +> This module provides a strictly-typed, global event bus for the monorepo ecosystem. It decouples cross-component communication and manages real-time hardware signals (such as printers and POS peripherals), ensuring a reactive and memory-safe architecture across all applications. --- -## Architecture +## 🧠 System Overview + +`@repo/core-events` is the **decoupled Nervous System** of the ERP. It provides a highly performant, strictly-typed Event Bus powered by `mitt` and custom React hooks. + +**This package is a pure tool.** It ships zero application-specific events. Each consuming app (`apps/web`, `apps/desktop`, etc.) registers its own events autonomously using **TypeScript Declaration Merging** β€” the exact same Inversion of Control (IoC) pattern utilized by our `@repo/core-api` factory and `@repo/core-storage` engine. + +### Architectural Topology + +### 1. Conceptual Topology: The Pub/Sub Data Flow +This diagram illustrates the high-level concept of our decoupled architecture, demonstrating how application-specific types merge into the core bus. + +```mermaid +graph LR + %% ─── Styling Definitions (Dark-Mode Friendly Enterprise Palette) ─── + classDef appEntity fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a + classDef coreEntity fill:#e2e8f0,stroke:#64748b,stroke-width:2px,color:#0f172a + classDef busEntity fill:#10b981,stroke:#047857,stroke-width:2px,color:#ffffff + + %% ─── Nodes ─── + TYPES[[App-Specific Event Types]] + PUB([Publisher Component]) + BUS{Global Event Bus 'mitt'} + SUB([Subscriber Component]) + + %% ─── Flow ─── + TYPES -.->|Declaration Merging| BUS + PUB ===>|emit 'event', payload| BUS + BUS ===>|useAppEvent 'event'| SUB + + %% ─── Apply Styles ─── + class TYPES,PUB,SUB appEntity; + class BUS busEntity; +``` + +### 2. System Architecture: Core Engine vs. App Autonomy +This detailed diagram shows the exact boundaries between the @repo/core-events engine and the consuming application, highlighting real-world publishers (e.g., Cashier UI) and subscribers. ```mermaid graph TD + %% ─── Styling Definitions (Dark-Mode Friendly Enterprise Palette) ─── + classDef appComponent fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e3a8a + classDef injection fill:#f59e0b,stroke:#b45309,stroke-width:2px,color:#ffffff + classDef registry fill:#10b981,stroke:#047857,stroke-width:2px,color:#ffffff + classDef coreBus fill:#6366f1,stroke:#4338ca,stroke-width:2px,color:#ffffff + + %% ─── Subgraphs ─── subgraph Core ["@repo/core-events (Pure Tool)"] - R["AppEventRegistry
(empty interface)"] - T["AppEvents = mapped type"] - E((Event Bus
mitt)) - H[useAppEvent / usePublishEvent] - R --> T --> E - E --> H + R[AppEventRegistry Empty Interface] + T[AppEvents Mapped Type] + E((Global Event Bus mitt)) + H[Hooks: useAppEvent / usePublishEvent] end subgraph Apps ["apps/web (App Autonomy)"] - D["events.d.ts
declare module augmentation"] - A[Cashier UI] - B[Profile Settings] - C[WebSocket Client] - X[Electron IPC Bridge] - Y[IndexedDB Sync] - Z[Stock Grid Row] + D[[events.d.ts Declaration Merging]] + + %% Publishers + A([Cashier UI]) + B([Profile Settings]) + C([WebSocket Client]) + + %% Subscribers + X([Electron IPC Bridge]) + Y([IndexedDB Sync]) + Z([Stock Grid Row]) end - D -. "merges into" .-> R + %% ─── Flow & Relationships ─── + D -.->|Augments| R + R ---> T ---> E + E ---> H - A -- "DEVICE:PRINT_RECEIPT" --> E - B -- "AUTH:PROFILE_UPDATED" --> E - C -- "WS:STOCK_UPDATE" --> E + %% Emitting Events + A ===>|DEVICE:PRINT_RECEIPT| E + B ===>|AUTH:PROFILE_UPDATED| E + C ===>|WS:STOCK_UPDATE| E - E -.-> X - E -.-> Y - E -.-> Z + %% Subscribing to Events + E -.->|Triggers| X + E -.->|Triggers| Y + E -.->|Triggers| Z - %% Styling Subgraphs (Backgrounds) - style Core fill:#f8f9fa,stroke:#ced4da,stroke-width:2px,color:#495057 - style Apps fill:#e7f5ff,stroke:#74c0fc,stroke-width:2px,color:#1864ab + %% ─── Apply Styles ─── + class A,B,C,X,Y,Z appComponent; + class D injection; + class R,T registry; + class E,H coreBus; - %% Styling Core Engine (Purple) & Contracts (Green) - style R fill:#20c997,stroke:#089981,color:#fff - style T fill:#20c997,stroke:#089981,color:#fff - style E fill:#845ef7,stroke:#5f3dc4,color:#fff - style H fill:#845ef7,stroke:#5f3dc4,color:#fff - - %% Styling App Injection (Orange) & Components (Blue) - style D fill:#fd7e14,stroke:#d9480f,color:#fff - style A fill:#339af0,stroke:#1864ab,color:#fff - style B fill:#339af0,stroke:#1864ab,color:#fff - style C fill:#339af0,stroke:#1864ab,color:#fff - style X fill:#339af0,stroke:#1864ab,color:#fff - style Y fill:#339af0,stroke:#1864ab,color:#fff - style Z fill:#339af0,stroke:#1864ab,color:#fff + %% ─── Subgraph Backgrounds (Transparent for Native GitHub Support) ─── + style Core fill:transparent,stroke:#94a3b8,stroke-width:2px,stroke-dasharray: 5 5 + style Apps fill:transparent,stroke:#3b82f6,stroke-width:2px,stroke-dasharray: 5 5 ``` +### Core Value Proposition + +By routing communication through this centralized event bus, we achieve: + +* **App Autonomy**: The core defines the engine. The app defines the contract. There is zero circular dependency. +* **Zero Coupling**: Publishers and subscribers do not need to import, reference, or know about each other's existence. +* **Extreme Performance**: Components can subscribe to high-frequency data streams (like WebSockets or hardware signals) and update their own local state *without* triggering massive React tree re-renders. +* **Memory Safety**: The provided `useAppEvent` hook automatically handles subscription cleanup on component unmount, proactively preventing the most common source of memory leaks in Single Page Architectures (SPAs). + + --- ## Defining Events (Module Augmentation) diff --git a/packages/core-i18n/README.md b/packages/core-i18n/README.md index 853098f..2495a15 100644 --- a/packages/core-i18n/README.md +++ b/packages/core-i18n/README.md @@ -1,7 +1,7 @@ -# Enterprise i18n Architecture (`@repo/core-i18n`) - [← Back to Root](../../README.md) +# i18n Architecture (`@repo/core-i18n`) + A highly decoupled, type-safe internationalization engine for the monorepo. It uses a **Hybrid Namespace Strategy**: diff --git a/packages/core-storage/README.md b/packages/core-storage/README.md index f14ceac..1652ba6 100644 --- a/packages/core-storage/README.md +++ b/packages/core-storage/README.md @@ -1,7 +1,7 @@ -# Enterprise Storage Engine (`@repo/core-storage`) - [← Back to Root](../../README.md) +# Storage Engine (`@repo/core-storage`) + `@repo/core-storage` is the **Enterprise-grade, multi-tool storage engine** for the Eigen Monorepo. It provides a unified set of strictly-typed, secure, and fault-tolerant storage mechanisms tailored for React applications. It enforces strict **Inversion of Control (IoC)**β€”the core engine knows absolutely nothing about your application's business domains; instead, the consuming apps inject their own configurations and types.