Cross-platform desktop standards
Criteria verified as of August 2026. Re-verify on the web before committing to anything (§8).
1. Scope and triggers
Applies to shipping a desktop application to more than one operating system from a single code
base: choosing the technology with its real cost, integrating with the system, packaging, signing
and notarising, distributing, updating and maintaining that for years. It also covers the prior question:
whether it really has to be a desktop app and not a web app.
Triggers: Electron's main.js/preload.js, BrowserWindow, contextIsolation, electron-builder,
electron-forge, electron-updater, tauri.conf.json, src-tauri/, WebView2, WKWebView,
WebKitGTK, Avalonia's .axaml, WinUI 3, Mac Catalyst, .pro/qmake/CMakeLists.txt with Qt6,
.ui, .qrc, windeployqt, macdeployqt, PySide6/PyQt, Flutter desktop, wxWidgets, GTK,
signtool, Authenticode, SmartScreen, codesign, hardened runtime, .entitlements, notarytool,
stapler, Gatekeeper, .dmg, .pkg, .msix, WiX, flatpak-builder and its manifest,
snapcraft.yaml, AppImage, system tray, native notification, global hotkey, launch at
login, file association, deep link with a custom scheme, UI Automation,
NSAccessibility, AT-SPI.
Guiding principle: a desktop app is justified by what the web cannot do. Frictionless access
to the user's filesystem, permanent presence (tray, global hotkey,
launch with the session), integration with other applications, hardware, working offline
as the normal state and not as a degradation. If the list of reasons is empty, the answer is a
web app — or an installable PWA, pwa-standards — and you save yourself signing, notarisation, an updater,
per-platform packaging and three distribution channels forever.
Second thesis, the most underestimated one: choosing Electron is signing a recurring security
commitment. You package Chromium, so you inherit its CVE calendar: every Chromium
update is yours, and an app that does not update is a complete attack surface distributed onto the
user's desk. That is not a maintenance detail: it is the main line of the app's operating
budget (§7.1).
Not applicable: see mobile-standards (native iOS and Android are theirs: SwiftUI, Compose, signing and
publishing on the App Store and Play, permissions and the mobile lifecycle. Boundary: if the same code
also compiles to mobile — Flutter, MAUI, Compose Multiplatform — the desktop part is decided
here and the mobile store part, there), pwa-standards (the installable web as a real alternative to this
skill: service worker, manifest, offline cache and update model are theirs. The
honest comparison: a PWA gives you no tray, no global hotkey, no launch at login, no full
filesystem access; a desktop app costs you signing, notarisation and an updater. That
is the trade-off, and neither skill should soften it), frontend-frameworks-standards and
frontend-web-platform-standards (the web UI that goes inside the webview is theirs — framework, render
model, CSP, bundle budget, build —; here the window, the process, the bridge to the system and
what gets shipped signed), typescript-standards, rust-standards, dotnet-standards,
dart-standards, cpp-standards, python-standards (the language, its build, its lint and its tests
are theirs; here the desktop app's architecture and its delivery),
accessibility-standards (WCAG and conformance are theirs; here the native accessibility
APIs — UI Automation, NSAccessibility, AT-SPI — and why an embedded webview meets them in a different
way), cryptography-pki-standards (algorithm choice and PKI management; here the concrete use
of code signing and of verification in the updater), secrets-management-standards
(custody and rotation of the private signing key; here the rule that it lives on an HSM/token and
that signing happens in an isolated CI step), cicd-standards (the pipeline that builds, signs and
publishes), vulnerability-management-standards (triage and SLA for the CVEs you inherit from Chromium or
Qt), appsec-standards (general threat modelling; here the concrete sinks of an embedded
webview), macos-fleet-standards and endpoint-security-standards (managed fleet deployment,
MDM and application control are theirs; here producing an artifact those systems
can accept), i18n-standards (localisation), webassembly-standards (Wasm inside the app).
2. Default decisions / Toolchain
Verify the latest version on the web before pinning it in a real project (§8).
The real cost of each option. There is no free option: the column that decides is the last one.
| Option |
Licence (read raw) |
UI engine |
What it really costs |
| Electron |
MIT |
Bundled Chromium |
Size (~100–200 MB per artifact) and RAM per process; support only for the 3 most recent stable majors, with a new major every 8 weeks → a permanent obligation to repackage for Chromium CVEs |
| Tauri 2.x |
Apache-2.0 OR MIT (workspace Cargo.toml) |
System webview: WebView2 (Windows), WKWebView (macOS), WebKitGTK 4.1 (Linux) |
A small artifact, but engine fragmentation: your app behaves differently per OS and per distro; WebKitGTK is the weak link. It requires Rust on the team |
| Qt 6 |
Triple: commercial, LGPLv3, and GPLv3 for certain modules |
Its own native one |
The licence (§2.1). Mature widgets, excellent accessibility and performance; the curve and cost of C++ or of the Python binding |
| Avalonia |
MIT (licence.md) |
Its own renderer (Skia) |
.NET everywhere including Linux; a smaller ecosystem than WPF and visually less "native" |
| .NET MAUI |
MIT |
Native per platform |
It does not support Linux: the official docs list Android, iOS, Mac Catalyst and Windows (WinUI 3), plus Tizen from Samsung. If you need Linux, MAUI is ruled out from the start |
| Flutter desktop |
BSD-3-Clause |
Its own renderer |
Windows/macOS/Linux supported; the feel is not native and desktop integration depends on plugins of uneven quality. Language: dart-standards |
| wxWidgets |
wxWindows Library Licence 3.1 (LGPL2+ with a linking exception) |
Real native widgets |
An old API; the licence exception allows distributing binaries under your own terms, which is its advantage over Qt |
| GTK 4 |
LGPL-2.1+ |
Native GNOME |
Excellent on Linux, second-rate on Windows and macOS. Choosing it for cross-platform work is almost always a mistake |
| Native per platform |
— |
WinUI/AppKit/GTK |
Three code bases. The best experience and the highest cost; it is justified in niche professional apps where integration is everything |
Honest choice criteria, by application type:
- Internal company app, web team, short deadline → Electron, with the update budget
accepted in writing.
- A small tool, size- or consumption-sensitive, a team with Rust → Tauri, accepting the
per-webview-engine test matrix.
- A long-lived app, with a lot of UI, performance, large tables or demanding accessibility →
Qt (or native). It is the option with the best accessibility and the worst licence cost.
- A .NET shop that needs Linux → Avalonia. A .NET shop that does not → WPF/WinUI
and it is no longer cross-platform.
- An app that already exists on mobile with Flutter → Flutter desktop, knowing that the desktop
integration part is one you will write yourself.
- A system utility, an administration tool, something with barely any UI → a CLI. Half of
the desktop apps proposed are a CLI with a window on top.
2.1 Qt: the licence is the decision, not the detail
Qt is offered under a commercial licence, LGPLv3 and GPLv3 depending on the module. The official
documentation puts it like this: the commercial one is "appropriate for development of proprietary/commercial software
where you do not want to share any source code", and there are modules that "are not available under LGPL
v3, but under GPL" — as of Aug 2026 the list includes Qt Quick 3D, Qt MQTT, Qt Virtual Keyboard and Qt
Wayland Compositor, among others (the list changes between versions: verify it, §8). Using one of
those modules in a closed product turns your app into GPL or forces you to buy a licence.
The classic trap is static linking. Under LGPL, Qt's FAQ is explicit: "Dynamic linking
is usually recommended here" and "The user of your application has to be able to re-link your
application against a different or modified version of the Qt library"; with LGPLv3 it adds that
"the user needs to be able to run the re-linked binary on its intended target device". Translated into
engineering terms:
- Dynamic linking of Qt and touch nothing else: it is the path without surprises.
- Static linking under LGPL obliges you to ship the objects or the material needed to
re-link your binary with a different version of Qt. It is possible, but it is a permanent
delivery commitment, and almost nobody who does it honours it.
- LGPLv3 forbids tivoisation: if your app goes onto a locked device that prevents replacing the
library, LGPLv3 is no use to you. There, only a commercial licence works.
- Mixing is forbidden: Qt's own website says that "Combining or mixing the Commercial Qt
licensing and the Qt Community Edition within the same application or device development project is
not allowed".
- The bindings have their own, different licences: PySide6 is LGPL, PyQt is GPL or a
Riverbank commercial licence. Choosing PyQt in a closed product without buying a licence is the most repeated
infringement in the Python ecosystem.
2.2 Electron: the calendar is the contract
Electron publishes a major every 8 weeks, aligned with Chromium's 4-week cycle, and
"the latest three stable major versions are supported by the Electron team". As of Aug 2026 the
official calendar gave (verify §8): 41 → Chromium M146, 42 → M148, 43 → M150, with 44
(M152) planned for 25-Aug-2026. Consequences that go into the project plan, not the backlog:
- You are at most ~16 weeks from running out of support. Your app's cadence is
Electron's: budget for a maintenance release every 8 weeks, without exception.
- Without a working automatic updater you cannot meet this. The updater is not a
feature: it is your product's security patching mechanism (§5.3).
- If the 8-week cycle does not fit your organisation (validation, certification, a regulated environment),
Electron is the wrong option and that must be said before starting, not afterwards.
3. Structure and conventions
3.1 Process and bridge separation
In any webview technology the model is the same: a privileged process (main/backend) and
a UI process (renderer/webview) that is treated as untrusted. The bridge between the two is an explicit
and minimal API, never generic access.
- Electron:
contextIsolation: true, nodeIntegration: false, sandbox: true in every
renderer. The preload exposes specific functions through contextBridge, never raw
ipcRenderer. Electron's own documentation says it: "It is paramount that you do not enable
Node.js integration in any renderer that loads remote content" and "Do not expose Electron APIs to
untrusted web content".
- Tauri: an explicit permission and capability list in the configuration; the exposed command validates
its arguments as if they came from the network, because they effectively can.
- Common rule: the UI process never receives an arbitrary file path or a system
command. The backend decides which operation exists; the UI only invokes it.
3.2 Integration with the system, which is the app's reason to exist
Every integration point is platform-specific and you have to decide what is supported on each
one, not discover it in production:
- Notifications: the native API (Windows Toast with a registered AUMID,
UNUserNotificationCenter on
macOS, org.freedesktop.Notifications on Linux). On macOS they require a signed app and the user's
permission; if it is not signed, they do not arrive.
- Tray / status area: on Windows and macOS it is stable; on Linux it is a minefield
(GNOME requires an extension for
AppIndicator). If the app depends on the tray to work,
the design is wrong: the tray is one entry point, not the only one.
- Global hotkeys: they are a shared system resource; a silent conflict with other apps
is the norm. Always configurable, with failure detection on registration. On Wayland, global
registration is restricted: it is done through a portal, and it may not be available.
- Launch at login:
LaunchAgents/SMAppService on macOS, the Run key or a Scheduled
Task on Windows, a .desktop file in ~/.config/autostart on Linux. Always optional, always
disableable from the app itself, and off by default unless the product is resident.
- File associations and custom schemes (
myapp://): they are attack surface. A
custom scheme allows any web page to invoke your app with parameters; that handler validates
and rejects as if it were a public endpoint. Files opened through an association are parsed with the
same distrust.
- Clipboard: reading it continuously is a data leak (password managers). It is read
under an explicit user action, never on a timer.
- The user's filesystem is a responsibility, not a convenience. Hard rules:
atomic writes (a temporary file on the same volume +
rename), never deleting what you did
not create, respecting the system paths (%APPDATA%, ~/Library/Application Support,
XDG on Linux) instead of inventing directories in $HOME, and uninstalling without leaving remains except
for the user's data, which you ask about.
3.3 Per-platform packaging
| Platform |
Default format |
Alternative |
Note |
| Windows |
MSI or MSIX for enterprise |
NSIS/Squirrel for consumer |
Enterprise needs unattended installation and deployment through GPO/Intune; a per-user installer without MSI makes it impossible for the desktop team |
| macOS |
A signed and notarised .dmg |
.pkg if system components have to be installed |
A universal binary (arm64 + x86_64) or two artifacts, decided explicitly |
| Linux |
Flatpak |
AppImage for "download and run", .deb/.rpm for a managed fleet |
Snap only if the target is Ubuntu and you accept its single store |
Real isolation differences on Linux, which are not cosmetic:
- Flatpak: a real sandbox by default. The official documentation describes the initial state as
"no access to any host files except the runtime, the app,
~/.var/app/$FLATPAK_ID …", "no
access to the network", "no access to any device nodes", "limited syscalls". Access is requested
through portals (file chooser, notifications), which grant implicit permission through a user
action. --filesystem=home cancels almost all the advantage: if your manifest carries it, the sandbox
is decorative. Use it as a review signal.
- Snap: strict confinement with declared interfaces, or classic, which does not confine and
requires manual store approval. If your snap is classic, do not sell isolation (verify
the current wording of the docs, §8).
- AppImage: there is no sandbox, none. It is a portable binary with its dependencies. It is convenient
for distribution and it provides no security guarantee whatsoever; saying otherwise is lying to the
user.
4. Quality and testing
Gates in order of increasing cost:
- Language lint and tests (delegated to the language skill) + dependency audit
on every build.
- Process boundary tests: every command exposed by the bridge has tests with invalid
and malicious inputs (paths with
.., absolute paths, shell symbols, absurd sizes).
- E2E of the packaged app, not of the source code: Playwright with the Electron driver,
WebdriverIO, or the technology's native runner. Testing the app unpackaged leaves out
exactly the failures that only appear when packaged (resource paths, signing, permissions).
- A real platform matrix in CI: Windows, macOS (arm64 and x86_64 if you publish both) and at
least two different Linux environments. With Tauri, the matrix includes the WebKitGTK version, because
that is where it breaks.
- Installation, update and uninstallation testing as a test case, including the
update from version N-2. Migration of the user's data between versions is
tested with real data from the old version, not with new data.
- Measured cold start and artifact size with a threshold that breaks the build. Without a threshold,
both grow monotonically.
- Accessibility: a complete run with the keyboard and with each platform's screen reader
(Narrator/NVDA, VoiceOver, Orca). It is the gate almost nobody puts in and the one that avoids the most problems.
5. Stack security
5.1 The embedded webview
Apply Electron's official checklist in full (equivalent in Tauri): HTTPS content only,
contextIsolation on, nodeIntegration off, sandbox on, a defined CSP,
webSecurity never disabled, allowRunningInsecureContent off, an explicit handler for
permission requests, and shell.openExternal never with untrusted content — the docs themselves
warn that "improper use of openExternal can be leveraged to compromise the user's host".
An additional rule that does not appear in the checklists: do not load a remote URL as the application's UI.
If the content comes from the server, any XSS on your website turns into execution with the
desktop app's privileges. The UI is packaged; the remote content goes into an isolated webview with no
bridge, or into the user's browser.
5.2 Code signing
- Windows: Authenticode signing with a timestamp (without one, the binary stops validating
when the certificate expires). Since 1 June 2023, the CA/Browser Forum's Baseline Requirements
require the subscriber's private key to be "generated, stored, and used in a
suitable Hardware Crypto Module" — that is, a token/HSM is mandatory, which makes it impossible
to put the
.pfx into a CI secret and forces you to sign with a cloud signing service or a
runner with access to the HSM. (Current version of the BRs as of Aug 2026: 3.11.0, verify §8.)
SmartScreen is reputation, not signing: a new OV certificate drags warnings along until it accumulates
downloads; EV avoids them from the start. Budget for it.
- Windows, kernel: if the product includes a kernel-mode driver, it is a different world. Since
Windows 10 version 1607, "Windows will not load any new kernel-mode drivers which are not
signed by the Dev Portal", with limited exceptions (a machine upgraded from an earlier version,
Secure Boot disabled, or an end-entity certificate issued before 29 July
2015 chained to a supported cross-signed CA). Translation: cross-signing is no longer a
route; you have to register on the Hardware Dev Center — which requires an EV certificate — and sign
through the portal (attestation or HLK).
- macOS: signing with the hardened runtime and minimal entitlements, mandatory notarisation and
stapler so that it works offline. Gatekeeper, according to Apple, "verifies that the software
is from an identified developer, is notarized by Apple to be free of known malicious content, and
hasn't been altered". Without notarisation, the app does not open through the normal route. Every entitlement
you request (com.apple.security.cs.allow-unsigned-executable-memory,
disable-library-validation) disables a protection: it is justified one by one or it does not go in.
- Linux: repository signing (
.deb/.rpm), signing of the Flatpak bundle, and on AppImage
a detached signature published alongside the artifact — which almost nobody verifies, so the verification
has to be done by your own updater.
- The private key is never in the repository nor in a CI environment variable. A token/HSM or
a signing service; the signing step is an isolated job, with the smallest possible surface, and
audited. See
secrets-management-standards.
5.3 Automatic updates
An updater without signature verification is a backdoor with a feature's name. It is
literally a mechanism that downloads a binary and runs it with the user's privileges.
Non-negotiable requirements:
- HTTPS with certificate validation, and the artifact's and the manifest's signature verified by the
client against a key embedded in the app. Tauri imposes it by design: "Tauri's updater needs a
signature to verify that the update is from a trusted source. This cannot be disabled." That is the
bar for any other technology.
- The updater's signing key is different from the code-signing one and losing it is
terminal: Tauri's own docs say so — "if you lose this key you will NOT be able to publish new
updates to the users that have the app already installed". Custody and backup with the same rigour as
a root CA.
- Protection against downgrade: the client rejects versions lower than the installed one.
- Phased rollout with a stop switch, because a bad update is distributed to
the whole installed base within hours and it cannot be reverted from the server.
- No mandatory telemetry in order to update: the update channel is not an analytics
channel. See
privacy-engineering-standards.
5.4 Local data
Credentials and tokens go to the system store (DPAPI/Credential Manager, Keychain,
Secret Service/libsecret), never into a JSON file in %APPDATA%. And the honest warning: the
system store protects against another user of the machine, not against malware running as
the user themselves. Promising more than that is false.
6. Performance and operability
- An explicit budget measured in CI: installer size, installed size, RAM at rest
after 30 min with the app open, and time to an interactive window on cold start with
a cold disk. Any Electron consumption figure quoted without stating the version, the number of renderers
and the methodology is folklore: measure your own and set it as the threshold, do not quote one from a blog.
- A webview's real cost is not the binary, it is the process per window. Reducing windows and
renderers is worth more than optimising the bundle.
- Start-up: a visible window first, content after; lazy loading of everything non-critical.
- Desktop telemetry, if it exists: explicit and disableable consent, without
persistent identifiers unless a need is demonstrated, and with what is sent documented. The
useful minimum: version, platform, and the fraction of the installed base per version — without that data you do not
know how many people are still on a vulnerable version, which is the key operational metric.
- Errors and crash reports: symbolised, without PII, with bounded retention, and with a way to
disable them in managed deployments.
- A rotated and bounded local log at the platform's standard path, and accessible from the
app itself ("open log folder"): it is the difference between a solvable support ticket and
an eternal one.
7. Long-term sustainability
7.1 The recurring commitment
Before writing a line, someone signs up to this: the engine's update cadence (8 weeks
with Electron; with Tauri, whatever your users' systems update to, which you do not control),
renewal of the signing certificate with its 90-day reminder, the annual Apple
Developer Program fee without which you can no longer notarise — and therefore no longer publish —, and a matrix
of supported operating systems with a retirement date. A desktop app with no owner for these
four things ends up unable to publish the day something expires, usually in the middle of an incident.
7.2 Prohibitions
- ❌ FORBIDDEN to disable
contextIsolation or enable nodeIntegration in a renderer that loads
remote content.
- ❌ FORBIDDEN to disable
webSecurity, sandbox or TLS certificate validation "for
development" on a branch that can reach a release.
- ❌ FORBIDDEN to expose
ipcRenderer or a generic execution API to the webview: the surface is
a list of specific and validated commands.
- ❌ FORBIDDEN to load the main UI from a remote URL.
- ❌ FORBIDDEN to distribute unsigned on Windows and macOS, and forbidden to publish on macOS without
notarising and without
stapler.
- ❌ FORBIDDEN to store the signing certificate or its password in the repository, in the CI
image or in an environment variable; and forbidden to sign without a timestamp.
- ❌ FORBIDDEN an updater that does not verify the artifact's signature, or that accepts versions
lower than the installed one.
- ❌ FORBIDDEN to stay on an unsupported Electron version (outside the three current
majors) in a distributed product.
- ❌ FORBIDDEN to link Qt statically under LGPL without meeting — and documenting — the re-linking
obligation; and forbidden to use a GPL-only Qt module in a closed product without a commercial licence.
- ❌ FORBIDDEN to use PyQt in a closed product without a Riverbank licence (PySide6 is the LGPL route).
- ❌ FORBIDDEN
--filesystem=home in a Flatpak manifest without reviewed justification, and
forbidden to sell an AppImage or a classic snap as "isolated".
- ❌ FORBIDDEN to store credentials outside the system store.
- ❌ FORBIDDEN to write to the user's filesystem non-atomically, outside the
platform's standard paths, or to delete files the app did not create.
- ❌ FORBIDDEN to read the clipboard continuously or in the background.
- ❌ FORBIDDEN to register a
myapp:// scheme without treating its parameters as hostile input.
- ❌ FORBIDDEN telemetry enabled by default without consent, or an update conditional on
accepting it.
- ❌ FORBIDDEN to ship without a complete keyboard run and a screen-reader test.
- ❌ FORBIDDEN to quote "typical" Electron RAM consumption or sizes without your own measurement (§6).
8. Mandatory web verification
- Electron:
releases.electronjs.org/schedule — which majors are within the three supported
today, their EOL dates and the associated Chromium version. As of Aug 2026: 41/42/43 (M146/M148/M150),
with 44 (M152) planned for 25-Aug-2026.
- Tauri: current version (2.11.5 as of Aug 2026), the workspace's minimum
rust-version, webview
engines and minimum versions per platform, and the state of WebKitGTK in the target distros.
- Qt: the current list of GPL-only modules at
doc.qt.io/qt-6/licensing.html — it changes between
versions — and the terms of the current commercial licence. Read the licence of the specific
binding (PySide6 vs. PyQt) before choosing it.
- Raw licences (
LICENSE, LICENCE, licence.md, COPYING, watch out for master vs. main)
of any UI framework you link against. Verified for this document: Tauri
Apache-2.0 OR MIT, Avalonia MIT, wxWidgets wxWindows Library Licence 3.1.
- Windows code signing: the current version of the CA/Browser Forum's Baseline Requirements
(3.11.0, effective 16-Jun-2026, as of Aug 2026) and the hardware module requirements; the current offering of
cloud signing services compatible with CI.
- macOS: the current notarisation requirements, the current tool (
notarytool; altool is
retired) and Gatekeeper or entitlement changes in the latest macOS version.
- Linux: the current wording of Snap's confinement modes (strict/classic/devmode) and
of the classic approval process — this document could not verify it in the official
source (the Snapcraft docs did not respond); treat it as pending. The state of Flatpak's portals
for global hotkeys and autostart under Wayland.
- .NET MAUI / Avalonia / Flutter: supported platforms and minimum versions in the official docs —
as of Aug 2026 MAUI does not list Linux.
- CVEs: of Chromium (if Electron), of WebKitGTK and WebView2 (if Tauri), of Qt (if Qt). It is a
continuous flow, not a one-off check →
vulnerability-management-standards.
If the web contradicts this document, the web wins — flag the discrepancy.
1---2name: cross-platform-desktop-standards3description: Shipping a desktop application to Windows, macOS and Linux from one codebase, and paying its real cost. Use when choosing between Electron (main.js, preload.js, BrowserWindow, contextIsolation, electron-builder, electron-forge, electron-updater, Squirrel), Tauri (tauri.conf.json, src-tauri, WebView2 / WKWebView / WebKitGTK, the Tauri updater and its signing key), Qt / PySide6 / PyQt (.pro, CMake with Qt6, .ui and .qrc files, qmake, windeployqt / macdeployqt, and the commercial-versus-LGPLv3-versus-GPLv3 licence choice and the relinking obligation), .NET MAUI or Avalonia (.axaml, WinUI 3, Mac Catalyst), Flutter desktop, wxWidgets or GTK, integrating with the OS (tray icon, native notifications, global hotkeys, launch at login, file associations, deep links, clipboard, the user's filesystem), packaging and signing (Authenticode signtool, an EV/OV code-signing certificate on an HSM or token, SmartScreen reputation, macOS codesign with hardened runtime, entitlements, notarytool and stapler, Gatekeeper, .dmg and .4---56# Cross-platform desktop standards78Criteria verified as of **August 2026**. Re-verify on the web before committing to anything (§8).910## 1. Scope and triggers1112Applies to **shipping a desktop application to more than one operating system from a single code13base**: choosing the technology with its real cost, integrating with the system, packaging, **signing14and notarising**, distributing, updating and maintaining that for years. It also covers the prior question:15**whether it really has to be a desktop app and not a web app**.1617Triggers: Electron's `main.js`/`preload.js`, `BrowserWindow`, `contextIsolation`, `electron-builder`,18`electron-forge`, `electron-updater`, `tauri.conf.json`, `src-tauri/`, WebView2, WKWebView,19WebKitGTK, Avalonia's `.axaml`, WinUI 3, Mac Catalyst, `.pro`/`qmake`/`CMakeLists.txt` with Qt6,20`.ui`, `.qrc`, `windeployqt`, `macdeployqt`, PySide6/PyQt, Flutter desktop, wxWidgets, GTK,21`signtool`, Authenticode, SmartScreen, `codesign`, *hardened runtime*, `.entitlements`, `notarytool`,22`stapler`, Gatekeeper, `.dmg`, `.pkg`, `.msix`, WiX, `flatpak-builder` and its manifest,23`snapcraft.yaml`, AppImage, system tray, native notification, global hotkey, launch at24login, file association, *deep link* with a custom scheme, UI Automation,25NSAccessibility, AT-SPI.2627**Guiding principle**: **a desktop app is justified by what the web cannot do.** Frictionless access28to the user's filesystem, permanent presence (tray, global hotkey,29launch with the session), integration with other applications, hardware, working offline30as the normal state and not as a degradation. **If the list of reasons is empty, the answer is a31web app** — or an installable PWA, `pwa-standards` — and you save yourself signing, notarisation, an updater,32per-platform packaging and three distribution channels forever.3334**Second thesis, the most underestimated one**: **choosing Electron is signing a recurring security35commitment.** You package Chromium, so **you inherit its CVE calendar**: every Chromium36update is yours, and an app that does not update is a complete attack surface distributed onto the37user's desk. That is not a maintenance detail: it is the main line of the app's operating38budget (§7.1).3940**Not applicable**: see `mobile-standards` (**native iOS and Android are theirs**: SwiftUI, Compose, signing and41publishing on the App Store and Play, permissions and the mobile lifecycle. **Boundary: if the same code42also compiles to mobile — Flutter, MAUI, Compose Multiplatform — the desktop part is decided43here and the mobile store part, there**), `pwa-standards` (**the installable web as a real alternative to this44skill**: *service worker*, manifest, *offline* cache and update model are theirs. The45honest comparison: a PWA gives you no tray, no global hotkey, no launch at login, no full46filesystem access; a desktop app costs you signing, notarisation and an updater. That47is the trade-off, and neither skill should soften it), `frontend-frameworks-standards` and48`frontend-web-platform-standards` (**the web UI that goes inside the webview is theirs** — framework, render49model, CSP, bundle budget, build —; **here the window, the process, the bridge to the system and50what gets shipped signed**), `typescript-standards`, `rust-standards`, `dotnet-standards`,51`dart-standards`, `cpp-standards`, `python-standards` (**the language, its build, its lint and its tests52are theirs**; here the desktop app's architecture and its delivery),53`accessibility-standards` (**WCAG and conformance are theirs**; here the native accessibility54APIs — UI Automation, NSAccessibility, AT-SPI — and why an embedded webview meets them in a different55way), `cryptography-pki-standards` (algorithm choice and PKI management; here the concrete use56of code signing and of verification in the updater), `secrets-management-standards`57(**custody and rotation of the private signing key**; here the rule that it lives on an HSM/token and58that signing happens in an isolated CI step), `cicd-standards` (the pipeline that builds, signs and59publishes), `vulnerability-management-standards` (triage and SLA for the CVEs you inherit from Chromium or60Qt), `appsec-standards` (general threat modelling; here the concrete *sinks* of an embedded61webview), `macos-fleet-standards` and `endpoint-security-standards` (**managed fleet deployment,62MDM and application control are theirs**; here producing an artifact those systems63can accept), `i18n-standards` (localisation), `webassembly-standards` (Wasm inside the app).6465## 2. Default decisions / Toolchain6667> Verify the latest version on the web before pinning it in a real project (§8).6869The real cost of each option. **There is no free option**: the column that decides is the last one.7071| Option | Licence (read raw) | UI engine | What it really costs |72|---|---|---|---|73| **Electron** | MIT | **Bundled** Chromium | Size (~100–200 MB per artifact) and RAM per process; **support only for the 3 most recent stable majors**, with a new major every 8 weeks → **a permanent obligation to repackage for Chromium CVEs** |74| **Tauri 2.x** | `Apache-2.0 OR MIT` (workspace `Cargo.toml`) | **System webview**: WebView2 (Windows), WKWebView (macOS), **WebKitGTK 4.1** (Linux) | A small artifact, but **engine fragmentation**: your app behaves differently per OS and per distro; WebKitGTK is the weak link. It requires Rust on the team |75| **Qt 6** | **Triple**: commercial, **LGPLv3**, and **GPLv3 for certain modules** | Its own native one | The licence (§2.1). Mature widgets, excellent accessibility and performance; the curve and cost of C++ or of the Python *binding* |76| **Avalonia** | MIT (`licence.md`) | Its own renderer (Skia) | .NET everywhere **including Linux**; a smaller ecosystem than WPF and visually less "native" |77| **.NET MAUI** | MIT | Native per platform | **It does not support Linux**: the official docs list Android, iOS, **Mac Catalyst** and Windows (WinUI 3), plus Tizen from Samsung. If you need Linux, MAUI is ruled out from the start |78| **Flutter desktop** | BSD-3-Clause | Its own renderer | Windows/macOS/Linux supported; **the feel is not native** and desktop integration depends on *plugins* of uneven quality. Language: `dart-standards` |79| **wxWidgets** | **wxWindows Library Licence 3.1** (LGPL2+ with a linking exception) | Real native widgets | An old API; the licence exception allows distributing binaries under your own terms, which is its advantage over Qt |80| **GTK 4** | LGPL-2.1+ | Native GNOME | Excellent on Linux, **second-rate on Windows and macOS**. Choosing it for cross-platform work is almost always a mistake |81| **Native per platform** | — | WinUI/AppKit/GTK | Three code bases. The best experience and the highest cost; it is justified in niche professional apps where integration is everything |8283**Honest choice criteria, by application type:**8485- **Internal company app, web team, short deadline** → **Electron**, with the update budget86 accepted in writing.87- **A small tool, size- or consumption-sensitive, a team with Rust** → **Tauri**, accepting the88 per-webview-engine test matrix.89- **A long-lived app, with a lot of UI, performance, large tables or demanding accessibility** →90 **Qt** (or native). It is the option with the best accessibility and the worst licence cost.91- **A .NET shop that needs Linux** → **Avalonia**. A .NET shop that does not → **WPF/WinUI**92 and it is no longer cross-platform.93- **An app that already exists on mobile with Flutter** → **Flutter desktop**, knowing that the desktop94 integration part is one you will write yourself.95- **A system utility, an administration tool, something with barely any UI** → **a CLI**. Half of96 the desktop apps proposed are a CLI with a window on top.9798### 2.1 Qt: the licence is the decision, not the detail99100Qt is offered under a **commercial licence**, **LGPLv3** and **GPLv3 depending on the module**. The official101documentation puts it like this: the commercial one is *"appropriate for development of proprietary/commercial software102where you do not want to share any source code"*, and **there are modules that "are not available under LGPL103v3, but under GPL"** — as of Aug 2026 the list includes Qt Quick 3D, Qt MQTT, Qt Virtual Keyboard and Qt104Wayland Compositor, among others (**the list changes between versions: verify it, §8**). Using one of105those modules in a closed product **turns your app into GPL or forces you to buy a licence**.106107The classic trap is **static linking**. Under LGPL, Qt's FAQ is explicit: *"Dynamic linking108is usually recommended here"* and *"The user of your application has to be able to re-link your109application against a different or modified version of the Qt library"*; with LGPLv3 it adds that110*"the user needs to be able to run the re-linked binary on its intended target device"*. Translated into111engineering terms:112113- **Dynamic linking** of Qt and touch nothing else: it is the path without surprises.114- **Static linking under LGPL** obliges you to ship the objects or the material needed to115 **re-link** your binary with a different version of Qt. It is possible, but it is a permanent116 delivery commitment, and almost nobody who does it honours it.117- **LGPLv3 forbids tivoisation**: if your app goes onto a locked device that prevents replacing the118 library, LGPLv3 is no use to you. There, only a commercial licence works.119- **Mixing is forbidden**: Qt's own website says that *"Combining or mixing the Commercial Qt120 licensing and the Qt Community Edition within the same application or device development project is121 not allowed"*.122- The *bindings* have their own, different licences: **PySide6 is LGPL**, **PyQt is GPL or a123 Riverbank commercial licence**. Choosing PyQt in a closed product without buying a licence is the most repeated124 infringement in the Python ecosystem.125126### 2.2 Electron: the calendar is the contract127128Electron publishes a major every **8 weeks**, aligned with Chromium's 4-week cycle, and129**"the latest three stable major versions are supported by the Electron team"**. As of Aug 2026 the130official calendar gave (verify §8): **41 → Chromium M146**, **42 → M148**, **43 → M150**, with 44131(M152) planned for 25-Aug-2026. Consequences that go into the project plan, not the backlog:132133- **You are at most ~16 weeks from running out of support.** Your app's cadence **is**134 Electron's: budget for a maintenance release every 8 weeks, without exception.135- **Without a working automatic updater you cannot meet this.** The updater is not a136 feature: it is your product's security patching mechanism (§5.3).137- If the 8-week cycle does not fit your organisation (validation, certification, a regulated environment),138 **Electron is the wrong option** and that must be said before starting, not afterwards.139140## 3. Structure and conventions141142### 3.1 Process and bridge separation143144In any webview technology the model is the same: **a privileged process (main/backend) and145a UI process (renderer/webview) that is treated as untrusted**. The bridge between the two is an **explicit146and minimal API**, never generic access.147148- Electron: `contextIsolation: true`, `nodeIntegration: false`, `sandbox: true` in every149 renderer. The `preload` exposes **specific functions** through `contextBridge`, never raw150 `ipcRenderer`. Electron's own documentation says it: *"It is paramount that you do not enable151 Node.js integration in any renderer that loads remote content"* and *"Do not expose Electron APIs to152 untrusted web content"*.153- Tauri: an **explicit permission and capability list** in the configuration; the exposed command validates154 its arguments as if they came from the network, because they effectively can.155- Common rule: **the UI process never receives an arbitrary file path or a system156 command.** The backend decides which operation exists; the UI only invokes it.157158### 3.2 Integration with the system, which is the app's reason to exist159160Every integration point is platform-specific and **you have to decide what is supported on each161one**, not discover it in production:162163- **Notifications**: the native API (Windows Toast with a registered AUMID, `UNUserNotificationCenter` on164 macOS, `org.freedesktop.Notifications` on Linux). On macOS they require a signed app and the user's165 permission; **if it is not signed, they do not arrive**.166- **Tray / status area**: on Windows and macOS it is stable; **on Linux it is a minefield**167 (GNOME requires an extension for `AppIndicator`). If the app *depends* on the tray to work,168 the design is wrong: the tray is one entry point, not the only one.169- **Global hotkeys**: they are a shared system resource; a **silent conflict** with other apps170 is the norm. Always configurable, with failure detection on registration. On Wayland, global171 registration is restricted: it is done through a portal, and it may not be available.172- **Launch at login**: `LaunchAgents`/`SMAppService` on macOS, the `Run` key or a Scheduled173 Task on Windows, a `.desktop` file in `~/.config/autostart` on Linux. **Always optional, always174 disableable from the app itself**, and off by default unless the product is resident.175- **File associations and custom schemes (`myapp://`)**: they are **attack surface**. A176 custom scheme allows any web page to invoke your app with parameters; that *handler* validates177 and rejects as if it were a public endpoint. Files opened through an association are parsed with the178 same distrust.179- **Clipboard**: reading it continuously is a data leak (password managers). It is read180 under an explicit user action, never on a timer.181- **The user's filesystem is a responsibility, not a convenience.** Hard rules:182 **atomic** writes (a temporary file on the same volume + `rename`), **never** deleting what you did183 not create, respecting the system paths (`%APPDATA%`, `~/Library/Application Support`,184 XDG on Linux) instead of inventing directories in `$HOME`, and **uninstalling without leaving remains** except185 for the user's data, which you ask about.186187### 3.3 Per-platform packaging188189| Platform | Default format | Alternative | Note |190|---|---|---|---|191| Windows | **MSI or MSIX** for enterprise | NSIS/Squirrel for consumer | Enterprise needs unattended installation and deployment through GPO/Intune; a per-user installer without MSI makes it impossible for the desktop team |192| macOS | **A signed and notarised `.dmg`** | `.pkg` if system components have to be installed | A universal binary (arm64 + x86_64) or two artifacts, decided explicitly |193| Linux | **Flatpak** | AppImage for "download and run", `.deb`/`.rpm` for a managed fleet | Snap only if the target is Ubuntu and you accept its single store |194195**Real isolation differences on Linux, which are not cosmetic:**196197- **Flatpak**: a real *sandbox* by default. The official documentation describes the initial state as198 *"no access to any host files except the runtime, the app, `~/.var/app/$FLATPAK_ID` …"*, *"no199 access to the network"*, *"no access to any device nodes"*, *"limited syscalls"*. Access is requested200 through **portals** (file chooser, notifications), which grant implicit permission through a user201 action. **`--filesystem=home` cancels almost all the advantage**: if your manifest carries it, the sandbox202 is decorative. Use it as a review signal.203- **Snap**: **strict** confinement with declared interfaces, or **classic**, which **does not confine** and204 requires manual store approval. If your snap is *classic*, do not sell isolation (verify205 the current wording of the docs, §8).206- **AppImage**: **there is no sandbox, none**. It is a portable binary with its dependencies. It is convenient207 for distribution and **it provides no security guarantee whatsoever**; saying otherwise is lying to the208 user.209210## 4. Quality and testing211212Gates in order of increasing cost:2132141. **Language lint and tests** (delegated to the language skill) + **dependency audit**215 on every build.2162. **Process boundary tests**: every command exposed by the bridge has tests with invalid217 and malicious inputs (paths with `..`, absolute paths, shell symbols, absurd sizes).2183. **E2E of the packaged app, not of the source code**: Playwright with the Electron driver,219 WebdriverIO, or the technology's native *runner*. Testing the app unpackaged leaves out220 exactly the failures that only appear when packaged (resource paths, signing, permissions).2214. **A real platform matrix in CI**: Windows, macOS (arm64 **and** x86_64 if you publish both) and at222 least two different Linux environments. With Tauri, the matrix includes the **WebKitGTK version**, because223 that is where it breaks.2245. **Installation, update and uninstallation testing** as a test case, including the225 **update from version N-2**. Migration of the user's data between versions is226 tested with real data from the old version, not with new data.2276. **Measured cold start** and artifact size **with a threshold that breaks the build**. Without a threshold,228 both grow monotonically.2297. **Accessibility**: a complete run with the keyboard and with each platform's screen reader230 (Narrator/NVDA, VoiceOver, Orca). It is the gate almost nobody puts in and the one that avoids the most problems.231232## 5. Stack security233234### 5.1 The embedded webview235236Apply Electron's official checklist in full (equivalent in Tauri): **HTTPS content only**,237`contextIsolation` on, `nodeIntegration` off, `sandbox` on, **a defined CSP**,238`webSecurity` **never** disabled, `allowRunningInsecureContent` off, an explicit handler for239permission requests, and **`shell.openExternal` never with untrusted content** — the docs themselves240warn that *"improper use of openExternal can be leveraged to compromise the user's host"*.241242**An additional rule that does not appear in the checklists: do not load a remote URL as the application's UI.**243If the content comes from the server, any XSS on your website turns into execution with the244desktop app's privileges. The UI is packaged; the remote content goes into an isolated `webview` with no245bridge, or into the user's browser.246247### 5.2 Code signing248249- **Windows**: Authenticode signing with a **timestamp** (without one, the binary stops validating250 when the certificate expires). Since **1 June 2023**, the CA/Browser Forum's *Baseline Requirements*251 require the subscriber's private key to be *"generated, stored, and used in a252 suitable Hardware Crypto Module"* — that is, **a token/HSM is mandatory**, which makes it impossible253 to put the `.pfx` into a CI secret and forces you to sign with a cloud signing service or a254 *runner* with access to the HSM. (Current version of the BRs as of Aug 2026: **3.11.0**, verify §8.)255 **SmartScreen** is reputation, not signing: a new OV certificate drags warnings along until it accumulates256 downloads; EV avoids them from the start. Budget for it.257- **Windows, kernel**: if the product includes a **kernel-mode driver**, it is a different world. Since258 **Windows 10 version 1607**, *"Windows will not load any new kernel-mode drivers which are not259 signed by the Dev Portal"*, with limited exceptions (a machine upgraded from an earlier version,260 **Secure Boot disabled**, or an end-entity certificate issued **before 29 July261 2015** chained to a supported cross-signed CA). Translation: **cross-signing is no longer a262 route**; you have to register on the Hardware Dev Center — which **requires an EV certificate** — and sign263 through the portal (attestation or HLK).264- **macOS**: signing with the **hardened runtime** and minimal *entitlements*, **mandatory notarisation** and265 **`stapler`** so that it works offline. Gatekeeper, according to Apple, *"verifies that the software266 is from an identified developer, is notarized by Apple to be free of known malicious content, and267 hasn't been altered"*. Without notarisation, the app **does not open** through the normal route. Every *entitlement*268 you request (`com.apple.security.cs.allow-unsigned-executable-memory`,269 `disable-library-validation`) disables a protection: it is justified one by one or it does not go in.270- **Linux**: repository signing (`.deb`/`.rpm`), signing of the Flatpak *bundle*, and on AppImage271 **a detached signature published alongside the artifact** — which almost nobody verifies, so the verification272 has to be done by your own updater.273- **The private key is never in the repository nor in a CI environment variable.** A token/HSM or274 a signing service; the signing step is an isolated *job*, with the smallest possible surface, and275 audited. See `secrets-management-standards`.276277### 5.3 Automatic updates278279**An updater without signature verification is a backdoor with a feature's name.** It is280literally a mechanism that downloads a binary and runs it with the user's privileges.281Non-negotiable requirements:282283- **HTTPS with certificate validation**, and **the artifact's and the manifest's signature verified by the284 client against a key embedded in the app**. Tauri imposes it by design: *"Tauri's updater needs a285 signature to verify that the update is from a trusted source. This cannot be disabled."* That is the286 bar for any other technology.287- **The updater's signing key is different from the code-signing one** and losing it is288 terminal: Tauri's own docs say so — *"if you lose this key you will NOT be able to publish new289 updates to the users that have the app already installed"*. Custody and backup with the same rigour as290 a root CA.291- **Protection against *downgrade***: the client rejects versions lower than the installed one.292- **Phased rollout with a stop switch**, because a bad update is distributed to293 the whole installed base within hours and **it cannot be reverted from the server**.294- **No mandatory telemetry in order to update**: the update channel is not an analytics295 channel. See `privacy-engineering-standards`.296297### 5.4 Local data298299Credentials and tokens go to the system store (**DPAPI/Credential Manager**, **Keychain**,300**Secret Service/`libsecret`**), never into a JSON file in `%APPDATA%`. And the honest warning: **the301system store protects against another user of the machine, not against malware running as302the user themselves.** Promising more than that is false.303304## 6. Performance and operability305306- **An explicit budget measured in CI**: installer size, installed size, RAM at rest307 after 30 min with the app open, and **time to an interactive window on cold start** with308 a cold disk. *Any* Electron consumption figure quoted without stating the version, the number of renderers309 and the methodology is folklore: **measure your own and set it as the threshold**, do not quote one from a blog.310- **A webview's real cost is not the binary, it is the process per window.** Reducing windows and311 renderers is worth more than optimising the bundle.312- **Start-up**: a visible window first, content after; lazy loading of everything non-critical.313- **Desktop telemetry, if it exists**: **explicit and disableable consent**, without314 persistent identifiers unless a need is demonstrated, and with what is sent documented. The315 useful minimum: version, platform, and **the fraction of the installed base per version** — without that data you do not316 know how many people are still on a vulnerable version, which is the key operational metric.317- **Errors and *crash reports***: symbolised, without PII, with bounded retention, and **with a way to318 disable them** in managed deployments.319- **A rotated and bounded local log** at the platform's standard path, and accessible from the320 app itself ("open log folder"): it is the difference between a solvable support ticket and321 an eternal one.322323## 7. Long-term sustainability324325### 7.1 The recurring commitment326327Before writing a line, someone signs up to this: **the engine's update cadence** (8 weeks328with Electron; with Tauri, whatever your users' systems update to, which **you do not control**),329**renewal of the signing certificate** with its 90-day reminder, the **annual Apple330Developer Program fee** without which you can no longer notarise — and therefore no longer publish —, and a **matrix331of supported operating systems with a retirement date**. A desktop app with no owner for these332four things ends up unable to publish the day something expires, usually in the middle of an incident.333334### 7.2 Prohibitions335336- ❌ **FORBIDDEN** to disable `contextIsolation` or enable `nodeIntegration` in a renderer that loads337 remote content.338- ❌ **FORBIDDEN** to disable `webSecurity`, `sandbox` or TLS certificate validation "for339 development" on a branch that can reach a *release*.340- ❌ **FORBIDDEN** to expose `ipcRenderer` or a generic execution API to the webview: the surface is341 a list of specific and validated commands.342- ❌ **FORBIDDEN** to load the main UI from a remote URL.343- ❌ **FORBIDDEN** to distribute unsigned on Windows and macOS, and **forbidden to publish on macOS without344 notarising and without `stapler`**.345- ❌ **FORBIDDEN** to store the signing certificate or its password in the repository, in the CI346 image or in an environment variable; and forbidden to sign without a timestamp.347- ❌ **FORBIDDEN** an updater that does not verify the artifact's signature, or that accepts versions348 lower than the installed one.349- ❌ **FORBIDDEN** to stay on an unsupported Electron version (outside the three current350 majors) in a distributed product.351- ❌ **FORBIDDEN** to link Qt statically under LGPL without meeting — and documenting — the re-linking352 obligation; and forbidden to use a GPL-only Qt module in a closed product without a commercial licence.353- ❌ **FORBIDDEN** to use PyQt in a closed product without a Riverbank licence (PySide6 is the LGPL route).354- ❌ **FORBIDDEN** `--filesystem=home` in a Flatpak manifest without reviewed justification, and355 forbidden to sell an AppImage or a *classic* snap as "isolated".356- ❌ **FORBIDDEN** to store credentials outside the system store.357- ❌ **FORBIDDEN** to write to the user's filesystem non-atomically, outside the358 platform's standard paths, or to delete files the app did not create.359- ❌ **FORBIDDEN** to read the clipboard continuously or in the background.360- ❌ **FORBIDDEN** to register a `myapp://` scheme without treating its parameters as hostile input.361- ❌ **FORBIDDEN** telemetry enabled by default without consent, or an update conditional on362 accepting it.363- ❌ **FORBIDDEN** to ship without a complete keyboard run and a screen-reader test.364- ❌ **FORBIDDEN** to quote "typical" Electron RAM consumption or sizes without your own measurement (§6).365366## 8. Mandatory web verification367368- **Electron**: `releases.electronjs.org/schedule` — which majors are within the three supported369 **today**, their EOL dates and the associated Chromium version. As of Aug 2026: 41/42/43 (M146/M148/M150),370 with 44 (M152) planned for 25-Aug-2026.371- **Tauri**: current version (2.11.5 as of Aug 2026), the workspace's minimum `rust-version`, webview372 engines and minimum versions per platform, and the state of WebKitGTK in the target distros.373- **Qt**: the **current list of GPL-only modules** at `doc.qt.io/qt-6/licensing.html` — it changes between374 versions — and the terms of the current commercial licence. **Read the licence of the specific375 *binding*** (PySide6 vs. PyQt) before choosing it.376- **Raw licences** (`LICENSE`, `LICENCE`, `licence.md`, `COPYING`, watch out for `master` vs. `main`)377 of any UI framework you link against. Verified for this document: Tauri378 `Apache-2.0 OR MIT`, Avalonia MIT, wxWidgets **wxWindows Library Licence 3.1**.379- **Windows code signing**: the current version of the CA/Browser Forum's *Baseline Requirements*380 (3.11.0, effective 16-Jun-2026, as of Aug 2026) and the hardware module requirements; the current offering of381 cloud signing services compatible with CI.382- **macOS**: the current notarisation requirements, the current tool (`notarytool`; `altool` is383 retired) and Gatekeeper or *entitlement* changes in the latest macOS version.384- **Linux**: the current wording of Snap's confinement modes (*strict*/*classic*/*devmode*) and385 of the *classic* approval process — **this document could not verify it in the official386 source** (the Snapcraft docs did not respond); treat it as pending. The state of Flatpak's portals387 for global hotkeys and autostart under Wayland.388- **.NET MAUI / Avalonia / Flutter**: supported platforms and minimum versions in the official docs —389 as of Aug 2026 **MAUI does not list Linux**.390- **CVEs**: of Chromium (if Electron), of WebKitGTK and WebView2 (if Tauri), of Qt (if Qt). It is a391 continuous flow, not a one-off check → `vulnerability-management-standards`.392393If the web contradicts this document, **the web wins** — flag the discrepancy.