# Path A POC — WebKitGTK + C99: Findings ## Status: ✅ Working A 17 KB C99 binary opens a GTK window with a URL bar and a WebKitWebView, and successfully loads real-world HTTPS pages. Verified load: ``` [loaded] https://laantungir.net/ -- title: Laan Tungir ``` `laantungir.net` is a real site (the one from the FIPS setup docs) and WebKitGTK fetched, rendered, and parsed its `` correctly. ## Environment - Debian 13 (trixie), x86_64 - WebKitGTK 2.52.3 (API `webkit2gtk-4.1`) - GTK 3.24.49 - gcc 14.2.0 - `libwebkit2gtk-4.1-dev` installed via apt (~one `apt install`) ## What we built - [`main.c`](main.c) — ~130 lines of C99. A `GtkWindow` containing a `GtkBox` with a `GtkEntry` URL bar and a `WebKitWebView`. Signals: - URL entry `activate` → `webkit_web_view_load_uri()` - `load-changed` → sync URL bar + print `[loaded]` on `WEBKIT_LOAD_FINISHED` - `load-failed` → print `[failed]` with the error - [`Makefile`](Makefile) — pkg-config driven, one `make`. ## Time to first page load Very fast. From "install package" to "page rendered": 1. `apt install libwebkit2gtk-4.1-dev` — one command. 2. Write ~130 lines of C. 3. `make` → 17 KB binary. 4. Run → page loads. Total active effort: under an hour, most of which was the C file. ## Problems encountered (the friction we came to measure) ### 1. `-DGTK_DISABLE_DEPRECATED` breaks the build (hard error) Adding `-DGTK_DISABLE_DEPRECATED` to be "clean" caused: ``` WebKitContextMenuItem.h:56:60: error: unknown type name 'GtkAction' ``` WebKitGTK's context-menu API still references `GtkAction`, which GTK has deprecated and hides behind `GTK_DISABLE_DEPRECATED`. **Lesson:** do not define `GTK_DISABLE_DEPRECATED` when building against WebKitGTK 4.1. This is mildly concerning for the "reckless" goal — it means WebKitGTK drags along some legacy GTK API surface. ### 2. `-Wpedantic` is unusable with GObject headers (warning storm) `-std=c99 -Wpedantic` produces dozens of `redefinition of typedef` warnings because GObject's `WEBKIT_DECLARE_*` / `G_DECLARE_*` macros redeclare typedefs (a pattern that is legal in C11 but flagged under C99 `-Wpedantic`). **Lesson:** drop `-Wpedantic`, or move to `-std=c11`/`-std=gnu11` if strict pedantry matters. `-Wall -Wextra` is fine and clean. ### 3. Header layout is non-obvious The pkg-config include is `-I/usr/include/webkitgtk-4.1`, and the umbrella header is `#include <webkit2/webkit2.h>`. Individual headers live under `webkit/` (e.g. `webkit/WebKitWebView.h`), not under `webkit2/WebKit/` as the API docs sometimes imply. Not a real problem once you know to just include the umbrella — but worth noting. ### 4. `gtk_main()` is deprecated in GTK 4 (not a problem here) We targeted GTK 3 (WebKitGTK 4.1 API pairs with GTK 3). GTK 4 would require `gtk_application_run()` and a different lifecycle. Since WebKitGTK's 4.1 API is GTK-3-based, this is the correct pairing for now. A GTK-4-based WebKit (`webkitgtk-6.0`) exists in newer distros but is not in Debian 13. ## What this tells us about the reckless browser goals ### FIPS integration (custom URI scheme) — looks straightforward WebKitGTK has a first-class custom URI scheme API: `webkit_web_context_register_uri_scheme()`. You register a scheme (e.g. `fips`) and get a `WebKitURISchemeRequest` callback where you feed bytes into an input stream. This is exactly the hook we need to route `fips://` (or `*.fips`) requests to the FIPS TUN interface. The header `webkit/WebKitURISchemeRequest.h` is present. **No obvious blocker.** ### Nostr signing (`window.nostr` injection) — looks straightforward WebKitGTK exposes JavaScriptCore via `webkit/WebKitUserContentManager.h` and `jsc/jsc.h`. You can inject a script into every page with `webkit_user_content_manager_add_script()` and a `WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES` + `WEBKIT_USER_INJECTED_FRAMES_ALL_FRAMES` script. The injected script can define `window.nostr = { getPublicKey, signEvent, ... }` and bridge calls back to the C host via `jsc_value_object_set_property` or a message handler. **No obvious blocker**, and no extension system needed — this is the native mechanism. ### Security stripping — needs investigation WebKitGTK exposes `WebKitSettings` (disable JS, enable devtools, etc.) and `WebKitWebContext` / `WebKitWebsiteDataManager` for cookies, TLS, etc. The specific "reckless" asks: - **Disable CORS / same-origin** — not exposed as a simple setting in the 4.1 API. Likely requires a `WebKitWebExtension` (a separate shared lib loaded into the web process) that hooks the WebCore loader, or building WebKit from source with patches. This is the biggest unknown. - **Accept any certificate** — `WebKitWebContext` has a `load-failed-with-tls-errors` signal where you can call `webkit_web_context_allow_tls_certificate_for_host()`. Easy. - **Shared identity across origins** — cookies are per-`WebKitWebContext`; a single shared context gives a shared cookie jar by default. Easy. The CORS/same-origin strip is the one item that may require deeper engine access (a WebExtension or a custom build). Worth a follow-up spike. ## Binary size (actual) - Host binary (`reckless_webkit`): **17 KB** (stripped of debug info by `-O2`). - Engine library `libwebkit2gtk-4.1.so`: ~35 MB on this system. - The host binary is tiny because it links dynamically against the system WebKitGTK. This matches the "smallest practical footprint on a Linux desktop" prediction in the options doc. ## Verdict for Path A WebKitGTK + C99 is a **very comfortable** embedding path. The C API is genuine (no C++ required), the docs are adequate, custom URI schemes and JS injection are first-class, and a real site loaded on the first try. The only real unknown for the full reckless browser is CORS/same-origin stripping, which may need a WebExtension or source patch. Path A is a strong candidate for the primary engine.