Angular 21 + Electron desktop foundation built as an Nx monorepo.
- Human engineers onboarding to the desktop platform.
- AI coding agents operating inside this workspace.
This repository is a secure, typed baseline for desktop applications with strict privilege boundaries:
- Renderer UI:
apps/renderer - Electron main process:
apps/desktop-main - Electron preload bridge:
apps/desktop-preload - E2E tests:
apps/renderer-e2e - Shared IPC contracts:
libs/shared/contracts - Typed desktop API surface for renderer:
libs/platform/desktop-api
Core design principle:
- Renderer is untrusted.
- Privileged capabilities terminate in preload/main.
- Contracts are validated and versioned.
- Renderer calls
window.desktop.*via@electron-foundation/desktop-api. - Preload validates request/response envelopes and invokes IPC.
- Main validates payloads again and performs privileged work.
- Responses return as
DesktopResult<T>.
Security defaults:
contextIsolation: truesandbox: truenodeIntegration: false
Renderer routes include:
- Home and diagnostics/lab flows (
/,/ipc-diagnostics,/telemetry-console,/auth-session-lab) - UI system showcases (
/material-showcase,/carbon-showcase,/tailwind-showcase,/material-carbon-lab) - Data/form/workbench flows (
/data-table-workbench,/form-validation-lab,/async-validation-lab) - File/storage/API/update tooling (
/file-tools,/file-workflow-studio,/storage-explorer,/api-playground,/updates-release)
Desktop API surface includes:
desktop.app.*(version/runtime diagnostics)desktop.auth.*(sign-in/out, session summary, token diagnostics)desktop.dialog.openFile()+desktop.fs.readTextFile()desktop.storage.*desktop.api.invoke()desktop.updates.check()desktop.telemetry.track()desktop.python.*(local Python sidecar probe/inspect/stop for privileged helper workflows)
- Renderer must never access Node/Electron APIs directly.
- All privileged IPC calls must be validated in preload and main.
- Error responses should use typed envelopes with correlation IDs.
- Auth tokens stay out of renderer; bearer handling occurs in main process.
- Frontend should use Angular v21 patterns by default unless explicitly agreed otherwise.
- Node.js
^24.13.0 - pnpm
^10.14.0 - Python
3.11+(required for Python sidecar lab/tests)
pnpm install
pnpm exec playwright install chromiumNotes:
- Playwright browser binaries are local environment artifacts, not tracked in git.
- E2E is configured to run against a clean test server on
http://localhost:4300. - On Windows, if you see
Keytar unavailablewarnings, runpnpm native:rebuild:keytar.
Desktop development (Windows):
pnpm desktop:dev:winRenderer-only development:
pnpm renderer:servepnpm lint
pnpm unit-test
pnpm nx run desktop-main:test-python
pnpm integration-test
pnpm e2e-smoke
pnpm a11y-e2e
pnpm i18n-check
pnpm build
pnpm ci:localpnpm forge:make
pnpm forge:make:staging
pnpm forge:make:productionFlavor behavior:
forge:make:staging- sets
APP_ENV=staging - enables packaged DevTools (
DESKTOP_ENABLE_DEVTOOLS=1) - builds renderer in
stagingmode so Labs routes remain available for verification
- sets
forge:make:production- sets
APP_ENV=production - disables packaged DevTools (
DESKTOP_ENABLE_DEVTOOLS=0)
- sets
Primary setup path:
- Start the app with
pnpm desktop:dev:win. - Open
Settings > Auth. - Enter OIDC values and save.
- Optionally import
examples/config/runtime-config.auth.example.json.
Required Auth settings:
issuerclientIdredirectUri(loopback, example:http://127.0.0.1:42813/callback)scopes(must includeopenid)
Optional Auth settings:
audienceallowedSignOutOriginssendAudienceInAuthorizeapiBearerTokenSource
Token persistence behavior:
- Windows preference order:
keytar-> encrypted file store (ElectronsafeStorage) -> plaintext file store only whenOIDC_ALLOW_INSECURE_TOKEN_STORAGE=1. - If
keytarnative binding is missing, run:pnpm native:rebuild:keytar
Packaged builds read JSON runtime configuration only.
Instead, place one runtime config file at:
%APPDATA%\Angulectron\config\runtime-config.json
Supported shapes:
- flat allowlisted env-style keys (for compatibility), or
- nested feature config shape (
app,auth,api) used by Settings.
Example runtime-config.json (nested):
{
"version": 1,
"app": {},
"auth": {
"issuer": "https://your-issuer.example.com",
"clientId": "your-client-id",
"redirectUri": "http://127.0.0.1:42813/callback",
"scopes": "openid profile email offline_access",
"audience": "api.your-domain.example"
},
"api": {
"secureEndpointUrlTemplate": "https://api.your-domain.example/resources/{{resource_id}}",
"secureEndpointClaimMap": {
"resource_id": "sub"
}
}
}Notes:
- Restart the app after changing runtime config files.
- Process environment variables still take precedence over file values if both are set.
- Do not put client secrets into renderer or checked-in files; this flow assumes desktop public-client PKCE.
- In desktop runtime, use the
Settingspage for guided per-feature and full-config import/export flows (App,Auth, andAPIsettings). - Example import files are provided under
examples/config/.
The call.secure-endpoint API operation is endpoint-configurable and does not rely on a hardcoded private URL.
Set in Settings > API (or import examples/config/runtime-config.api.example.json):
secureEndpointUrlTemplatesecureEndpointClaimMap(optional map of placeholder -> JWT claim path)
Requirements:
- Must be
https://. - Placeholder values can come from request params and/or mapped JWT claims.
- Endpoint should accept bearer JWT from the desktop OIDC flow.
Examples:
secureEndpointUrlTemplate=https://your-api.example.com/resources/{{resource_id}}secureEndpointClaimMap={"resource_id":"sub","tenant_id":"org.id"}
If not configured, calling call.secure-endpoint returns a typed API/OPERATION_NOT_CONFIGURED failure.
This workspace supports a local Python helper backend model for privileged desktop capabilities (for example, file parsing libraries such as PyMuPDF).
Current status:
- Implemented as a lab capability (
Python Sidecar Labroute). - Runs a local sidecar HTTP service bound to loopback (
127.0.0.1). - Main process remains the security policy enforcement point.
Pattern:
- Renderer selects a file via typed desktop dialog API.
- Renderer receives a short-lived file token, not a raw path.
- Preload/main validates contract envelopes.
- Main resolves token (window-scoped + expiring), validates file extension and magic header, then calls Python sidecar.
- Main returns safe diagnostics/results to renderer.
Security properties:
- Renderer cannot pass arbitrary filesystem paths for privileged parsing.
- File ingress is fail-closed on extension/signature mismatch.
- Helper runtime is local-only and not a renderer-controlled authority.
How to run/verify:
- Open
Python Sidecar Lab. Probe Sidecarto start/diagnose local runtime.Select PDFthenInspect Selected PDFto verify end-to-end file handoff.- Run test gate:
pnpm nx run desktop-main:test-python
Deterministic packaged runtime (staging/production):
- Runtime source is pinned via official artifact catalog:
tools/python-runtime-artifacts.json
- Prepare runtime payload from pinned artifact:
pnpm run python-runtime:prepare-local
- Prepared payload is written to:
build/python-runtime/<platform>-<arch>/- example:
build/python-runtime/win32-x64/
- Pin sidecar dependencies in:
apps/desktop-main/python-sidecar/requirements-runtime.txt
- Run validation:
pnpm run python-runtime:assert- assertion verifies interpreter exists, source policy is
official-artifactby default, and importsfitzwhen PyMuPDF is declared in manifest
- Optional local emergency override:
PYTHON_RUNTIME_SOURCE_DIR=...to provide a local source directoryPYTHON_RUNTIME_ALLOW_UNOFFICIAL_SOURCE=1if assertion must permit non-official source manifests
- Runtime payload is copied into desktop build artifacts by:
pnpm run build-desktop-mainpnpm run forge:make:stagingpnpm run forge:make:production
- Staging/production package commands fail fast if runtime bundle is missing or invalid.
- Runtime diagnostics include
pythonExecutableso packaged builds can prove the sidecar path at runtime.
How to extend for new Python-backed operations:
- Add a new typed channel/contract in
libs/shared/contracts. - Add preload API binding in
apps/desktop-preload. - Add main handler validation and token/scope checks in
apps/desktop-main. - Add sidecar endpoint behavior in
apps/desktop-main/src/assets/python_sidecar/service.py. - Add/extend Python tests under
apps/desktop-main/python-sidecar/tests.
apps/runnable applicationslibs/reusable librariestools/scripts/utilitiesdocs/architecture, standards, delivery, governance
- Use Nx-driven commands (
pnpm nx ...or repo scripts wrapping Nx). - Respect app/lib boundaries and platform tags.
- Use short-lived branches and PR workflow; do not push directly to
main. - Keep changes minimal and behavior-preserving unless explicitly changing behavior.
- For security-sensitive changes, include review artifacts and negative-path tests.
Start here:
docs/docs-index.mddocs/03-engineering/onboarding-guide.md
Architecture:
docs/02-architecture/solution-architecture.mddocs/02-architecture/repo-topology-and-boundaries.mddocs/02-architecture/security-architecture.mddocs/02-architecture/ipc-contract-standard.md
Engineering:
docs/03-engineering/coding-standards.mddocs/03-engineering/testing-strategy.mddocs/03-engineering/reliability-and-error-handling.mddocs/03-engineering/observability-and-diagnostics.mddocs/03-engineering/security-review-workflow.md
Delivery + governance:
docs/04-delivery/ci-cd-spec.mddocs/04-delivery/release-management.mddocs/05-governance/definition-of-done.mddocs/05-governance/backlog.mddocs/05-governance/current-sprint.md
- No direct commits to
main. - Branch naming:
feat/*,fix/*,chore/*. - Merge by PR after required checks and approvals.
- Conventional Commits required.
Canonical policy:
docs/03-engineering/git-and-pr-policy.mdtools/templates/pr-body-template.md(local PR authoring template)