# 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 `. 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.