Chromium Customization for Kiosk¶
Warmwind runs a customized Chromium in kiosk mode on Sway, rendering each AI agent's UI. This article covers Chromium's multi-process architecture, the Ozone abstraction for Wayland, kiosk flags, CEF embedding, building from source, and how Brave/ungoogled-chromium patch upstream.
Multi-Process Architecture¶
graph LR
Browser["Browser"] --> GPU["GPU"]
Browser --> R1["Renderer 1"]
Browser --> R2["Renderer 2"]
Browser --> Util["Utility"]
| Process | Sandbox | Role |
|---|---|---|
| Browser | None (privileged) | Tab management, navigation, IPC routing |
| Renderer | seccomp-bpf + namespaces | HTML/CSS/JS per site instance |
| GPU | Namespace sandbox | All GL/Vulkan calls, compositing |
| Utility | Varies | Codecs, network service |
For kiosk: typically one renderer process (single origin) and one GPU process.
Ozone Platform Abstraction¶
Ozone decouples Chromium from the display server. At build time you select
backends; at runtime --ozone-platform= picks one:
| Platform | Backend |
|---|---|
wayland |
wl_surface, linux-dmabuf, xdg-shell |
x11 |
X11 (legacy) |
headless |
Off-screen rendering |
drm |
Direct KMS (ChromeOS) |
Wayland backend uses linux-dmabuf-unstable-v1 for zero-copy buffer sharing
with the compositor and xdg-shell for window management.
Kiosk Command-Line Flags¶
chromium \
--kiosk \ # fullscreen, no UI chrome
--ozone-platform=wayland \ # Wayland backend
--noerrdialogs \ # suppress error popups
--disable-extensions \ # no extension loading
--disable-translate \ # no translate bar
--disable-infobars \ # no info bars
--disable-session-crashed-bubble \ # no crash recovery UI
--disable-component-update \ # no background updates
--autoplay-policy=no-user-gesture-required \
--no-first-run \ # skip first-run wizard
--start-maximized \ # fallback if --kiosk fails
--disable-features=TranslateUI \
"https://agent.warmwind.internal"
Additional flags for hardened environments:
--disable-gpu-sandbox \ # if GPU sandbox conflicts with container
--enable-features=UseOzonePlatform \
--disable-dev-shm-usage \ # use /tmp instead of /dev/shm (containers)
--user-data-dir=/tmp/chromium # ephemeral profile
CEF (Chromium Embedded Framework)¶
CEF wraps Chromium's content layer into a C/C++ library for custom browsers:
// Minimal CEF kiosk browser
class KioskClient : public CefClient, public CefLifeSpanHandler {
CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override {
return this;
}
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
// Browser window created -- go fullscreen
}
bool DoClose(CefRefPtr<CefBrowser> browser) override {
return false; // prevent closing
}
IMPLEMENT_REFCOUNTING(KioskClient);
};
CEF advantages: stable API across Chromium versions, fine-grained control over navigation, JavaScript bindings, no address bar code to strip.
Building Chromium from Source¶
# ~30GB source, ~4 hours build on 16-core
fetch --nohooks chromium
cd src && gclient sync
# GN build configuration
cat > out/Release/args.gn << 'EOF'
is_official_build = true
is_debug = false
target_os = "linux"
ozone_platform_wayland = true
ozone_platform_x11 = false
ozone_auto_platforms = false
use_system_minigbm = true
enable_nacl = false
is_component_build = false
EOF
gn gen out/Release
autoninja -C out/Release chrome # ~16,000 build targets
Key GN variables for Warmwind: ozone_platform_wayland, use_system_minigbm,
enable_vulkan, proprietary_codecs.
Brave's Patching Approach¶
Brave builds on top of upstream Chromium using chromium_src overrides:
If this file exists, the build system compiles it instead of the upstream file. Conditional compilation:
#if BUILDFLAG(BRAVE_CHROMIUM_BUILD)
// Brave-specific behavior
#else
// Upstream Chromium behavior
#endif
Brave also patches via brave/patches/*.patch (applied during gclient sync).
This approach minimizes merge conflicts on Chromium rebases.
ungoogled-chromium's Approach¶
Different philosophy: no forked source, only:
- Config flags (
user_flags.gn): disable Google services at build time. - Patch series:
patches/core/*.patchapplied in order. - Pruning list: files deleted before build (telemetry, updater).
Warmwind can adopt this model: maintain a small patch set on top of upstream Chromium releases, rebuild per release cycle.
systemd Unit for Kiosk Chromium on Sway¶
[Unit]
Description=Kiosk Chromium on Sway
After=sway.service
Requires=sway.service
[Service]
Type=simple
User=kiosk
Environment=WAYLAND_DISPLAY=wayland-1
Environment=XDG_RUNTIME_DIR=/run/user/1000
ExecStart=/usr/bin/chromium \
--kiosk \
--ozone-platform=wayland \
--noerrdialogs \
--disable-extensions \
--user-data-dir=/tmp/chromium-kiosk \
https://agent.warmwind.internal
Restart=always
RestartSec=3
# Hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
SystemCallFilter=@system-service
[Install]
WantedBy=graphical.target
What's new (2025–2026)
- Rust in Chromium: production Rust components now ship --
crabbyavif(AVIF image decoder) and a QR code generator, with more on the way. - WebDriver BiDi shipping across Selenium, Cypress, and Puppeteer as the successor to the Chrome DevTools Protocol (CDP).
- Chrome 132 removed old headless mode;
--headless=newis now the only headless option (full browser rendering, not the old minimal path).
Glossary
- Ozone
- Chromium's platform abstraction layer. Selects display backend (Wayland, X11, DRM) at build or runtime.
- CEF (Chromium Embedded Framework)
- Library wrapping Chromium's content layer for embedding in custom applications.
- GN
- Chromium's meta-build system generating Ninja files. Replaced GYP.
- Ninja
- Fast build system consuming GN-generated files.
autoninjaauto-selects parallelism. - chromium_src override
- Brave's mechanism: drop a file in
brave/chromium_src/to replace the upstream equivalent. - linux-dmabuf
- Wayland protocol for zero-copy buffer sharing via dma-buf file descriptors.
- kiosk mode
- Chromium mode hiding all UI chrome (address bar, tabs, menus) and going fullscreen.