Wails-inspired, V-native, with ideas borrowed from Tauri.
Not a line-by-line port of wailsapp/wails:
Wails v2 (stable) and v3 (beta) are used as a design guide, while the
implementation is idiomatic V — and improves on both Wails and Tauri where
V is stronger (sub-second builds, v -live, veb livereload, a single
binary, direct C interop with zero COM code on our side).
Status: Phases 0–4 E2E ✅ on Windows (Edge/WebView2) and Linux (WebKitGTK) — screenshots in
tests/e2e_windows/andtests/e2e_linux/. Phase 5 S1 waves 1–3 are shipped (ADR-0014/0015/0017): thedialog,os-info,clipboard,opener,notification,menuandtrayservices with real Windows backends (including the window host seam, the first OS-initiated events in Vails), Linux backends forclipboard+opener+menu+tray, the grant-driven.d.ts, andvails doctorreporting which backends are native here. macOS comes in Phase 6, mobile is planned (see ROADMAP).
From Wails — native backend without bundling a browser engine; unified
bindings, events and asset serving; a small vails CLI (init/run/build/ doctor) in the spirit of the wails CLI.
From Tauri — a capabilities model
(per-window command allowlists, asset scope, platform filtering); a strict
command-vs-event IPC contract; services shaped as plugins with manifests;
a vails.json project config; secure frontend defaults (CSP, no API outside
a Vails window).
Better than both, thanks to V — full rebuilds in about a second,
veb browser livereload in dev mode, one self-contained binary, and C
interop so thin that the Windows backend needs no COM code at all (the
webview library owns the WebView2 lifecycle).
Current state (all verified, not promised):
- Window + WebView on Windows and Linux, HTML rendering proven by screenshot
- JS → V → JS round trip:
window.vails.call('ping')→ V handler →pong - V → JS events:
webview.Ctx.emit(services push results/events back) - JSON-RPC bridge (
bridge/), two-way event bus (events/), JS escaping (jsesc/), asset serving with traversal protection (assets/), a dev server with livereload (dev/), thevailsCLI (cli/), and services as plugin manifests (services/, T5) with grant-driven.d.tsgeneration (vails dts) - Services:
dialog(native file picker / save dialog / message box — Windows E2E proof intests/e2e_windows/dialog.png),os-info,clipboard(Windows + Linux, E2E round trip on both —tests/e2e_windows/services.png,tests/e2e_linux/services.png),opener(Windows + Linux, with a scheme allowlist),notification(Windows: a real WinRT toast — a proper Action Center notification carrying the app's own name, withis_supportedso a frontend can ask first and a documentedbundle.identifieras its AppUserModelID; compile-verified, runtime proof pending — seedocs/ADR/0018),menu(native popup on both platforms; the choice arrives as an event, never as the command's result) andtray(Windows: a shell icon whose click reaches V through a comctl32 subclass —webview/host.v— and arrives astray:clicked; E2E proof intests/e2e_windows/tray.png) v test .green on Windows (36 test files) and on Linux (32 test files as of 2026-09-28; the four new pure-V modules are OS-agnostic and the Linux count has not been re-measured since — seetests/e2e_linux/README.md)- Landed since: the window menu bar (
menu.set_menu, ADR-0023), the tray menu (tray.set_menu, ADR-0026) and the GTKdialog(ADR-0027) - The build track landed 2026-09-29 (ADR-0034):
vails buildproduces a binary that starts (it stages the five side-by-side DLLs that used to be only a README comment),--versionstamps it, aDockerfile+ a first matrix CI exist,vails.jsongains adependenciesblock withvails.lock, and thesqlsecurity policy is code (sqlreg) rather than prose. Six corrections came out of it, four of which are inAGENTS.md§2b because they will bite the next contributor. - Planned next:
notificationon Linux, thenpost_to_main(U0/W0) — see ROADMAP.md
| Wails (v2/v3) | Tauri (v2) | Vails (this repo) | |
|---|---|---|---|
| Backend language | Go | Rust | V |
| Web engine | OS native (WebView2/WKWebView/WebKitGTK) | OS native (wry) | OS native (webview lib / WebKitGTK) |
| App model | Single window + services (v3) | Multi-window + labels | Single window ✅ / multi-window (planned) |
| JS → backend | Bound methods, reflection-based | invoke commands (typed) |
Bound methods, explicit registration ✅ |
| Backend → JS | Events | Events + channels | Events ✅ / channels ✅ (T3) |
| Security model | Open bridge | Capabilities + permissions + scopes | Capabilities ✅ (T1) + service commands as grant names |
| Services/plugins | Built-in services (v3) | 30+ plugins | Service manifests + 7 services ✅ / dialog, os-info, clipboard, opener, notification, menu, tray (T5, ADR-0014/0015/0017) |
| Mobile | v3: Android ✅ / iOS ✅ | Android + iOS | Planned (M0–M4, ADR-0006) |
| Config file | build/config.yml (v3) |
tauri.conf.json |
vails.json ✅ (T6) |
| Build speed | Go toolchain (tens of seconds) | Rust/cargo (minutes) | V + gcc (≈ seconds) ✅ |
| Binary size story | Single Go binary | ~600KB minimal | Single V binary ✅ |
| License | MIT | Apache-2.0 / MIT | MIT |
| Path | What |
|---|---|
application/ |
App + AppOptions (cf. Wails v3/pkg/application) |
webview/ |
Pure-V facade + webview_windows.c.v (Edge) + webview_linux.c.v (WebKitGTK) + webview_shim.h |
bridge/ |
JSON-RPC dispatch: JS calls V methods (handle_message, runtime_js, runtime_js_bound) |
events/ |
Two-way event bus (JS <-> V) + to_js snippets |
jsesc/ |
JS string-literal escaping shared by bridge/events |
assets/ |
Asset serving with traversal protection; $embed_file prod / veb dev (Phase 3) |
services/ |
Services as plugin manifests (T5 ✅, ADR-0014) + install path + support (per-OS backend report for doctor); dialog (Windows native), os_info, clipboard + opener + menu + tray (Windows + Linux), notification (Windows) |
generator/ |
.d.ts generation for bound methods; vails dts drives it from service manifests |
capabilities/ |
Per-window command allowlists (T1 ✅, ADR-0007) |
config/ |
vails.json project config: windows, capabilities, asset root, bundle (T6 ✅, ADR-0008) |
buildinfo/ |
The app's build identity: -d vails_version= stamping, SemVer validation, the framework version and v.mod drift (B0 ✅, ADR-0034) |
buildplan/ |
What vails build runs and stages, as a value — flags, output name, the five side-by-side DLLs — with the target as an argument so a Linux recipe is asserted on a Windows run (B1 ✅, ADR-0034) |
deps/ |
The dependencies block + vails.lock: Vails declares and reports, VPM resolves (B5 ✅, ADR-0034) |
sqlreg/ |
The sql security policy as code: a page names a query, V owns the statement (D0 ✅, ADR-0034) |
mobile/ |
Mobile entry + no-op stubs (planned, M0) |
cli/ |
vails init/run/build/doctor/dts/deps |
examples/hello/ |
Minimal app (window + ping button) |
examples/dialog/ |
Two services in one window (native dialogs + os-info) |
examples/multiwindow/ |
Two windows, one document, emit_to routing by label (F0, ADR-0035) |
examples/showcase/ |
The reference: one panel per capability, four honest verdicts, one tally (R3, ADR-0037) |
examples/services/ |
Seven services in one window + the E2E probe vehicle (superseded as the vehicle by showcase, kept for its probes) |
tests/e2e_windows/ |
Manual Edge E2E + proof screenshot |
tests/e2e_linux/ |
Headless xvfb script + screenshot |
docs/ADR/ |
Architecture decisions (why, not what) |
CHANGELOG.md |
Per-release changelog (Keep a Changelog) |
| Need | Windows | Linux / WSL Ubuntu | Notes |
|---|---|---|---|
| V | 0.5.x (v version) |
0.5.x, built from source (e.g. /root/vsrc) |
vails doctor only checks the version string; GUI on Linux requires a source build |
| C toolchain | MSYS2 ucrt64 (gcc --version must work) |
gcc + pkg-config |
Windows: put C:\msys64\ucrt64\bin first on PATH in every terminal |
| Webview deps | mingw-w64-ucrt-x86_64-webview + mingw-w64-ucrt-x86_64-webview2-loader |
libgtk-3-dev libwebkit2gtk-4.1-dev |
Linux E2E also needs xvfb (xwd, xwininfo for screenshots) |
| Browser engine | Edge WebView2 Runtime (ships with Windows 10/11; install it on bare VMs) | WebKitGTK 4.1 (pkg-config --modversion webkit2gtk-4.1) |
No bundled Chromium — the OS engine is used |
| Git | any recent Git | any recent Git | Needed to clone V and this repo |
Check your machine any time with the built-in doctor (see below). It reports
exactly these items: V version, OS, webkit2gtk on Linux, gcc + webview.h
on Windows.
Follow the official V install guide, then verify:
v version # need 0.5.xOn WSL Ubuntu the GUI path needs V built from source (the -gc none
requirement in ADR-0005 only holds for that setup).
Run in PowerShell:
$env:PATH = "C:\msys64\ucrt64\bin;" + $env:PATH
gcc --version # must print a ucrt64 gccIf gcc is missing, install MSYS2 ucrt64 and its toolchain, then install the
webview packages inside the ucrt64 shell:
pacman -S mingw-w64-ucrt-x86_64-webview mingw-w64-ucrt-x86_64-webview2-loadervails doctor looks for the header at
C:/msys64/ucrt64/include/webview/webview.h — if it reports MISSING, the
pacman step above is what fixes it.
Runtime DLLs are not linked statically. After every Windows build, copy
these next to the .exe (all from C:\msys64\ucrt64\bin):
libwebview-0.12.dll,WebView2Loader.dlllibgcc_s_seh-1.dll,libstdc++-6.dll,libwinpthread-1.dll
sudo apt install libgtk-3-dev libwebkit2gtk-4.1-dev xvfb
pkg-config --modversion webkit2gtk-4.1 # must print a 4.1.x versionHeadless runs (no desktop) additionally need:
unset WAYLAND_DISPLAY
export GDK_BACKEND=x11 WEBKIT_DISABLE_COMPOSITING_MODE=1See tests/e2e_linux/run_headless.sh for the exact sequence (build with
-gc none, run under xvfb-run, screenshot with xwd).
git clone <this-repo> vails
cd vails
v test . # unit tests, all green, no windows openedOn Windows with gcc 16 the test command carries one extra flag, and it is not optional:
$env:PATH = "C:\msys64\ucrt64\bin;" + $env:PATH
v -cc gcc test . # 33 test files (`webview` is excluded; see the ROADMAP)The reason is a V 0.5.2 bug rather than a Vails one: dev/ imports
net.http, V's dependency_scan_fallback link path emits #flag-sourced
-l flags before most object files, and GNU ld only resolves an
archive against the objects that precede it — so ws2_32 is on the link
line and cannot resolve anyway. A #flag in the importing module does not
help (-ldflags is emitted last); see ADR-0034 and AGENTS.md §1.
v test . never opens a window. GUI checks stay manual — see
tests/e2e_windows/README.md and tests/e2e_linux/README.md.
.github/workflows/ci.yml runs v test . plus a smoke build on both
platforms per push: Linux inside the repository's Dockerfile (V's own
thevlang/vlang:ubuntu-build image, GTK + WebKitGTK + AppIndicator, V
compiled once as a cached layer), Windows on a native runner with MSYS2
ucrt64. The Windows job's distinguishing step runs vails build and then
asserts the five side-by-side DLLs are in the artifact directory — a
green CI that ships an .exe which cannot start is worse than no CI. A
v* tag additionally triggers a version-stamped release build.
docker build -t vails-ci -f Dockerfile .
docker run --rm vails-ci # v test . inside the containerThe vails CLI is minimal on purpose. It does six things: version,
doctor (incl. vails.json validation), init (scaffolds main.v +
vails.json), config-validating run, a build that produces a binary
that starts, dts (grant-driven .d.ts + per-service JS) and
deps (the declared dependencies and how they compare to vails.lock).
# 1. build the CLI (from the repo root)
# Windows + gcc 16 needs two extra flags: the CLI embeds the dev
# server, so it imports net and links winsock.
v -o vails ./cli
# 2. sanity checks
./vails version
./vails doctor
# v version : check with `v version` (need 0.5.x)
# os : windows | linux | ...
# on Linux : webkit2gtk version or MISSING + apt hint
# on Windows: gcc version + webview.h found/MISSING + pacman hint,
# and how many of the 5 side-by-side DLLs are present
# vails.json: ok | INVALID + reason | not found (optional here)
# services : which services the capabilities grant (T5)
# backends : which services have a NATIVE backend on this machine, and
# which granted service is a stub here (ADR-0015)
# deps : what the dependencies block declares, and whether
# vails.lock matches it
# build : whether THIS binary was stamped with a version. `dev`
# means every update check is silently disabled (ADR-0034)
# 3. scaffold a minimal app (default name: hello)
./vails init myapp
ls myapp # contains main.v + vails.json
./vails run --config myapp/vails.json # dev server + window at its URL
# 4. build it - the five side-by-side DLLs are staged for you, and
# --version stamps the binary so `buildinfo.version()` is not 'dev'
./vails build --config myapp/vails.json --version 1.0.0
./vails deps --config myapp/vails.json # declared vs. resolvedvails init <name> creates ./<name>/main.v — a single-file window that
loads the project frontend. It is a starting point; for the fuller pattern
copy examples/hello/ (bridge + handlers) or examples/dialog/ (services
on_ready+ generated.d.ts).
A service is a manifest (services/manifest.v) plus a native backend, and
it registers through one path (services.install). Its commands are
its capability names, so a grant in vails.json is all a frontend needs:
"capabilities": [
{ "id": "dialogs", "windows": ["main"], "commands": ["dialog.open", "dialog.save", "dialog.message"] }
]./vails dts --config myapp/vails.json --js
# wrote myapp/frontend/vails.d.ts (1 service(s): dialog)
# wrote myapp/frontend/vails-services.js (load it from index.html)The .d.ts is generated from the grants, so a TypeScript frontend can
only type-check what it was actually allowed to call; grant names that no
service provides (your own commands) are reported as such, not as errors.
--check prints instead of writing (CI). In the app, services are
installed from Config.on_ready, which is where the window handle a
service needs to parent its native UI to exists:
mut router := bridge.new_router()
on_ready := fn [mut router] (ctx webview.Ctx) {
services.install_clipboard(mut router, ctx) or { eprintln(err.msg()) }
services.install_dialog(mut router, ctx) or { eprintln(err.msg()) }
}
webview.run(router: &router, registry: cfg.to_registry(), html: html, on_ready: on_ready)!One field-name rule worth knowing before you write a service: a V struct
field name is the wire name (json2 drops keys it does not recognize, and
@json: attributes do not survive V's C codegen), so a manifest's ts_types
promise default_path, never defaultPath.
The built-in catalog today, and where each one actually works:
| Service | Commands | Windows | Linux |
|---|---|---|---|
dialog |
open / save / message |
✅ Common Item Dialog | ✅ GTK (ADR-0027) |
os_info |
get |
✅ pure V | ✅ pure V |
clipboard |
read_text / write_text |
✅ user32 | ✅ GTK clipboard |
opener |
open_url / open_path |
✅ ShellExecuteW |
✅ GIO (no with) |
notification |
notify / is_supported |
✅ WinRT toast (ADR-0018) | stub |
menu |
popup / close |
✅ TrackPopupMenuEx |
✅ GtkMenu |
tray |
set / destroy |
✅ Shell_NotifyIconW + click → tray:clicked |
✅ StatusNotifierItem (needs a tray host) |
vails doctor prints that same table for the machine you are on
(backends : 7/7 service(s) native here on Windows), and
notification.is_supported lets a frontend ask at runtime instead of firing
a notification that quietly does nothing.
menu and tray are the two services that are driven by the OS rather than
by the page (ADR-0017): a native menu choice and a tray click both arrive as
events — menu:clicked / menu:canceled and tray:clicked — and on Windows
they reach V through a comctl32 subclass the window host seam installs on
demand (webview/host.v), so an app that uses neither service never gets a
subclass on its window.
v test . never opens a window, never opens a native dialog or menu, and
never touches the real clipboard or tray: modal services are the documented
exception to the "handlers stay fast" rule (ADR-0014), and the clipboard is
shared machine state — its round trip is proven by examples/services instead.
Project config (vails.json with windows list, capabilities, asset roots,
bundle settings) is live (T6, ADR-0008): vails init scaffolds it,
vails run/build read it, vails doctor validates it.
Windows (PowerShell, MSYS2 ucrt64):
$env:PATH = "C:\msys64\ucrt64\bin;" + $env:PATH
v -cc gcc -o hello.exe ./examples/hello
# copy the 5 DLLs listed above next to hello.exe
./hello.exe # click "ping backend" -> "pong"Linux / WSL Ubuntu:
v test . # unit tests first
v -gc none -o hello ./examples/hello # -gc none is required (ADR-0005)
./helloWhat success looks like: a 1024x768 window titled "Hello Vails", HTML
rendered, clicking "ping backend" changes the status to "backend says: pong"
(JS → V → JS round trip through bridge.Router.handle_message). Proof
screenshots live in tests/e2e_windows/pong.png and tests/e2e_linux/.
$env:PATH = "C:\msys64\ucrt64\bin;" + $env:PATH
v -cc gcc -o dialog.exe ./examples/dialog # same 5 DLLs next to the exe
./dialog.exe
# "Open file…" / "Open several…" / "Save as…" -> real Windows dialogs,
# parented to the window; "Ask…"/"Confirm…" -> MessageBox; "Read host info"
# -> the os_info service; "Call an ungranted command" -> `forbidden: …`vails dts regenerates examples/dialog/frontend/vails.d.ts from its
grants. VAILS_DIALOG_PROBE=open|multi|save|message|host makes the page
call that command on load — the E2E hook used for the proof below (a
synthetic click does not reach WebView2 content). On Linux the dialog
service is real as of ADR-0027 — a call made with no display is refused
with a message saying so, and os_info works everywhere.
$env:PATH = "C:\msys64\ucrt64\bin;" + $env:PATH
v -cc gcc -o services.exe ./examples/services # same 5 DLLs next to the exe
$env:VAILS_SERVICES_PROBE = "clipboard" # or: opener | notification | none
./services.exeThe clipboard probe is the one worth running: it copies a fixed non-ASCII
string and pastes it back without a human in the loop, so the status line
is the whole round trip through the real Windows clipboard — proof in
tests/e2e_windows/services.png. opener reports what the shell said, and
notification asks is_supported before firing.
Linux (WSL Ubuntu, V at /root/vsrc/v):
cd /mnt/d/MyProjects/vails
VAILS_SERVICES_PROBE=clipboard sh tests/e2e_linux/run_services.sh
# builds with -gc none, runs under xvfb, screenshots to
# tests/e2e_linux/services.pngSame proof through the GTK clipboard (tests/e2e_linux/services.png), and
VAILS_SERVICES_PROBE=notification shows the honest Linux answer:
not supported here - the service says so instead of doing nothing
(tests/e2e_linux/notification.png).
On Windows, tests/e2e_windows/capture.ps1 -Out shot.png -WindowTitle "Vails Services Demo" takes the screenshot and raises the window first — a shot of
a covered window proves nothing.
Read AGENTS.md first — it is the workflow contract. The short version:
v fmt -w . # format before every commit (law, no debates)
v test . # must be green on Windows AND on LinuxLinux (WSL Ubuntu, the v there is not on PATH):
wsl -d Ubuntu -- /root/vsrc/v test . # 29/29
wsl -d Ubuntu -- /root/vsrc/v -gc none -o services ./examples/servicesRules that matter daily:
- No globals.
pubonly what other modules need. - Errors as values (
!T+or { }). Never panic in library code;panic/exitis allowed only incli/andexamples/. - C interop (
#include+fn C.*) lives only in*_linux.c.v(later_windows.c.v/_darwin.c.v) files. Everything else is pure V and must compile and pass tests on Windows too (the CI machine has no gcc/pkg-config;webview_linux.c.vis skipped there by the_linuxsuffix rule). - Cross-platform code goes behind the
webview/facade;application/,bridge/,events/stay OS-agnostic. - New native capability = new file under
services/+ test + ADR entry. - Every change gets a line in
CHANGELOG.mdunder## [Unreleased]. - Work only inside the current phase's scope (
ROADMAP.mdcheckbox); each phase ends with code + tests + a docs line.
| Symptom | Fix | Ref |
|---|---|---|
vails doctor: webkit2gtk: MISSING |
sudo apt install libgtk-3-dev libwebkit2gtk-4.1-dev |
cli/vails.v, tests/e2e_linux/README.md |
vails doctor: gcc: MISSING (Windows) |
Install MSYS2 ucrt64 toolchain, prepend C:\msys64\ucrt64\bin to PATH |
cli/vails.v |
vails doctor: webview: MISSING (Windows) |
pacman -S mingw-w64-ucrt-x86_64-webview mingw-w64-ucrt-x86_64-webview2-loader |
cli/vails.v |
hello.exe exits silently / WebView2 error |
Install Edge WebView2 Runtime; copy the 5 DLLs next to the exe | tests/e2e_windows/README.md |
services/ fails to build on Windows |
It holds a .c.v: needs MSYS2 gcc (same as webview/). v test ./services fails without it |
ADR-0014, vails doctor |
dialog.* rejects with forbidden: |
The capability in vails.json does not grant that command (names are dialog.open, …) |
ADR-0007/0014 |
dialog.* says no display available on Linux |
The app has no DISPLAY (headless). GTK needs gtk_init first, so the service refuses with a message naming the cause rather than crashing |
ADR-0027 |
A right click on a tray icon with a menu attached stops emitting tray:clicked |
That is the contract: the menu owns the right click. Apps that never call tray.set_menu are unaffected |
ADR-0026 |
notification.notify says not implemented on Linux |
Expected: the Windows backend is a WinRT toast, which has no Linux equivalent yet. Ask notification.is_supported first — it answers false there |
ADR-0015/0018, ROADMAP Phase 5b |
notification.notify says no app identity |
vails.json has no bundle.identifier, and a desktop app cannot raise a toast without an AppUserModelID. vails init scaffolds one; vails doctor warns about it too |
ADR-0018 |
notification.notify says the Windows toast could not be shown (…hr=0x80040154) |
The machine has no WinRT runtime (a stripped/Server-style Windows image): CLASS_E_CLASSNOTAVAILABLE, so no WinRT class can be activated. Not a Vails bug — check for C:\Windows\System32\Windows.Foundation.dll |
ADR-0018 |
| A page renders but every button is disabled ("preview mode") | window.vails was never injected: the bridge is not wired on this platform/build |
ADR-0015 (the Linux transport was declared, not connected) |
A Linux app fails to build with a -Wincompatible-pointer-types / implicit-declaration error |
gcc 14+ treats those as errors; a V function pointer is not a WebKit callback, and some WebKit getters are not public in your version — read the installed headers | webview/webview_linux_shim.h, ADR-0015 Notes |
A service's params arrive empty although the .d.ts type-checks |
The wire names are the V field names: default_path, not defaultPath |
ADR-0015, dialog_test.v |
vails doctor shows a service as stub |
It is honest: this platform has no backend for it yet (the note says which phase) | ADR-0015, services/support.v |
A Linux build of services/ fails on app_indicator_* |
sudo apt install libayatana-appindicator3-dev (needed by the tray backend) |
ADR-0017, tests/e2e_linux/README.md |
| The tray icon is installed but invisible on Linux | There is no StatusNotifierHost in a headless session; the item is registered, nothing draws it. dbus-run-session is also needed for the D-Bus registration |
ADR-0017, tray_support() |
| A menu or tray answer never arrives | Both are events, not command results — listen for menu:clicked / menu:canceled / tray:clicked with onEvent. On Linux the tray has no click event at all (the host opens the item's menu) |
ADR-0017 |
v test on Linux hangs on a file that builds a Backend |
A V 0.5.2 C-codegen problem with a closure inside a map[string]fn in a test build (the same code compiles in seconds in an app build) |
ADR-0017 Notes, tests/e2e_linux/README.md |
| Linux GUI crash / GC fork errors | Always build GUI apps with -gc none |
ADR-0005, examples/hello/main.v header |
| Black/blank window under Wayland | unset WAYLAND_DISPLAY, GDK_BACKEND=x11, WEBKIT_DISABLE_COMPOSITING_MODE=1 |
AGENTS.md §1, run_headless.sh |
frontend/index.html not found |
Run from the repo root or the example dir so load_frontend_html finds its candidates |
examples/hello/main.v |
AGENTS.md— workflow contract (read first)CONTEXT.md— domain modelROADMAP.md— phases + Tauri-inspired track (T1–T7) + mobile track (M0–M4)CHANGELOG.md— what changed, per release (Keep a Changelog)docs/ADR/— why-not-what decisionstests/e2e_windows/README.md,tests/e2e_linux/README.md— manual GUI proofs