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 <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

  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 ✅
S
Description
Architecture & build plan for our MIT-licensed Wayland-to-HTML5 compositor (replaces Greenfield's AGPL parts). 14 files, ~80 pages.
Readme
120 KiB
0 Stars 1 Watchers 0 Forks
Languages
Markdown 100%