Files
sovereign_browser/docs/webkit-poc-findings.md
Laan Tungir 6803eec526 first
2026-07-11 11:32:55 -04:00

5.8 KiB

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 <title> 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 — ~130 lines of C99. A GtkWindow containing a GtkBox with a GtkEntry URL bar and a WebKitWebView. Signals:
    • URL entry activatewebkit_web_view_load_uri()
    • load-changed → sync URL bar + print [loaded] on WEBKIT_LOAD_FINISHED
    • load-failed → print [failed] with the error
  • 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 certificateWebKitWebContext 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.