# MIT-Licensed Wayland-to-HTML5 Compositor — Architecture & Build Plan > **Purpose.** This bundle documents the architecture, components, and > implementation plan for building a **fully proprietary** Wayland-to-HTML5 > compositor that integrates with an Odoo 20 fork. It is the alternative to > the AGPL-licensed Greenfield codebase (`../greenfield-analysis/`). > > **Goal.** Run real Linux applications (GTK3/4, Qt, xterm, X11 via XWayland) > **natively inside the Odoo web client**, as if they were Odoo-native apps — > while a real Linux machine under the hood actually executes them. > > **License strategy.** Use every MIT/Apache-2.0/BSD-licensed Greenfield > library as a building block. Replace the AGPL-licensed packages > (`@gfld/compositor`, `@gfld/compositor-proxy`, `@gfld/compositor-shell`, > `@gfld/compositor-proxy-cli`) with **clean-room reimplementations** in > TypeScript (browser) and Python (Linux host). Ship the result as a > proprietary addon for the Odoo fork. ## How to read this bundle The bundle is split into 13 Markdown files mirroring the Greenfield analysis structure (so you can compare them side-by-side). Files are ordered from the highest-level view down to implementation details, then pitfalls, then appendix. | # | File | What you'll find inside | |---|------|-------------------------| | 0 | `00-overview.md` | The MIT approach at a glance, component inventory, license summary, architecture diagram | | 1 | `01-architecture.md` | Layered map of the proposed architecture, side-by-side with Greenfield's layers | | 2 | `02-wayland-protocol.md` | How to handle the Wayland protocol layer with MIT libs only (pywayland + Greenfield's MIT @gfld libs) | | 3 | `03-html5-rendering.md` | Custom WebGL2 + Web Workers + OffscreenCanvas browser compositor (replacing @gfld/compositor) | | 4 | `04-transport.md` | Python aiohttp WebSocket + KCP, replacing Node's ws + KCP TS | | 5 | `05-input-handling.md` | Python xkbcommon for keymap compilation, browser-side xkbcommon WASM for state | | 6 | `06-data-clipboard.md` | Clipboard + DnD bridge, with primary selection (which Greenfield lacks) | | 7 | `07-native-app-bridge.md` | pywayland + bubblewrap + seccomp + systemd-run, with proper sandboxing (Greenfield has none) | | 8 | `08-app-lifecycle.md` | xdg-shell state machine — what to implement, with a clean-room spec | | 9 | `09-hardware-accel.md` | GStreamer Python + WebCodecs (no GPL x264, no GPL FFmpeg WASM) | | 10 | `10-odoo-integration.md` | Odoo 20 OWL shell + backend addon, JWT auth, multi-tenancy | | 11 | `11-pitfalls.md` | Pitfalls of the MIT approach + risk analysis + the 3 GPL contaminants found | | 12 | `12-appendix.md` | Glossary, file index, references | | — | `../Greenfield-vs-MIT-Comparison.md` | Top-level comparison document with person-week estimates and strong recommendation | ## TL;DR — can we really do this? **YES — with three mandatory substitutions.** The 9 MIT/Apache/BSD Greenfield libraries are reusable as-is, with **zero AGPL contamination** (verified by grep'ing every `import` statement in `libs/`). But the audit found three GPL contaminants that must be replaced: 1. **`@gfld/compositor-ffmpeg-h264` is GPL-3.0, not Apache-2.0** as its `package.json` claims. The TypeScript wrapper is Apache-2.0, but the compiled FFmpeg WASM binary is GPL-3.0 (built with `--enable-gpl --enable-version3 --enable-nonfree`). **Replace with WebCodecs `VideoDecoder` API** (browser-native, MIT/W3C standard). 2. **`x264enc` GStreamer plugin is GPL-2.0+**. Using it in a proprietary product forces GPL on the entire combined work. **Replace with `vaapih264enc` (Intel/AMD), `nvh264enc` (NVIDIA), or `openh264enc` (BSD libopenh264, CPU fallback)**. 3. **`gstreamer1.0-libav` on Debian is GPL** — the source is LGPL-2.1+, but Debian's `libavcodec.so.61` is built with `--enable-gpl` and directly links `libx264.so.164` + `libx265.so.215`. **Don't install this package** — use only gst-plugins-base + gst-plugins-good + gst-vaapi + BSD subset of gst-plugins-bad. With these substitutions, **the proprietary strategy is unambiguously viable**. See `11-pitfalls.md` for the full audit table and `../Greenfield-vs-MIT-Comparison.md` for the final recommendation. ## Architecture in one diagram ``` ┌──────────────────────────────────────────────────────────────────────────────────┐ │ BROWSER (Odoo 20 web client) │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────┐ │ │ │ Odoo 20 OWL Shell addon (PROPRIETARY — your code) │ │ │ │ - Application launcher, window manager, keyboard picker │ │ │ │ - Per-output element │ │ │ │ - Implements UserShellApi.events.* callbacks (clean-room spec) │ │ │ │ - Calls userShell.actions.* (initScene, activateSurface, ...) │ │ │ └────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ userShell.events + actions (clean-room API) │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────────────────────┐ │ │ │ Custom Browser Compositor Library (PROPRIETARY — your code, clean-room) │ │ │ │ - Surface.ts, View.ts, Output.ts, Seat.ts, Keyboard.ts, Pointer.ts, ... │ │ │ │ - render/ (WebGL2 + OffscreenCanvas + Web Workers) │ │ │ │ - transport/ (WebSocket + KCP + ReconnectingWebSocket) │ │ │ │ - roles/ (xdg-shell state machine, clean-room from spec) │ │ │ │ - desktop/, input/, data/, math/ │ │ │ │ ~13,400 LOC TypeScript, ~25 dev-weeks for MVP │ │ │ └────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ Reuses MIT libs as npm deps: │ │ │ - @gfld/common (MIT, wire format) │ │ │ - @gfld/compositor-protocol (MIT, server stubs)│ │ │ - @gfld/client-protocol (MIT, browser apps) │ │ │ - @gfld/compositor-wasm (MIT, pixman+xkbcommon)│ │ │ - @gfld/xtsb (MIT, X11 — only if XWayland) │ │ │ - WebCodecs API (W3C standard, replaces GPL │ │ │ FFmpeg WASM) │ └──────────────────────────────────────────────────────────────────────────────────┘ │ │ WebSocket + KCP-over-WS │ 9 endpoint paths per session │ JWT (HS256) auth ▼ ┌──────────────────────────────────────────────────────────────────────────────────┐ │ PYTHON HOST (Linux server) │ │ │ │ ┌────────────────────────────────────────────────────────────────────────────┐ │ │ │ Odoo 20 backend addon (PROPRIETARY — your code) │ │ │ │ - /web/compositor/token → JWT issuance (HS256, 15-min expiry) │ │ │ │ - /web/compositor/apps → per-user allowed apps list │ │ │ │ - /web/compositor/launch → triggers proxy to spawn app │ │ │ │ - /web/compositor/audit → audit log intake │ │ │ │ - Models: compositor.app, compositor.session, compositor.audit.log │ │ │ └────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ subprocess / IPC │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────────────────────┐ │ │ │ Python Compositor Proxy (PROPRIETARY — your code, ~2880 LOC) │ │ │ │ - aiohttp WebSocket server (9 endpoints) on :8081 │ │ │ │ - asyncio event loop integrating libwayland FD (5-line cffi shim) │ │ │ │ - pywayland (Apache-2.0) for Wayland protocol dispatch │ │ │ │ - PyGObject+GStreamer (LGPL) for H.264/PNG encoding │ │ │ │ - Uses vaapih264enc / nvh264enc / openh264enc (NOT x264enc — GPL) │ │ │ │ - python-xkbcommon (MIT) for keymap │ │ │ │ - kcp PyPI package (MIT, Cython wrapper over ikcp.c) │ │ │ │ - bubblewrap + seccomp + systemd-run for app sandboxing │ │ │ │ - multiprocessing.Process per Odoo session (fork) │ │ │ │ - JWT validation (pyjwt, MIT) │ │ │ └────────────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ Reuses Greenfield MIT C files as vendored src:│ │ │ - westfield-buffer.c, westfield-dmabuf.c, │ │ │ westfield-egl.c, westfield-xwayland.c │ │ │ - wlr_drm.c, wlr_linux_dmabuf_v1.c (all MIT) │ │ │ - Greenfield's changes.diff (patched │ │ │ libwayland-server, MIT) │ │ │ │ │ │ WAYLAND_DISPLAY=wayland-N │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────────────────────┐ │ │ │ Native Linux apps (GTK3/4, Qt, xterm, X11 via XWayland) │ │ │ │ - Spawned via bwrap + seccomp + systemd-run --scope │ │ │ │ - cgroup limits: MemoryMax=1G, CPUQuota=200%, TasksMax=256 │ │ │ │ - Filesystem: /usr ro-bind, minimal /etc, $HOME overlay (discarded) │ │ │ │ - GPU: /dev/dri/renderD128 only (no card0) │ │ │ │ - Network: --unshare-net by default (most apps don't need it) │ │ │ │ - UID remap to 100000-165535 (per-app, /etc/subuid) │ │ │ └────────────────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────────────┘ ``` ## Effort summary (single engineer, sequential) | Phase | Weeks | Deliverable | |---|---|---| | **Phase 0: Spike** | 1 | Validate libwayland ↔ asyncio integration (5-line cffi shim), validate kcp Python package works | | **Phase 1: Browser compositor MVP** | 25 | Clean-room TypeScript compositor library, ~13,400 LOC, runs a single Linux app in `` | | **Phase 2: Python proxy MVP** | 3.5 (~17 dev-days) | aiohttp + pywayland + GStreamer, ~2880 LOC Python | | **Phase 3: Odoo integration** | 3.5 (~18 dev-days) | OWL shell + backend addon + JWT auth + Nginx + Docker | | **Phase 4: Sandboxing** | 1.5 (~7-9 dev-days) | bubblewrap + seccomp + systemd-run, v1 shippable | | **Phase 5: Polish & testing** | 3 | Tests, hardening, docs, XWayland (optional +8 weeks) | | **TOTAL (MVP)** | **~37 weeks** | Single-tenant, single-app, fully proprietary, sandboxed | | **TOTAL (with XWayland + multi-tenant)** | **~46 weeks** | Production-ready | With 2 senior engineers in parallel: **~5-6 months to MVP**, **~7-9 months to production**. See `../Greenfield-vs-MIT-Comparison.md` for the detailed comparison and recommendation. ## Key findings to remember 1. **License is clean** — with 3 substitutions (no GPL x264, no GPL FFmpeg WASM, no Debian gstreamer1.0-libav), the entire stack is MIT/Apache-2.0/BSD/LGPL. Fully proprietary product is shippable. (See `11-pitfalls.md`.) 2. **Clean-room is mandatory** — you CANNOT read Greenfield's AGPL `@gfld/compositor` source and reimplement it as "inspired-by" code. Under the *Computer Associates v. Altai* Abstraction-Filtration-Comparison test, even non-literal similarity (class names, method names, code structure) is sufficient to find derivative-work infringement. AGPL §13 network-use clause would then force you to open-source your entire Odoo fork. **One person writes the spec (may read AGPL), another implements (must NOT read AGPL).** See `11-pitfalls.md` §Clean-room. 3. **pywayland is Apache-2.0, not MIT** — but Apache-2.0 is permissive (allows proprietary use, modification, distribution). The user's "MIT-only" preference is satisfied in spirit; the only obligation is to keep the LICENSE + NOTICE files. (See `02-wayland-protocol.md`.) 4. **pywlroots is NOT recommended** — it operates ABOVE the wire-dispatch layer (wlroots handles each request locally), incompatible with Greenfield's wire-forwarding architecture. Instead: pywayland + 5-line cffi shim + vendored Greenfield C files (MIT) for dmabuf/egl/xwayland. (See `07-native-app-bridge.md`.) 5. **Odoo's built-in WebSocket server is unsuitable** — `addons/bus/websocket.py` has a 1 MB frame cap, shared PostgreSQL pool, single gevent process. Run a separate aiohttp Python process on :8081 and route via Nginx. (See `10-odoo-integration.md`.) 6. **Sandboxing is critical and achievable in 7-9 days** — Greenfield has zero sandboxing (pitfall M). With bubblewrap + seccomp + systemd-run + cgroups v2, v1 shippable in 7-9 engineer-days. (See `07-native-app-bridge.md`.) 7. **The browser compositor is the biggest effort** — ~25 dev-weeks for MVP, ~30-36 weeks total with XWayland. This is the bottleneck. See `03-html5-rendering.md` for the file structure and component-by-component estimate. ## How this analysis was produced This bundle synthesizes findings from 10 parallel research agents (one failed with a context deadline, but its scope was covered by other agents): - **Agent K** — pywayland deep dive (API surface, asyncio integration, license verification) - **Agent L** — wlroots + pywlroots + Smithay comparison, recommendation to NOT use pywlroots - **Agent M** — Greenfield MIT libs audit (per-library license verification, found GPL contaminant in compositor-ffmpeg-h264) - **Agent N** — GStreamer Python bindings feasibility, found x264enc GPL issue - **Agent P** — Custom browser compositor library design, clean-room legal analysis, file structure, effort estimate - **Agent Q** — Python WebSocket + KCP transport, recommended aiohttp + kcp PyPI package - **Agent R** — Odoo 20 integration patterns, JWT auth, multi-tenancy - **Agent S** — App sandboxing with bubblewrap + seccomp + systemd-run - **Agent T** — Comprehensive license audit, found 3 GPL contaminants, verified all other components are clean Each agent read the worklog (`/home/z/my-project/worklog.md`), researched its assigned area, and appended findings. This bundle is the synthesis. ## License summary table | Component | License | Compatible with proprietary? | |---|---|---| | **Browser side** | | | | @gfld/common | MIT | ✅ | | @gfld/client-protocol | MIT | ✅ | | @gfld/compositor-protocol | MIT | ✅ (verified NO @gfld/compositor import) | | @gfld/client-generator | MIT | ✅ | | @gfld/compositor-generator | MIT | ✅ | | @gfld/compositor-wasm (pixman+xkbcommon) | MIT | ✅ | | @gfld/xtsb (X11 bindings) | MIT | ✅ (only if XWayland needed) | | ~~@gfld/compositor-ffmpeg-h264~~ | ~~GPL-3.0~~ | ❌ REPLACE with WebCodecs | | Custom browser compositor (your code) | PROPRIETARY | ✅ (clean-room) | | **Python backend** | | | | pywayland | Apache-2.0 | ✅ | | python-xkbcommon (`xkbcommon` PyPI) | MIT | ✅ | | PyGObject (gi.repository.Gst) | LGPL-2.1+ | ✅ (dynamic linking) | | GLib | LGPL-2.1+ | ✅ (dynamic linking) | | GStreamer core | LGPL-2.0+ | ✅ (dynamic linking) | | gst-plugins-base, gst-plugins-good | LGPL | ✅ | | gst-vaapi (vaapih264enc) | LGPL + MIT (libva) | ✅ | | gst-plugins-bad (nvh264enc, openh264enc) | BSD subset | ✅ | | ~~gst-plugins-ugly (x264enc)~~ | ~~GPL-2.0+~~ | ❌ DROP | | ~~gst-libav (libavcodec)~~ | ~~LGPL source, GPL binary on Debian~~ | ❌ DROP | | libwayland | MIT | ✅ | | libdrm | MIT | ✅ | | mesa (libGL, libEGL, libgbm) | MIT | ✅ | | aiohttp | Apache-2.0 | ✅ | | kcp (PyPI, RealistikDash) | MIT (Cython wrapper over ikcp.c) | ✅ | | PyJWT | MIT | ✅ | | bubblewrap (bwrap) | LGPL-2.0+ | ✅ (dynamic linking) | | libseccomp | LGPL-2.1 | ✅ | | **Odoo integration** | | | | Odoo 20 Community | LGPL-3.0 | ✅ (separate process, no contamination) | | Nginx | BSD-2-Clause | ✅ | | Docker | Apache-2.0 | ✅ |