diff --git a/images/README.md b/images/README.md
new file mode 100644
index 00000000..0a2462ac
--- /dev/null
+++ b/images/README.md
@@ -0,0 +1,15 @@
+## How to test kernel-images changes locally with docker
+
+- Make relevant changes to kernel-images example adding a new endpoint at `kernel-images/server/cmd/api/api/computer.go`, example I added `SetCursor()` endpoint.
+- Run openApi to generate the boilerplate for the new endpoints with make oapi-generate
+- Check changes at `kernel-images/server/lib/oapi/oapi.go`
+- `cd kernel-images/images/chromium-headful`
+- Build and run the docker image with `./build-docker.sh && ENABLE_WEBRTC=true ./run-docker.sh`
+- Open http://localhost:8080/ in your browser
+- Now new endpoint should be available for tests example curl command:
+```sh
+curl -X POST localhost:444/computer/cursor \
+ -H "Content-Type: application/json" \
+ -d '{"hidden": true}'
+```
+
diff --git a/images/chromium-headful/Dockerfile b/images/chromium-headful/Dockerfile
index 4e87c9a7..031d5545 100644
--- a/images/chromium-headful/Dockerfile
+++ b/images/chromium-headful/Dockerfile
@@ -1,51 +1,69 @@
FROM docker.io/golang:1.25.0 AS server-builder
WORKDIR /workspace/server
-ARG TARGETOS
+# Allow cross-compilation when building with BuildKit platforms
ARG TARGETARCH
+ARG TARGETOS
+ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-golang1250
ENV CGO_ENABLED=0
COPY server/go.mod ./
COPY server/go.sum ./
-RUN --mount=type=cache,target=/root/.cache/go-build \
- --mount=type=cache,target=/go/pkg/mod \
+RUN --mount=type=cache,target=/root/.cache/go-build,id=$CACHEIDPREFIX-go-build \
+ --mount=type=cache,target=/go/pkg/mod,id=$CACHEIDPREFIX-go-pkg-mod \
go mod download
COPY server/ .
# Build kernel-images API
-RUN --mount=type=cache,target=/root/.cache/go-build \
- --mount=type=cache,target=/go/pkg/mod \
+RUN --mount=type=cache,target=/root/.cache/go-build,id=$CACHEIDPREFIX-go-build \
+ --mount=type=cache,target=/go/pkg/mod,id=$CACHEIDPREFIX-go-pkg-mod \
GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
go build -ldflags="-s -w" -o /out/kernel-images-api ./cmd/api
# Build chromium launcher
-RUN GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
+RUN --mount=type=cache,target=/root/.cache/go-build,id=$CACHEIDPREFIX-go-build \
+ --mount=type=cache,target=/go/pkg/mod,id=$CACHEIDPREFIX-go-pkg-mod \
+ GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
go build -ldflags="-s -w" -o /out/chromium-launcher ./cmd/chromium-launcher
# webrtc client
FROM node:22-bullseye-slim AS client
WORKDIR /src
+
+# Allow cross-compilation when building with BuildKit platforms
+ARG TARGETARCH
+ARG TARGETOS
+ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-node22bullseye
+
COPY images/chromium-headful/client/package*.json ./
-RUN --mount=type=cache,target=/root/.npm npm install
+RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install
COPY images/chromium-headful/client/ .
-RUN --mount=type=cache,target=/root/.npm npm run build
+RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm run build
# xorg dependencies
FROM docker.io/ubuntu:22.04 AS xorg-deps
WORKDIR /xorg
+
+# Allow cross-compilation when building with BuildKit platforms
+ARG TARGETARCH
+ARG TARGETOS
+ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-ubuntu2204
+
ENV DEBIAN_FRONTEND=noninteractive
-RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \
- --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \
+ set -eux; \
rm -f /etc/apt/apt.conf.d/docker-clean; \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \
- set -eux; \
apt-get update; \
apt-get --no-install-recommends -y install \
git gcc pkgconf autoconf automake libtool make xorg-dev xutils-dev;
COPY images/chromium-headful/xorg-deps/ /xorg/
# build xf86-video-dummy v0.3.8 with RandR support
-RUN set -eux; \
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \
+ set -eux; \
cd xf86-video-dummy/v0.3.8; \
patch -p1 < ../01_v0.3.8_xdummy-randr.patch; \
autoreconf -v --install; \
@@ -53,23 +71,96 @@ RUN set -eux; \
make -j$(nproc); \
make install;
# build custom input driver
-RUN set -eux; \
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \
+ set -eux; \
cd xf86-input-neko; \
./autogen.sh --prefix=/usr; \
./configure; \
make -j$(nproc); \
make install;
+FROM docker.io/ubuntu:22.04 AS ffmpeg-downloader
+
+# Allow cross-compilation when building with BuildKit platforms
+ARG TARGETARCH
+ARG TARGETOS
+ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-ubuntu2204
+
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \
+ set -xe; \
+ rm -f /etc/apt/apt.conf.d/docker-clean; \
+ echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \
+ apt-get -yqq update; \
+ apt-get -yqq --no-install-recommends install ca-certificates curl xz-utils;
+
+# Download FFmpeg (latest static build) for the recording server
+RUN --mount=type=cache,target=/tmp/cache/ffmpeg,sharing=locked,id=$CACHEIDPREFIX-ffmpeg \
+ <<-'EOT'
+ set -eux
+ FFMPEG_CACHE_PATH="/tmp/cache/ffmpeg"
+ case ${TARGETARCH:-amd64} in
+ "amd64") FFMPEG_TARGET_ARCH="64" ;;
+ "arm64") FFMPEG_TARGET_ARCH="arm64" ;;
+ esac
+ FFMPEG_TARGET=linux${FFMPEG_TARGET_ARCH:?}
+ ARCHIVE_NAME="ffmpeg-n7.1-latest-${FFMPEG_TARGET}-gpl-7.1.tar.xz"
+ FFMPEG_CACHED_ARCHIVE_PATH="$FFMPEG_CACHE_PATH/$ARCHIVE_NAME"
+ FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH="$FFMPEG_CACHED_ARCHIVE_PATH.sha256"
+ URL="https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/$ARCHIVE_NAME"
+ TEMPORARY_SHA256_CHECKSUM_PATH=$(mktemp /tmp/tmp_sha256.XXXXXXXXXX)
+ CONTINUE="true"
+ echo "Downloading FFmpeg checksum"
+ if curl --connect-timeout 10 -fsSL "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/checksums.sha256" -o $TEMPORARY_SHA256_CHECKSUM_PATH; then
+ grep -F "$ARCHIVE_NAME" $TEMPORARY_SHA256_CHECKSUM_PATH > $FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH
+ else
+ echo "Failed to connect to ffmpeg static build provider for checksum."
+ echo "Checking for cached version to use."
+ if [ -f "$FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH" ]; then
+ echo "Found cached checksum."
+ else
+ echo "Unable to locate cached checksum."
+ CONTINUE="false"
+ fi
+ fi
+ rm $TEMPORARY_SHA256_CHECKSUM_PATH
+
+ if [ "$CONTINUE" = "false" ]; then
+ exit 1
+ fi
+
+ echo "Checking cache for FFmpeg archive and validating checksum"
+ if (cd $FFMPEG_CACHE_PATH && sha256sum --check $FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH); then
+ echo "Checksum validated, using cached FFmpeg archive"
+ else
+ echo "Downloading FFmpeg static build from $URL"
+ curl -fsSL "$URL" -o $FFMPEG_CACHED_ARCHIVE_PATH
+ echo "Validating checksum of FFmpeg static build download"
+ (cd $FFMPEG_CACHE_PATH && sha256sum --check $FFMPEG_CACHED_ARCHIVE_CHECKSUM_PATH)
+ fi
+
+ tar -xJf $FFMPEG_CACHED_ARCHIVE_PATH -C /tmp
+ install -m755 /tmp/ffmpeg-*/bin/ffmpeg /usr/local/bin/ffmpeg
+ install -m755 /tmp/ffmpeg-*/bin/ffprobe /usr/local/bin/ffprobe
+ rm -rf /tmp/ffmpeg*
+EOT
+
FROM ghcr.io/onkernel/neko/base:3.0.8-v1.3.0 AS neko
# ^--- now has event.SYSTEM_PONG with legacy support to keepalive
FROM node:22-bullseye-slim AS node-22
FROM docker.io/ubuntu:22.04
+# Allow cross-compilation when building with BuildKit platforms
+ARG TARGETARCH
+ARG TARGETOS
+ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-ubuntu2204
+
ENV DEBIAN_FRONTEND=noninteractive
ENV DEBIAN_PRIORITY=high
-RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \
- --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \
rm -f /etc/apt/apt.conf.d/docker-clean; \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \
apt-get update && \
@@ -119,50 +210,21 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptca
fonts-nanum \
fontconfig \
unzip && \
- apt-get clean && fc-cache -f
+ fc-cache -f
# install ffmpeg manually since the version available in apt is from the 4.x branch due to #drama.
-# as of writing these static builds will be the latest 7.0.x release.
-RUN --mount=type=cache,target=/tmp/cache/ffmpeg,id=ffmpeg \
- <<-'EOT'
- set -eux
- URL="https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz"
- echo "Downloading FFmpeg MD5 checksum"
- if ! curl --connect-timeout 10 -fsSL "${URL}.md5" -o /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5; then
- echo "Failed to connect to ffmpeg static build provider for MD5 checksum."
- echo "Checking for cached version to use."
- if [ ! -f /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5 ]; then
- echo "Unable to locate cached MD5 checksum. Exiting."
- exit 1
- else
- echo "Found cached MD5 checksum."
- fi
- fi
- sed -i -e 's/ .*$/ \/tmp\/cache\/ffmpeg\/ffmpeg.tar.xz/' /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5
- echo "Checking cache for FFmpeg archive and validating MD5 checksum"
- if md5sum --check /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5; then
- echo "Checksum validated, using cached FFmpeg archive"
- else
- echo "Downloading FFmpeg static build from $URL"
- curl -fsSL "$URL" -o /tmp/cache/ffmpeg/ffmpeg.tar.xz
- echo "Validating MD5 checksum of FFmpeg static build download"
- md5sum --check /tmp/cache/ffmpeg/ffmpeg.tar.xz.md5
- fi
- tar -xJf /tmp/cache/ffmpeg/ffmpeg.tar.xz -C /tmp
- install -m755 /tmp/ffmpeg-*/ffmpeg /usr/local/bin/ffmpeg
- install -m755 /tmp/ffmpeg-*/ffprobe /usr/local/bin/ffprobe
- rm -rf /tmp/ffmpeg*
-EOT
+COPY --from=ffmpeg-downloader /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
+COPY --from=ffmpeg-downloader /usr/local/bin/ffprobe /usr/local/bin/ffprobe
# runtime
ENV USERNAME=root
-RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \
- --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \
set -eux; \
apt-get update; \
apt-get --no-install-recommends -y install \
- wget ca-certificates python2 supervisor xclip xdotool \
- pulseaudio dbus-x11 xserver-xorg-video-dummy \
+ wget ca-certificates python2 supervisor xclip xdotool unclutter \
+ pulseaudio dbus-x11 xserver-xorg-video-dummy rtkit upower \
libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx7 \
x11-xserver-utils \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
@@ -187,14 +249,15 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptca
/home/$USERNAME/.local/share/xorg; \
chmod 1777 /var/log/neko; \
chown $USERNAME /var/log/neko/ /tmp/runtime-$USERNAME; \
- chown -R $USERNAME:$USERNAME /home/$USERNAME;
+ chown -R $USERNAME:$USERNAME /home/$USERNAME; \
+ chmod 777 /etc/pulse;
# install chromium and sqlite3 for debugging the cookies file
-RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \
- --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \
add-apt-repository -y ppa:xtradeb/apps;
-RUN --mount=type=cache,target=/var/cache/apt,sharing=private,id=ubuntu2204-aptcache \
- --mount=type=cache,target=/var/lib/apt,sharing=private,id=ubuntu2204-aptlib \
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-apt-cache \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked,id=$CACHEIDPREFIX-apt-lib \
apt update -y && \
apt -y install chromium && \
apt --no-install-recommends -y install sqlite3;
@@ -214,8 +277,8 @@ RUN set -eux; \
ln -sf /usr/local/lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack; \
fi
-# Install TypeScript and Playwright globally
-RUN --mount=type=cache,target=/root/.npm npm install -g typescript playwright-core tsx
+# Install TypeScript, Playwright, Patchright globally
+RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install -g typescript playwright-core patchright tsx
# setup desktop env & app
ENV DISPLAY_NUM=1
@@ -225,6 +288,10 @@ ENV WITHDOCKER=true
COPY images/chromium-headful/xorg.conf /etc/neko/xorg.conf
COPY images/chromium-headful/neko.yaml /etc/neko/neko.yaml
+COPY images/chromium-headful/default.pa /etc/pulse/default.pa
+COPY images/chromium-headful/daemon.conf /etc/pulse/daemon.conf
+COPY images/chromium-headful/dbus-pulseaudio.conf /etc/dbus-1/system.d/pulseaudio.conf
+COPY images/chromium-headful/dbus-mpris.conf /etc/dbus-1/system.d/mpris.conf
COPY --from=neko /usr/bin/neko /usr/bin/neko
COPY --from=client /src/dist/ /var/www
COPY --from=xorg-deps /usr/local/lib/xorg/modules/drivers/dummy_drv.so /usr/lib/xorg/modules/drivers/dummy_drv.so
@@ -244,6 +311,11 @@ COPY --from=server-builder /out/chromium-launcher /usr/local/bin/chromium-launch
# Copy the Playwright executor runtime
COPY server/runtime/playwright-executor.ts /usr/local/lib/playwright-executor.ts
-RUN useradd -m -s /bin/bash kernel
+RUN useradd -m -s /bin/bash kernel && \
+ usermod -aG audio,video,pulse,pulse-access kernel
+
+# Environment variables for audio
+ENV XDG_RUNTIME_DIR=/tmp/runtime-kernel
+ENV PULSE_SERVER=unix:/tmp/runtime-kernel/pulse/native
ENTRYPOINT [ "/wrapper.sh" ]
diff --git a/images/chromium-headful/build-unikernel.sh b/images/chromium-headful/build-unikernel.sh
index ab860b43..7730468a 100755
--- a/images/chromium-headful/build-unikernel.sh
+++ b/images/chromium-headful/build-unikernel.sh
@@ -3,7 +3,7 @@
# Move to the script's directory so relative paths work regardless of the caller CWD
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
cd "$SCRIPT_DIR"
-source "$SCRIPT_DIR/../../shared/ensure-common-build-run-vars.sh" chromium-headful
+source "$SCRIPT_DIR/../../shared/ensure-common-build-run-vars.sh" chromium-headful require-ukc-vars
source "$SCRIPT_DIR/../../shared/erofs-utils.sh"
# Ensure the mkfs.erofs tool is available
@@ -25,6 +25,8 @@ docker cp cnt-"$app_name":/ ./.rootfs
rm -f initrd || true
sudo mkfs.erofs --all-root -d2 -E noinline_data -b 4096 initrd ./.rootfs
+echo "Image index/name: $UKC_INDEX/$IMAGE"
+
# Package the unikernel (and the new initrd) to KraftCloud
kraft pkg \
--name $UKC_INDEX/$IMAGE \
diff --git a/images/chromium-headful/client/Dockerfile b/images/chromium-headful/client/Dockerfile
index 85e77c36..44b9eccf 100644
--- a/images/chromium-headful/client/Dockerfile
+++ b/images/chromium-headful/client/Dockerfile
@@ -3,15 +3,20 @@ FROM $BASE_IMAGE AS client
WORKDIR /src
+# Allow cross-compilation when building with BuildKit platforms
+ARG TARGETARCH
+ARG TARGETOS
+ARG CACHEIDPREFIX=${TARGETOS:-linux}-${TARGETARCH:-amd64}-node18bullseye
+
#
# install dependencies
COPY package*.json ./
-RUN --mount=type=cache,target=/root/.npm npm install
+RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm install
#
# build client
COPY . .
-RUN --mount=type=cache,target=/root/.npm npm run build
+RUN --mount=type=cache,target=/root/.npm,id=$CACHEIDPREFIX-npm npm run build
#
# artifacts from this stage
diff --git a/images/chromium-headful/client/public/browserconfig.xml b/images/chromium-headful/client/public/browserconfig.xml
index ededce1f..0fd3ece4 100644
--- a/images/chromium-headful/client/public/browserconfig.xml
+++ b/images/chromium-headful/client/public/browserconfig.xml
@@ -3,7 +3,7 @@