393 lines
16 KiB
Markdown
393 lines
16 KiB
Markdown
# 11 — Pitfalls, GPL Contaminants, Clean-Room Requirements
|
|
|
|
This is the prominent pitfalls chapter for the MIT approach. It documents:
|
|
|
|
1. **3 GPL contaminants found** (must be replaced)
|
|
2. **Clean-room legal requirements** (cannot read AGPL source and reimplement)
|
|
3. **Engineering pitfalls of building from scratch**
|
|
4. **Risk analysis**
|
|
|
|
## Critical finding: 3 GPL contaminants
|
|
|
|
The user's #1 priority is **license purity** — no GPL/AGPL contamination of
|
|
the shipping product. The license audit (Agent T) found **3 GPL
|
|
contaminants** that MUST be replaced:
|
|
|
|
### Contaminant 1: `@gfld/compositor-ffmpeg-h264` is GPL-3.0, not Apache-2.0
|
|
|
|
**What Greenfield claims**: `package.json` declares `"license":
|
|
"Apache-2.0"`.
|
|
|
|
**What the LICENSE file actually says**: Full GPL v3 text.
|
|
|
|
**Why this happened**: The TypeScript wrapper (`H264Decoder.ts`,
|
|
`H264Worker.ts`, `index.ts`) is genuinely Apache-2.0. But the
|
|
`build_wasm.sh` script compiles FFmpeg with `--enable-gpl
|
|
--enable-version3 --enable-nonfree`, which makes the resulting WASM
|
|
binary GPL-3.0. The 1.8 MB compiled `libav-h264.js` contains embedded
|
|
GPL/LGPL license strings.
|
|
|
|
**Impact on the user**: Using `@gfld/compositor-ffmpeg-h264` would force
|
|
the entire Odoo fork to be GPL-3.0.
|
|
|
|
**Replacement**: WebCodecs `VideoDecoder` API (browser-native,
|
|
hardware-accelerated, W3C standard). Fallback: OpenH264 BSD Wasm (NOT
|
|
FFmpeg).
|
|
|
|
### Contaminant 2: `gstreamer1.0-plugins-ugly` (provides `x264enc`) is GPL-2.0+
|
|
|
|
**What it provides**: The `x264enc` GStreamer element, which is the
|
|
software H.264 encoder Greenfield uses by default.
|
|
|
|
**Why it's GPL**: `libx264.so.164` is GPL-2.0+. The plugin that wraps it
|
|
(`gst-plugins-ugly`) inherits the GPL. Loading this plugin into the
|
|
Python process = GPL contamination under FSF's strict interpretation.
|
|
|
|
**Verified**: `ldd libgstx264.so` → links `libx264.so.164` (GPL-2.0+).
|
|
x264 has NO dynamic-linking exception.
|
|
|
|
**Impact**: Using `x264enc` would force the entire Python proxy to be
|
|
GPL-2.0+.
|
|
|
|
**Replacement**: Use `vaapih264enc` (Intel/AMD, LGPL plugin + MIT libva),
|
|
`nvh264enc` (NVIDIA, BSD plugin + proprietary NVENC blob), or
|
|
`openh264enc` (CPU fallback, BSD libopenh264).
|
|
|
|
### Contaminant 3: `gstreamer1.0-libav` on Debian is GPL (HIDDEN TRAP)
|
|
|
|
**What it provides**: The `gst-libav` plugin set, which wraps FFmpeg's
|
|
libavcodec for various codecs.
|
|
|
|
**Why it's a trap**: The gst-libav plugin SOURCE CODE is LGPL-2.1+.
|
|
**BUT** Debian's `libavcodec.so.61` is built with `--enable-gpl` and
|
|
directly links `libx264.so.164` + `libx265.so.215`.
|
|
|
|
**Verified**: `ldd libgstlibav.so` → `libavcodec.so.61` → `libx264.so.164`
|
|
+ `libx265.so.215`.
|
|
|
|
**Impact**: Even if the user might never use `avenc_h264`, the mere
|
|
presence of `libgstlibav.so` triggers GStreamer's plugin auto-discovery,
|
|
which dlopens it, which loads the GPL'd FFmpeg into the Python process's
|
|
address space.
|
|
|
|
**Replacement**: Don't install `gstreamer1.0-libav`. Use only
|
|
`gst-plugins-base` + `gst-plugins-good` + `gst-vaapi` + BSD subset of
|
|
`gst-plugins-bad`.
|
|
|
|
## Clean-room legal requirements (CRITICAL)
|
|
|
|
### The problem
|
|
|
|
**You CANNOT read Greenfield's AGPL `@gfld/compositor` source and
|
|
reimplement it as "inspired-by" code.** Even non-literal similarity is
|
|
sufficient to find derivative-work infringement.
|
|
|
|
### Reasons
|
|
|
|
1. **AFC test risk**: Under *Computer Associates v. Altai*'s
|
|
Abstraction-Filtration-Comparison test, even non-literal similarity
|
|
(same class names, method names, code structure) is sufficient to
|
|
find derivative-work infringement. A single developer who reads AGPL
|
|
source then writes a TS replacement will almost certainly reproduce
|
|
the SSO (structure, sequence, organization) subconsciously.
|
|
|
|
2. **AGPL §13 network-use clause**: This is the killer for an Odoo fork.
|
|
AGPL-3.0 §13 requires offering the source of any modified AGPL work
|
|
to all users interacting with it over a network. If a court finds the
|
|
custom compositor is an AGPL derivative, the entire Odoo fork (which
|
|
links to the compositor) must be open-sourced to all users —
|
|
destroying the proprietary value.
|
|
|
|
3. **Westfield doesn't help**: Greenfield's predecessor repo at
|
|
https://github.com/udevbe/westfield IS MIT-licensed, but it contains
|
|
ONLY the protocol infrastructure (the same `@gfld/common` +
|
|
`@gfld/compositor-protocol` + `@gfld/compositor-wasm` we already have).
|
|
It NEVER had the in-browser compositor logic (Surface/Renderer/Seat/etc.).
|
|
The compositor code was always in the Greenfield monorepo, always AGPL.
|
|
|
|
### Right approach: True clean-room
|
|
|
|
1. **Person A** (spec author, MAY read AGPL source) writes a behavioral
|
|
spec (inputs/outputs/edge cases, NO code).
|
|
2. **Person B** (implementer, MUST NOT open `packages/compositor/src/`)
|
|
implements from spec + MIT references only:
|
|
- Wayland protocol spec (https://wayland.freedesktop.org/docs/html/)
|
|
- wlroots C source (MIT, https://gitlab.freedesktop.org/wlroots/wlroots)
|
|
- Weston C source (MIT, https://gitlab.freedesktop.org/wayland/weston)
|
|
- Smithay Rust source (MIT, https://github.com/Smithay/smithay)
|
|
- WebGL2/WebCodecs/KCP MDN docs
|
|
|
|
### Legal-defensibility artifacts
|
|
|
|
Maintain these as legal-defensibility artifacts:
|
|
|
|
- `SPEC.md` — the behavioral spec (written by Person A)
|
|
- `CLEAN_ROOM_LOG.md` — who read AGPL, who didn't, when, what they referenced
|
|
|
|
### What can Person B reference?
|
|
|
|
| Reference | License | OK to read for implementation? |
|
|
|---|---|---|
|
|
| Greenfield's `packages/compositor/src/` | AGPL-3.0 | ❌ NO (Person B forbidden) |
|
|
| Greenfield's `libs/` (MIT) | MIT | ✅ Yes (already reused) |
|
|
| Greenfield's `protocol/` XML | MIT | ✅ Yes |
|
|
| Greenfield's `docs/` | MIT | ✅ Yes |
|
|
| Wayland protocol spec | MIT | ✅ Yes |
|
|
| wlroots C source | MIT | ✅ Yes |
|
|
| Weston C source | MIT | ✅ Yes |
|
|
| Smithay Rust source | MIT | ✅ Yes |
|
|
| MDN docs (WebGL2, WebCodecs, etc.) | CC-BY-SA | ✅ Yes |
|
|
| pywayland source | Apache-2.0 | ✅ Yes |
|
|
| qtile source | GPL-2.0 | ❌ NO (architectural patterns OK, code not) |
|
|
|
|
## License audit table (full stack)
|
|
|
|
### Browser side
|
|
|
|
| Component | License | Compatible with proprietary? |
|
|
|---|---|---|
|
|
| @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, optional) | MIT | ✅ (only if XWayland needed) |
|
|
| ~~@gfld/compositor-ffmpeg-h264~~ | ~~GPL-3.0~~ | ❌ **REPLACE with WebCodecs** |
|
|
| Custom browser compositor (your code) | PROPRIETARY | ✅ (clean-room) |
|
|
| WebCodecs API | W3C standard | ✅ |
|
|
| WebGL2 | W3C standard | ✅ |
|
|
| OffscreenCanvas | W3C standard | ✅ |
|
|
| OpenH264 BSD Wasm (fallback) | BSD-2-Clause | ✅ |
|
|
|
|
### Python backend
|
|
|
|
| Component | License | Compatible with proprietary? |
|
|
|---|---|---|
|
|
| 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 | LGPL | ✅ |
|
|
| gst-plugins-good (pngenc) | 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 | ✅ |
|
|
| PyJWT | MIT | ✅ |
|
|
| bubblewrap (bwrap) | LGPL-2.0+ | ✅ (dynamic linking) |
|
|
| libseccomp | LGPL-2.1 | ✅ |
|
|
|
|
### Odoo integration
|
|
|
|
| Component | License | Compatible? |
|
|
|---|---|---|
|
|
| Odoo 20 Community | LGPL-3.0 | ✅ (separate process, no contamination) |
|
|
| Nginx | BSD-2-Clause | ✅ |
|
|
| Docker | Apache-2.0 | ✅ |
|
|
|
|
### Vendored Greenfield C files (MIT, reused verbatim)
|
|
|
|
| File | License | Reuse? |
|
|
|---|---|---|
|
|
| westfield-buffer.c | MIT (Greenfield) | ✅ |
|
|
| westfield-dmabuf.c | MIT (Greenfield) | ✅ |
|
|
| westfield-egl.c | MIT (Greenfield) | ✅ |
|
|
| westfield-xwayland.c | MIT (Greenfield) | ✅ |
|
|
| wlr_drm.c | MIT (vendored from wlroots) | ✅ |
|
|
| wlr_linux_dmabuf_v1.c | MIT (vendored from wlroots) | ✅ |
|
|
| drm_format_set.c | MIT (vendored from wlroots) | ✅ |
|
|
| addon.c | MIT (vendored from wlroots) | ✅ |
|
|
| changes.diff | MIT (Greenfield patches to libwayland) | ✅ (optional, for fast-sync) |
|
|
|
|
## CI/CD license hygiene gates (recommended)
|
|
|
|
Add these to your CI pipeline to catch GPL contamination early:
|
|
|
|
### Docker image audit
|
|
|
|
```bash
|
|
# Must return zero matches
|
|
dpkg -l | grep -E "libx264|libx265|gstreamer1.0-plugins-ugly|gstreamer1.0-libav"
|
|
```
|
|
|
|
### npm license check
|
|
|
|
```bash
|
|
# Must pass (no GPL/AGPL)
|
|
npx license-checker --production --failOn "GPL-3.0;AGPL-3.0;GPL-2.0;GPL-2.0+;GPL-3.0+"
|
|
```
|
|
|
|
### Python license check
|
|
|
|
```bash
|
|
# Must pass (no GPL/AGPL)
|
|
pip-licenses --fail-on "GPL.*;AGPL.*" --ignore-builtin
|
|
```
|
|
|
|
### Runtime GStreamer plugin check
|
|
|
|
```bash
|
|
# Must return zero matches
|
|
gst-inspect-1.0 --print-plugin-auto-install-info | grep -E "x264|x265|libav"
|
|
```
|
|
|
|
## Engineering pitfalls (beyond licensing)
|
|
|
|
### 1. Browser compositor is the biggest effort
|
|
|
|
The custom browser compositor is ~13,400 LOC TypeScript for MVP, ~25
|
|
dev-weeks (1 senior dev). This is the critical path.
|
|
|
|
**Mitigation**: Hire 2 senior engineers to parallelize. Use Smithay (Rust,
|
|
MIT) as a reference for Wayland semantics. Use wlroots (C, MIT) as a
|
|
reference for compositor patterns.
|
|
|
|
### 2. xdg-shell state machine is the highest-risk component
|
|
|
|
The configure/ack-configure round-trip, popup positioner math, and
|
|
subsurface sync/desync cascades are subtle. Bugs here can cause apps to
|
|
hang or crash.
|
|
|
|
**Mitigation**: Write extensive unit tests using Weston's test clients
|
|
(ported to TypeScript). Test with real GTK4/Qt6 apps early and often.
|
|
|
|
### 3. asyncio + libwayland integration is under-documented
|
|
|
|
The 5-line cffi shim for `wl_event_loop_get_fd` is verified working, but
|
|
the pattern is not widely documented. Bugs here could cause Wayland
|
|
events to be dropped or duplicated.
|
|
|
|
**Mitigation**: Spend 1 day on a standalone prototype before committing.
|
|
Reference qtile's Wayland backend (architectural patterns OK, code is
|
|
GPL — don't copy).
|
|
|
|
### 4. GStreamer threading is non-trivial
|
|
|
|
GLib MainLoop runs in a separate thread; results must be marshalled back
|
|
to asyncio via `asyncio.run_coroutine_threadsafe`. Bugs here can cause
|
|
race conditions or deadlocks.
|
|
|
|
**Mitigation**: Reuse Greenfield's MIT C files via cffi — only the
|
|
marshalling needs Python. Test with high-frame-rate content (e.g. glxgears).
|
|
|
|
### 5. XWayland is a major time sink (~8 weeks)
|
|
|
|
If you need X11 app support (e.g. older apps that don't speak Wayland
|
|
natively), the XWayland bridge is ~5,000 LOC TypeScript + ~465 LOC C.
|
|
It uses `@gfld/xtsb` (MIT, REUSED) for X11 protocol bindings.
|
|
|
|
**Mitigation**: Skip XWayland in MVP. Most modern GTK4/Qt6 apps are
|
|
Wayland-native. Add XWayland only if a specific X11-only app is needed.
|
|
|
|
### 6. KCP Python port may have performance issues
|
|
|
|
The `kcp` PyPI package (RealistikDash, MIT) is a Cython wrapper over
|
|
upstream `ikcp.c`. Performance should be adequate, but if it's not,
|
|
porting Greenfield's TypeScript KCP (~1100 lines) to Python would be
|
|
~1500 lines of pure Python (slower).
|
|
|
|
**Mitigation**: Benchmark early. If Cython wrapper is too slow, write a
|
|
C extension via cffi.
|
|
|
|
### 7. WebGL2 + OffscreenCanvas + Workers is complex
|
|
|
|
Multi-threaded rendering with OffscreenCanvas requires careful message
|
|
passing between workers. Debugging is harder than main-thread code.
|
|
|
|
**Mitigation**: Start with main-thread WebGL2 (simpler), then move to
|
|
OffscreenCanvas in Phase 5 (polish).
|
|
|
|
### 8. WebCodecs hardware decode may have bugs
|
|
|
|
Greenfield explicitly disables hardware decode with `// FIXME hardware
|
|
decoding frame offset is wrong`. We're enabling it (`prefer-hardware`),
|
|
but the bug Greenfield hit may still affect us.
|
|
|
|
**Mitigation**: Test extensively on Intel/AMD/NVIDIA GPUs. Fall back to
|
|
`prefer-software` if hardware decode is broken on specific hardware.
|
|
|
|
### 9. Bubblewrap + seccomp may break some apps
|
|
|
|
Some GTK apps may need additional filesystem access (e.g. ~/.config for
|
|
settings, ~/.cache for thumbnails). The default-deny approach may break
|
|
them.
|
|
|
|
**Mitigation**: Test with the apps you want to support. Add per-app
|
|
overrides in `compositor.app` model (e.g. extra filesystem binds).
|
|
|
|
### 10. JWT secret rotation
|
|
|
|
The JWT secret is shared between Odoo and the compositor proxy via env
|
|
var. If the secret leaks, attackers can forge JWTs and run apps on
|
|
behalf of any user.
|
|
|
|
**Mitigation**: Rotate the secret periodically (e.g. monthly). Use a
|
|
secrets manager (Vault, AWS Secrets Manager). Log all JWT validations.
|
|
|
|
## Risk analysis summary
|
|
|
|
| Risk | Likelihood | Impact | Mitigation |
|
|
|---|---|---|---|
|
|
| GPL contamination in shipping product | Medium (3 contaminants found) | **CRITICAL** (forces open-source) | 3 substitutions + CI/CD license gates |
|
|
| AGPL derivative-work lawsuit | Low (clean-room approach) | **CRITICAL** (forces open-source) | True clean-room with SPEC.md + CLEAN_ROOM_LOG.md |
|
|
| Browser compositor takes >6 months | Medium | High (delays MVP) | 2 senior engineers in parallel |
|
|
| xdg-shell state machine bugs | High | Medium (app crashes) | Extensive testing with Weston test clients |
|
|
| asyncio + libwayland integration bugs | Medium | High (events dropped) | 1-day prototype before committing |
|
|
| GStreamer threading deadlocks | Medium | Medium (encoder stalls) | Reuse Greenfield's MIT C files via cffi |
|
|
| XWayland scope creep | High | High (+8 weeks) | Skip XWayland in MVP |
|
|
| KCP Python performance | Low | Medium (latency) | Benchmark early, fall back to cffi C extension |
|
|
| WebGL2 + Workers complexity | Medium | Medium (debugging pain) | Start with main-thread, move to Workers in Phase 5 |
|
|
| WebCodecs hardware decode bugs | Medium | Medium (CPU usage) | Test on Intel/AMD/NVIDIA, fall back to software |
|
|
| Bubblewrap breaks apps | Medium | Low (per-app overrides) | Test with target apps, add per-app overrides |
|
|
| JWT secret leak | Low | **CRITICAL** (auth bypass) | Rotate periodically, use secrets manager |
|
|
|
|
## Design wins to copy from Greenfield
|
|
|
|
These Greenfield design choices are worth copying in the MIT approach
|
|
(but implemented clean-room):
|
|
|
|
1. **Generator-driven protocol stubs** — single XML source for both
|
|
sides. REUSED from Greenfield's MIT libs.
|
|
2. **Dual-buffer commit semantics** — Surface state vs pendingState,
|
|
atomic merge. Clean-room from spec.
|
|
3. **UserShellApi decoupling** — shell sees only `{events, actions}`,
|
|
never Wayland resources. Clean-room from spec.
|
|
4. **Asynchronous browser compositor** — per-client channels, never
|
|
blocks on a slow client. Clean-room from spec.
|
|
5. **KCP-over-WebSocket** for UDP-like latency over TCP. Use `kcp` PyPI
|
|
package (MIT).
|
|
6. **WebCodecs primary + fallback** — capability probe at session
|
|
startup. Clean-room, but enable hardware decode (Greenfield doesn't).
|
|
7. **Zero-copy dmabuf → encoder** via `linux-dmabuf-unstable-v1`.
|
|
REUSED vendored Greenfield C files (MIT).
|
|
8. **Per-surface encoder feedback channel** — Greenfield produces it
|
|
but doesn't use it. **Our MIT approach consumes it for adaptive
|
|
bitrate** (NEW).
|
|
9. **`ReconnectingWebSocket`** with exponential backoff. Vendored from
|
|
pladaria (MIT).
|
|
10. **Honest docs** — Greenfield's `docs/pages/design/` openly admits
|
|
what's wrong. Use as reference for trade-off analysis.
|
|
|
|
## Top-5 recommendations
|
|
|
|
1. **Replace the 3 GPL contaminants IMMEDIATELY**: WebCodecs (not FFmpeg
|
|
Wasm), vaapih264enc/nvh264enc/openh264enc (not x264enc), don't install
|
|
gst-libav.
|
|
2. **Adopt true clean-room discipline** for the browser compositor. One
|
|
spec author (may read AGPL), one implementer (must NOT). Maintain
|
|
`SPEC.md` + `CLEAN_ROOM_LOG.md` as legal-defensibility artifacts.
|
|
3. **Skip XWayland in MVP** — add 8 weeks only if legacy X11 apps are
|
|
required.
|
|
4. **Use the kcp PyPI package** — don't port Greenfield's TS KCP to
|
|
Python (would be slower).
|
|
5. **Add CI/CD license hygiene gates** — Docker image audit, npm
|
|
license-checker, pip-licenses, gst-inspect check. Catch GPL
|
|
contamination early.
|