Files

283 lines
20 KiB
Markdown

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