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.
This commit is contained in:
Firman Ramdhani
2026-05-29 16:21:25 +07:00
parent 9f1158653a
commit d3eb242ebe
7 changed files with 373 additions and 301 deletions
+110 -77
View File
@@ -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/<target>/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/<target>/dist/` directory, and synchronizes the contents into `apps/desktop/web-dist/`. This bridge directory is then ingested by `electron-builder`.
```
┌─────────────────────────────┐ Deployment Bridge ┌──────────────────────────┐
│ apps/<target>/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/<target>/dist/` → `apps/desktop/web-dist/` | Deployment Bridge: build-time synchronization |
| **Development** | `__dirname` relative traversal from `out/main/` | `apps/desktop/out/main/` → `../../` → `apps/` → `<target>/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/<target>/dist/` → `apps/desktop/web-dist/` | Deployment Bridge: build-time synchronization |
| **Development** | `__dirname` relative traversal from `out/main/` | `apps/desktop/out/main/` → `../../` → `apps/` → `<target>/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 `<meta>` tag into the web app's `index.html`, since the In-Flight Policy Injection layer is no longer available:
```html
<meta http-equiv="Content-Security-Policy"
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self' file:; script-src 'self' file:;
style-src 'self' 'unsafe-inline' file:;
connect-src 'self' https:;
img-src 'self' file: data: https:;
font-src 'self' file: data:;" />
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 `<meta>` 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 `<meta>` 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 |