# 01 — Layered Architecture Map This chapter walks the proposed MIT-licensed architecture layer by layer, mirroring the Greenfield analysis structure (see `../greenfield-analysis/01-architecture.md`) so you can compare them side-by-side. ## The eight layers (MIT approach) ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ Layer 8: USER SHELL (Odoo 20 OWL addon, PROPRIETARY) │ │ Hooks: UserShellApi (events + actions) — clean-room spec │ │ See 10-odoo-integration.md │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 7: DESKTOP / LAYOUT POLICY (PROPRIETARY TS, clean-room) │ │ Floating vs AlwaysFullScreen, view stacking, focus activation refcount │ │ Custom: desktop/Desktop.ts (~800 LOC) │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 6: SURFACE LIFECYCLE / ROLES (PROPRIETARY TS, clean-room) │ │ XdgWmBase, XdgSurface, XdgToplevel, XdgPopup, XdgPositioner, Shell, │ │ ShellSurface, Subcompositor, Subsurface, SurfaceRole │ │ Custom: roles/*.ts (~1,800 LOC) │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 5: RENDERING PIPELINE (PROPRIETARY TS, clean-room, modernized) │ │ Surface (state stack), View, Output (WebGL2 canvas), Renderer, Scene, │ │ Shaders (YUV→RGBA, YUVA→RGBA, RGB+A→RGBA), Loop (RAF), Callback │ │ NEW: OffscreenCanvas + Web Workers + WebGL2 (Greenfield uses WebGL1) │ │ Custom: render/*.ts (~1,800 LOC), surface/*.ts (~1,200 LOC) │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 4: TRANSPORT (browser side, PROPRIETARY TS, clean-room) │ │ Channel (ARQ + Simple), ReconnectingWebSocket, RemoteAppLauncher, │ │ StreamingBuffer, BufferStream, EncodedFrame, EncoderFeedback, decoders │ │ NEW: WebCodecs VideoDecoder replaces FFmpeg WASM (was GPL) │ │ Custom: transport/*.ts (~3,500 LOC), decoders/*.ts (~500 LOC) │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 4: TRANSPORT (Python side, PROPRIETARY, clean-room) │ │ aiohttp WebSocket server (9 endpoints), asyncio + cffi shim, │ │ kcp PyPI package, multiprocessing.Process per session, JWT auth │ │ Custom: proxy/*.py (~2,880 LOC) │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 3: WAYLAND RUNTIME (browser side) │ │ REUSED: @gfld/common (Connection, WlObject, Fixed, marshallers) — MIT │ │ REUSED: @gfld/compositor-protocol (Display, Client, Resource, Global) │ │ — MIT, verified NO @gfld/compositor import │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 3: WAYLAND RUNTIME (Python side) │ │ REUSED: pywayland (Apache-2.0) — Display, Client, Resource, Global │ │ + 5-line cffi shim for wl_event_loop_get_fd (asyncio integration) │ │ + Greenfield's changes.diff (MIT patches) applied to libwayland-server │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 2: PROTOCOL GENERATORS (REUSED) │ │ REUSED: @gfld/client-generator, @gfld/compositor-generator (MIT) │ │ REUSED: pywayland-protocols (auto-generated from wayland-protocols repo) │ ├──────────────────────────────────────────────────────────────────────────┤ │ Layer 1: WAYLAND PROTOCOL XML (REUSED) │ │ REUSED: wayland.xml, xdg-shell.xml, wayland-drm.xml (upstream MIT) │ │ REUSED: web-bitmapbuf-unstable-v1.xml (Greenfield's custom extension) │ └──────────────────────────────────────────────────────────────────────────┘ ``` ## Side-by-side comparison with Greenfield | Layer | Greenfield | MIT approach | |---|---|---| | Layer 8: User Shell | `@gfld/compositor-shell` (AGPL, Preact) | Custom Odoo 20 OWL addon (proprietary) | | Layer 7: Desktop policy | `desktop/Desktop.ts` (AGPL) | Clean-room rewrite (~800 LOC TS) | | Layer 6: Surface roles | `Xdg*.ts`, `Shell*.ts`, `Sub*.ts` (AGPL) | Clean-room rewrite (~1,800 LOC TS) | | Layer 5: Rendering | `render/*.ts`, `Surface.ts`, etc. (AGPL, WebGL1, main-thread) | Clean-room rewrite (~3,000 LOC TS, WebGL2 + OffscreenCanvas + Workers) | | Layer 4: Transport (browser) | `remote/*.ts` (AGPL) + FFmpeg WASM (GPL) | Clean-room rewrite (~3,500 LOC TS) + WebCodecs (W3C standard) | | Layer 4: Transport (host) | `compositor-proxy/src/*.ts` (AGPL) + 3 N-API addons | Clean-room Python rewrite (~2,880 LOC Python, NO native addons) | | Layer 3: Wayland runtime (browser) | `@gfld/common` + `@gfld/compositor-protocol` (MIT) | **REUSED AS-IS** | | Layer 3: Wayland runtime (host) | `wayland-server-addon.node` (AGPL, 1231 lines C, patched libwayland) | **REPLACED** with pywayland (Apache-2.0) + 5-line cffi shim + Greenfield's `changes.diff` (MIT patches) | | Layer 2: Generators | `@gfld/*-generator` (MIT) | **REUSED AS-IS** + pywayland-protocols (auto-generated) | | Layer 1: Protocol XML | `protocol/*.xml` (upstream MIT + Greenfield's web-bitmapbuf) | **REUSED AS-IS** | ## How data flows across the layers ``` NATIVE APP (e.g. gtk4-demo, X11 via XWayland) │ │ libwayland-client → UNIX socket + SCM_RIGHTS FDs ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ PYTHON PROXY (aiohttp + pywayland + GStreamer + bubblewrap) │ │ │ │ Layer 3 (Python): pywayland + 5-line cffi shim │ │ ↳ VANILLA libwayland-server (NO patches needed for basic use) │ │ ↳ Apply Greenfield's changes.diff (MIT) IF we want wire-message │ │ interception hooks (recommended for the fast-sync optimization) │ │ ↳ Per-resource dispatchers (auto-generated by pywayland scanner) │ │ ↳ FDs wrapped as ProxyFD JSON │ │ │ │ Layer 4 (Python): aiohttp WebSocket + kcp PyPI package │ │ ↳ 9 endpoints: /signal, /channel, /mkfifo, /mkstemp-mmap, │ │ /read-fd, /read-fd-as-stream, /write-fd-as-stream, │ │ /close-fd, /request-keyframe │ │ ↳ asyncio integrates libwayland FD via loop.add_reader() │ │ ↳ multiprocessing.Process per Odoo session (fork) │ │ ↳ JWT validation (pyjwt) │ │ │ │ Layer 5 (encoding): PyGObject + GStreamer │ │ ↳ gst_parse_launch: same pipeline strings as Greenfield │ │ ↳ x264enc REPLACED with vaapih264enc / nvh264enc / openh264enc │ │ (x264enc is GPL — see 11-pitfalls.md) │ │ ↳ Zero-copy dmabuf via EGLImage → GstGLMemoryEGL (same as Greenfield)│ │ ↳ GLib MainLoop in separate Python thread │ │ ↳ asyncio.run_coroutine_threadsafe for cross-thread marshalling │ │ │ │ Layer 7 (sandboxing): bubblewrap + seccomp + systemd-run --scope │ │ ↳ Per-app: MemoryMax=1G, CPUQuota=200%, TasksMax=256 │ │ ↳ --unshare-all, --clearenv, --ro-bind /usr /usr │ │ ↳ --dev-bind /dev/dri/renderD128 /dev/dri/renderD128 (single GPU) │ │ ↳ --ro-bind $HOST_XDG/wayland-N /run/user//wayland-N │ │ ↳ --uid <100000-165535 per-app> --gid │ │ ↳ --unshare-net by default │ │ │ │ Layer 1 (XML) → Layer 2 (pywayland scanner) feeds Layer 3 dispatchers │ └─────────────────────────────────────────────────────────────────────────┘ │ │ WebSocket (TCP) + KCP-over-WS, JWT auth │ Same-origin via Nginx reverse proxy ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ BROWSER COMPOSITOR (Custom TypeScript + WebGL2 + WebCodecs + WASM) │ │ │ │ Layer 4 (browser): Custom Channel + KCP + ReconnectingWebSocket │ │ ↳ ARQChannel + SimpleChannel (clean-room, ~250 LOC) │ │ ↳ KCP ported fresh from MIT C reference (skywind3000/kcp) │ │ ↳ ReconnectingWebSocket (vendored from pladaria, MIT) │ │ ↳ WebCodecs VideoDecoder (primary, hardware-accelerated) │ │ ↳ OpenH264 BSD Wasm (fallback, NOT FFmpeg — FFmpeg is GPL) │ │ │ │ Layer 3 (browser): REUSED @gfld/common + @gfld/compositor-protocol │ │ ↳ Standard Wayland wire format, byte-for-byte │ │ ↳ WlObject registry, opcode dispatch via wlObject[opcode](msg) │ │ ↳ Per-client Connections (slow client never blocks another) │ │ │ │ Layer 5: Custom Surface.ts + View.ts + Output.ts + render/ │ │ ↳ Double-buffered state (state vs pendingState) │ │ ↳ Damage tracking via @gfld/compositor-wasm pixman regions │ │ ↳ WebGL2 with FBOs for YUV→RGBA, RGB+Alpha→RGBA conversion │ │ ↳ OffscreenCanvas in Web Worker (NEW vs Greenfield) │ │ ↳ Frame callbacks fired on requestAnimationFrame vsync │ │ │ │ Layer 6: Custom XdgWmBase + XdgSurface + XdgToplevel + XdgPopup + ... │ │ ↳ Configure/ack-configure round-trips │ │ ↳ Popup positioner math (anchor + gravity + constraints) │ │ ↳ Subsurface sync/desync mode cascading │ │ ↳ Primary selection (NEW — Greenfield lacks this) │ │ │ │ Layer 7: Custom desktop/Desktop.ts │ │ ↳ Floating vs AlwaysFullScreen mode │ │ ↳ View stacking, focus refcount │ │ ↳ Adaptive bitrate (NEW — consumes EncoderFeedback data) │ │ │ │ Layer 8: UserShellApi (clean-room spec) + Odoo 20 OWL shell │ │ ↳ Events: clientCreated, surfaceCreated, surfaceTitleUpdated, │ │ surfaceActivationUpdated, ... │ │ ↳ Actions: initScene, refreshScene, destroyScene, │ │ closeClient, activateSurface, setUserConfiguration │ │ ↳ OWL shell wires events to reactive state │ └─────────────────────────────────────────────────────────────────────────┘ │ │ userShell.events.* fired by compositor │ userShell.actions.* called by OWL shell ▼ ┌─────────────────────────────────────────────────────────────────────────┐ │ USER SHELL (Odoo 20 OWL addon — PROPRIETARY) │ │ │ │ - ir.actions.client with target=fullscreen │ │ - Application launcher (lists apps from Odoo /web/compositor/apps) │ │ - Window management UI (taskbar, decorations, minimize/maximize) │ │ - Keyboard layout picker (RMLVO) │ │ - Per-output element (OWL component owns the lifecycle) │ │ - Triggers app launches via /web/compositor/launch │ │ - Receives JWT from /web/compositor/token │ │ │ │ Not a Greenfield component — entirely your code. │ └─────────────────────────────────────────────────────────────────────────┘ ``` ## Reading guide for the rest of the bundle - `02-wayland-protocol.md` — Layer 1, 2, 3 (XML → generators → runtime, both sides) - `03-html5-rendering.md` — Layer 5 (custom WebGL2 + Workers + OffscreenCanvas) - `04-transport.md` — Layer 4 (Python aiohttp + kcp, browser side clean-room) - `05-input-handling.md` — Layer 5 input subsystem (Python xkbcommon, browser side) - `06-data-clipboard.md` — Layer 5 data device (with primary selection, Greenfield lacks) - `07-native-app-bridge.md` — Layer 4 Python + Layer 3 Python + sandboxing - `08-app-lifecycle.md` — Layer 6 + Layer 8 (UserShellApi clean-room spec) - `09-hardware-accel.md` — Layer 5 encoding + Layer 4 frame channel + WebCodecs - `10-odoo-integration.md` — Layer 8 + Odoo 20 backend addon + JWT + Nginx + Docker - `11-pitfalls.md` — 3 GPL contaminants found + clean-room legal requirements - `12-appendix.md` — glossary, file index, references