| Mode | Who starts first | BEAM | Flags |
|---|---|---|---|
| Packaged | Native host | Spawned by host | default (no --edw-no-beam) |
| Development | Elixir / mix | Already running | --edw-no-beam |
| E2E tests | Elixir | Already running | --edw-no-beam --edw-test-rpc |
Native always binds TCP; Elixir always connects.
MyApp.app/
Contents/
MacOS/DesktopWebView # host binary
Resources/
DesktopWebView.ini # optional
beam/bin/<app> # release start script
beam/...
Info.plist # include mic/camera usage strings when needed
Relative paths in the ini are resolved from Contents/Resources/ when running
inside a bundle, otherwise from the directory containing the executable.
Default when the host runs as a normal Win32 process (installer or portable zip):
MyApp/
MyApp.exe # native host (package.name.exe or host_executable)
MyApp.ini # optional, beside the exe (<exe_basename>.ini)
beam/
bin/
my_app.bat # or my_app (escript/release)
...
- Installed host name defaults to
package.name+.exeon Windows host-first (override withpackage.host_executable). The source binary may still beDesktopWebView.exefromdesktop_webview/DESKTOP_HOST_BINARY. - Ini discovery:
--edw-config→<exe_basename>.inibeside the executable →DesktopWebView.inibeside the executable. - Relative
beam.path/working_dirresolve against the directory containing the host executable. - Forwarded argv and
EDW_PORT/EDW_HOSTare unchanged. - Release asset name:
DesktopWebView-windows-x64.exe(see Binaries). - WebView2: document Evergreen Runtime dependency in the app installer; the host should fail with a clear stderr message if the runtime is missing.
- Microphone / camera: declare app capabilities in the packaged manifest / privacy settings as required by the target Windows version; the hybrid permission RPC still applies on top.
Default flat layout (also suitable for AppImage / AppDir with the same relative paths):
MyApp/
DesktopWebView # host binary
DesktopWebView.ini # optional
beam/
bin/
my_app
...
- Ini discovery:
--edw-config→DesktopWebView.inibeside the executable. - Relative paths resolve against the executable’s directory.
- Release asset name:
DesktopWebView-linux-x86_64. - Prefer shipping against a documented WebKitGTK/GTK baseline (note distro
packages in
native/linux/README.md). Ubuntu 24.04 baseline: GTK 4.14 + WebKitGTK 2.52 (libgtk-4-dev,libwebkitgtk-6.0-dev). - Tray: StatusNotifierItem when available; current host keeps an in-memory tray for RPC conformance (AppIndicator is GTK 3 and not linked).
- Microphone / camera: PipeWire/Pulse + portal prompts may appear; hybrid permission policy still applies. Flatpak/snap portals need extra packaging notes when those formats are supported.
--edw-config=/path/to.ini<exe_basename>.inibeside the executable (e.g.dDrive.inifordDrive.exe)DesktopWebView.inibeside the executable- macOS only:
Contents/Resources/DesktopWebView.ini(app bundle)
[beam]
path = beam
app_name = my_app
args = start
working_dir = beam
enabled = true
[network]
host = 127.0.0.1
port = 0
[lifetime]
mode = reconnect
# multi (default) | single — packaged apps set single
# instances = single
# instance_id = ddrive
restart_beam = true
restart_max_attempts = 0
restart_backoff_ms = 500
recovery_after = 3
# recovery_script = recovery.exs
[env]
# Extra environment for the BEAM child
# FOO = barOne-shot CLI (--edw-rpc, --edw-recover) does not listen, print
listening, or spawn start. --edw-rpc is a control-socket client of a
running single-instance host. See feature-edw-rpc.md,
feature-single-instance.md, and
feature-beam-restart.md.
All host options use the edw prefix. They are stripped before remaining
argv is forwarded to the BEAM release.
| Flag | Meaning |
|---|---|
--edw-no-beam |
Do not spawn BEAM (dev / E2E) |
--edw-port=N |
Listen port (0 = ephemeral) |
--edw-host=ADDR |
Bind address (default 127.0.0.1) |
--edw-config=PATH |
Ini path |
--edw-lifetime=reconnect|coupled |
Process coupling (default reconnect) |
--edw-test-rpc |
Enable test.* JSON-RPC methods |
--edw-beam-path=DIR |
Override beam release directory |
--edw-beam-app=NAME |
Override release script name |
--edw-instances=multi|single |
Instance mode (default multi) |
--edw-instance-id=NAME |
Control-socket lock name (default: host exe basename) |
--edw-rpc <expr> |
One-shot Elixir eval via control socket instance.eval |
--edw-recover |
One-shot Mix eval of recovery_script (no application start) |
--edw-recovery-script=PATH |
Recovery .exs path |
--edw-recovery-after=N |
Startup crashes before automatic recovery (default 3) |
--edw-restart-beam=true|false |
Respawn BEAM after unexpected exit (default true) |
--edw-max-restart-attempts=N |
Cap consecutive unexpected exits (0 = no cap) |
--edw-restart-backoff-ms=N |
Initial backoff; doubles, cap 5000 ms |
Forwarded argv example:
DesktopWebView --edw-port=0 -- --foo bar
# BEAM receives: --foo bar
# (and EDW_PORT / EDW_HOST in the environment)reconnect(default for packaged host-first): host keeps listening after BEAM/client disconnect. Session UI (trays, windows, menus, icons, notifications, permission policy) MUST be destroyed so the next BEAM starts clean. Elixir may reconnect and callinitializeagain.coupled: client disconnect → host exits; host exit → BEAM child is terminated. Reset session UI before exit.--edw-no-beam(dev): host exits when the Elixir client disconnects, even if lifetime isreconnect— the VM owns the host process. Reset session UI first.
Packaged mode (restart_beam, default true) respawns the release after an
unexpected child exit. Consecutive attempt counters reset only on a successful
initialize, not on spawn.
Backoff after unexpected exit n (1-based):
min(restart_backoff_ms * 2^min(n-1, 4), 5000).
If restart_max_attempts > 0 and consecutive unexpected exits reach that cap,
the host exits. 0 means no cap.
A startup crash is a child exit before initialize. After
recovery_after (default 3) consecutive startup crashes, if recovery_script
is set, the host runs Mix release eval:
{beam}/bin/{app} eval "Code.eval_file(\"ABS_PATH\")"
OTP and Elixir load; the application does not start. Then the host respawns
start. --edw-recover runs that same eval without starting the UI.
--edw-rpc and --edw-recover are mutually exclusive.
Default instances = multi so --edw-no-beam E2E can run more than one host.
Packaged apps set instances = single. The first host binds the control
socket. A second launch sends instance.activate (not a second EDW TCP
client) and exits 0. See feature-single-instance.md.
When the host spawns BEAM, it sets RELEASE_DISTRIBUTION=none if that
environment key is unset.
| Platform | Delivery | Artifact name |
|---|---|---|
| macOS | Universal binary in Hex priv/native/macos/DesktopWebView |
DesktopWebView-macos-universal (also on GitHub Releases) |
| Windows | GitHub Releases (draft on tag); not in Hex priv/ |
DesktopWebView-windows-x64.exe + .sha256 |
| Linux | GitHub Releases (draft on tag); not in Hex priv/ |
DesktopWebView-linux-x86_64 + .sha256 |
CI ad-hoc signs the macOS binary (codesign -s -). Full Developer ID / notarization
and Windows Authenticode are expected later via desktop_deployment.
Tag workflow (.github/workflows/release.yml) should attach every built platform
asset to the same draft release. Elixir download/cache for Win/Linux is TBD;
until then use DESKTOP_WEBVIEW_BINARY / config :desktop_webview, :binary.
For microphone / camera inside WKWebView, the packaged app’s Info.plist must include:
NSMicrophoneUsageDescriptionNSCameraUsageDescription
Ensure the application identity used at runtime can access microphone/camera
under Windows privacy settings. WebView2 may show its own permission UI when
policy is ask.
Ensure the desktop entry / sandbox (if any) allows device access. WebKitGTK permission requests must still honor the hybrid RPC policy.