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:
-
@gfld/compositor-ffmpeg-h264is GPL-3.0, not Apache-2.0 as itspackage.jsonclaims. 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 WebCodecsVideoDecoderAPI (browser-native, MIT/W3C standard). -
x264encGStreamer plugin is GPL-2.0+. Using it in a proprietary product forces GPL on the entire combined work. Replace withvaapih264enc(Intel/AMD),nvh264enc(NVIDIA), oropenh264enc(BSD libopenh264, CPU fallback). -
gstreamer1.0-libavon Debian is GPL — the source is LGPL-2.1+, but Debian'slibavcodec.so.61is built with--enable-gpland directly linkslibx264.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 <canvas> 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 <canvas> |
| 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
-
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.) -
Clean-room is mandatory — you CANNOT read Greenfield's AGPL
@gfld/compositorsource 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). See11-pitfalls.md§Clean-room. -
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.) -
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.) -
Odoo's built-in WebSocket server is unsuitable —
addons/bus/websocket.pyhas a 1 MB frame cap, shared PostgreSQL pool, single gevent process. Run a separate aiohttp Python process on :8081 and route via Nginx. (See10-odoo-integration.md.) -
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.)
- cgroups v2, v1 shippable in 7-9 engineer-days. (See
-
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.mdfor 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) |
| ❌ 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 | ✅ |
| ❌ DROP | ||
| ❌ 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 | ✅ |