diff --git a/Dockerfile b/Dockerfile
index 1a03b08..50b68dd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,7 +4,11 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ENV DEBIAN_FRONTEND=noninteractive
ENV EDITOR=nvim \
- VISUAL=nvim
+ VISUAL=nvim \
+ XDG_CONFIG_HOME=/home/node/.config \
+ XDG_CACHE_HOME=/home/node/.cache \
+ XDG_DATA_HOME=/home/node/.local/share \
+ XDG_STATE_HOME=/home/node/.local/state
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
@@ -42,7 +46,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
neovim \
nodejs \
novnc \
- openbox \
openssh-server \
pipx \
procps \
@@ -67,8 +70,39 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
x11-xserver-utils \
x11vnc \
xauth \
- xterm \
xvfb \
+ xfce4-session \
+ xfwm4 \
+ xfdesktop4 \
+ xfce4-settings \
+ xfce4-terminal \
+ thunar \
+ plank \
+ autocutsel \
+ greybird-gtk-theme \
+ elementary-xfce-icon-theme \
+ fonts-noto-core \
+ fonts-noto-color-emoji \
+ dbus-user-session \
+ xclip \
+ && rm -rf /var/lib/apt/lists/*
+
+# Chromium: Ubuntu 24.04 only ships a snap stub, so pull the real .deb from
+# the Debian Sid repo (pinned low so it only satisfies chromium itself).
+RUN printf '%s\n' \
+ "deb [arch=amd64] http://deb.debian.org/debian sid main" \
+ >/etc/apt/sources.list.d/debian-sid.list \
+ && printf '%s\n' \
+ "Package: *" \
+ "Pin: release a=unstable" \
+ "Pin-Priority: 10" \
+ "" \
+ "Package: chromium chromium-common chromium-sandbox" \
+ "Pin: release a=unstable" \
+ "Pin-Priority: 500" \
+ >/etc/apt/preferences.d/chromium \
+ && apt-get update \
+ && apt-get install -y --no-install-recommends chromium \
&& rm -rf /var/lib/apt/lists/*
RUN useradd --create-home --shell /bin/bash node \
@@ -84,6 +118,11 @@ RUN useradd --create-home --shell /bin/bash node \
&& ln -sf /usr/bin/nvim /usr/local/bin/vim \
&& ln -sf /usr/bin/nvim /usr/local/bin/vi
+COPY desktop/assets /opt/desktop/assets
+COPY desktop/xfce /opt/desktop/xfce
+COPY desktop/plank /opt/desktop/plank
+COPY desktop/scripts /opt/desktop/scripts
+
COPY sshd_config /etc/ssh/sshd_config
COPY microagent-init.sh /usr/local/bin/microagent-init
COPY microagent-desktop-session.sh /usr/local/bin/microagent-desktop-session
@@ -95,6 +134,7 @@ COPY terminfo/xterm-ghostty.terminfo /tmp/xterm-ghostty.terminfo
COPY terminfo/xterm-kitty.terminfo /tmp/xterm-kitty.terminfo
RUN chmod 755 /usr/local/bin/microagent-init /usr/local/bin/microagent-desktop-session /usr/local/bin/microagent-network-up \
+ && chmod 755 /opt/desktop/scripts/apply-desktop-profile.sh \
&& chown node:node /home/node/.zshrc /home/node/.bashrc /home/node/.profile \
&& usermod -s /usr/bin/zsh node \
&& install -d /opt/zsh/pure \
diff --git a/defaults/.bashrc b/defaults/.bashrc
index 3b2c3aa..e6b6ced 100644
--- a/defaults/.bashrc
+++ b/defaults/.bashrc
@@ -27,7 +27,6 @@ computer_prompt_name() {
export EDITOR="${EDITOR:-nvim}"
export VISUAL="${VISUAL:-nvim}"
alias vim='nvim'
-alias vi='nvim'
alias ls='eza --group-directories-first --icons=auto'
alias la='eza -a --group-directories-first --icons=auto'
alias ll='eza -lah --git --group-directories-first --icons=auto'
diff --git a/defaults/.zshrc b/defaults/.zshrc
index 88e6ee2..abe6926 100644
--- a/defaults/.zshrc
+++ b/defaults/.zshrc
@@ -48,7 +48,6 @@ fi
export EDITOR="${EDITOR:-nvim}"
export VISUAL="${VISUAL:-nvim}"
alias vim='nvim'
-alias vi='nvim'
alias ls='eza --group-directories-first --icons=auto'
alias la='eza -a --group-directories-first --icons=auto'
alias ll='eza -lah --git --group-directories-first --icons=auto'
diff --git a/desktop/assets/wallpaper.png b/desktop/assets/wallpaper.png
new file mode 100644
index 0000000..8e07274
Binary files /dev/null and b/desktop/assets/wallpaper.png differ
diff --git a/desktop/plank/dock1/launchers/chromium.dockitem b/desktop/plank/dock1/launchers/chromium.dockitem
new file mode 100644
index 0000000..2884b73
--- /dev/null
+++ b/desktop/plank/dock1/launchers/chromium.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/chromium.desktop
diff --git a/desktop/plank/dock1/launchers/thunar.dockitem b/desktop/plank/dock1/launchers/thunar.dockitem
new file mode 100644
index 0000000..1bfd173
--- /dev/null
+++ b/desktop/plank/dock1/launchers/thunar.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/thunar.desktop
diff --git a/desktop/plank/dock1/launchers/xfce4-terminal.dockitem b/desktop/plank/dock1/launchers/xfce4-terminal.dockitem
new file mode 100644
index 0000000..2fc64b3
--- /dev/null
+++ b/desktop/plank/dock1/launchers/xfce4-terminal.dockitem
@@ -0,0 +1,2 @@
+[PlankDockItemPreferences]
+Launcher=file:///usr/share/applications/xfce4-terminal.desktop
diff --git a/desktop/plank/dock1/settings b/desktop/plank/dock1/settings
new file mode 100644
index 0000000..4f4fa1a
--- /dev/null
+++ b/desktop/plank/dock1/settings
@@ -0,0 +1,21 @@
+[PlankDockPreferences]
+Alignment=3
+AutoPinning=true
+CurrentWorkspaceOnly=false
+DockItems=xfce4-terminal.dockitem;;thunar.dockitem;;chromium.dockitem
+HideDelay=0
+HideMode=3
+IconSize=48
+ItemsAlignment=3
+LockItems=true
+Monitor=
+Offset=0
+PinnedOnly=false
+Position=3
+PressureReveal=false
+ShowDockItem=false
+Theme=Transparent
+TooltipsEnabled=true
+UnhideDelay=0
+ZoomEnabled=true
+ZoomPercent=150
diff --git a/desktop/scripts/apply-desktop-profile.sh b/desktop/scripts/apply-desktop-profile.sh
new file mode 100644
index 0000000..1fb84a1
--- /dev/null
+++ b/desktop/scripts/apply-desktop-profile.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+: "${HOME:=/home/node}"
+: "${XDG_CONFIG_HOME:=$HOME/.config}"
+: "${XDG_STATE_HOME:=$HOME/.local/state}"
+
+PROFILE_VERSION="v1"
+PROFILE_ROOT="/opt/desktop"
+MARKER_DIR="$XDG_STATE_HOME/microagent/desktop"
+MARKER_FILE="$MARKER_DIR/desktop-${PROFILE_VERSION}.seeded"
+
+if [ -f "$MARKER_FILE" ]; then
+ exit 0
+fi
+
+mkdir -p "$MARKER_DIR" "$XDG_CONFIG_HOME"
+
+# XFCE config
+rm -rf "$XDG_CONFIG_HOME/xfce4"
+cp -R "$PROFILE_ROOT/xfce" "$XDG_CONFIG_HOME/xfce4"
+
+# Plank config
+mkdir -p "$XDG_CONFIG_HOME/plank"
+cp -R "$PROFILE_ROOT/plank/." "$XDG_CONFIG_HOME/plank/"
+
+touch "$MARKER_FILE"
+echo "Applied desktop profile: ${PROFILE_VERSION}"
diff --git a/desktop/xfce/terminal/terminalrc b/desktop/xfce/terminal/terminalrc
new file mode 100644
index 0000000..a1fbcaf
--- /dev/null
+++ b/desktop/xfce/terminal/terminalrc
@@ -0,0 +1,3 @@
+[Configuration]
+FontName=DejaVu Sans Mono 11
+FontUseSystem=FALSE
diff --git a/desktop/xfce/xfconf/xfce-perchannel-xml/xfce4-desktop.xml b/desktop/xfce/xfconf/xfce-perchannel-xml/xfce4-desktop.xml
new file mode 100644
index 0000000..de5fe63
--- /dev/null
+++ b/desktop/xfce/xfconf/xfce-perchannel-xml/xfce4-desktop.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/desktop/xfce/xfconf/xfce-perchannel-xml/xfce4-panel.xml b/desktop/xfce/xfconf/xfce-perchannel-xml/xfce4-panel.xml
new file mode 100644
index 0000000..60e3c23
--- /dev/null
+++ b/desktop/xfce/xfconf/xfce-perchannel-xml/xfce4-panel.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/desktop/xfce/xfconf/xfce-perchannel-xml/xfwm4.xml b/desktop/xfce/xfconf/xfce-perchannel-xml/xfwm4.xml
new file mode 100644
index 0000000..d685c45
--- /dev/null
+++ b/desktop/xfce/xfconf/xfce-perchannel-xml/xfwm4.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/desktop/xfce/xfconf/xfce-perchannel-xml/xsettings.xml b/desktop/xfce/xfconf/xfce-perchannel-xml/xsettings.xml
new file mode 100644
index 0000000..3ee9248
--- /dev/null
+++ b/desktop/xfce/xfconf/xfce-perchannel-xml/xsettings.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/microagent-desktop-session.sh b/microagent-desktop-session.sh
index 992450c..2bb1aa5 100644
--- a/microagent-desktop-session.sh
+++ b/microagent-desktop-session.sh
@@ -2,6 +2,10 @@
set -uo pipefail
export DISPLAY=:0
+export DESKTOP_SESSION=xfce
+export XDG_CURRENT_DESKTOP=XFCE
+export XDG_SESSION_DESKTOP=xfce
+export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-/home/node/.config}"
log() {
printf '[microagent-desktop] %s\n' "$*" >&2
@@ -23,31 +27,38 @@ cleanup() {
trap - INT TERM
[ -n "${websockify_pid:-}" ] && kill "$websockify_pid" >/dev/null 2>&1 || true
[ -n "${x11vnc_pid:-}" ] && kill "$x11vnc_pid" >/dev/null 2>&1 || true
- [ -n "${xterm_pid:-}" ] && kill "$xterm_pid" >/dev/null 2>&1 || true
- [ -n "${openbox_pid:-}" ] && kill "$openbox_pid" >/dev/null 2>&1 || true
+ [ -n "${plank_pid:-}" ] && kill "$plank_pid" >/dev/null 2>&1 || true
+ [ -n "${xfce_pid:-}" ] && kill "$xfce_pid" >/dev/null 2>&1 || true
[ -n "${xvfb_pid:-}" ] && kill "$xvfb_pid" >/dev/null 2>&1 || true
wait >/dev/null 2>&1 || true
exit 0
}
-start_openbox() {
- reap_if_needed "${openbox_pid:-}"
- log "starting openbox"
- runuser -u node -- env DISPLAY="$DISPLAY" openbox >>/tmp/openbox.log 2>&1 &
- openbox_pid=$!
+start_xfce() {
+ reap_if_needed "${xfce_pid:-}"
+ log "starting xfce4-session"
+ runuser -u node -- env \
+ DISPLAY="$DISPLAY" \
+ DESKTOP_SESSION="$DESKTOP_SESSION" \
+ XDG_CURRENT_DESKTOP="$XDG_CURRENT_DESKTOP" \
+ XDG_SESSION_DESKTOP="$XDG_SESSION_DESKTOP" \
+ XDG_CONFIG_HOME="$XDG_CONFIG_HOME" \
+ dbus-launch --exit-with-session xfce4-session >>/tmp/xfce.log 2>&1 &
+ xfce_pid=$!
}
-start_xterm() {
- reap_if_needed "${xterm_pid:-}"
- log "starting xterm"
- runuser -u node -- env DISPLAY="$DISPLAY" xterm -fa Monospace -fs 12 >>/tmp/xterm.log 2>&1 &
- xterm_pid=$!
+start_plank() {
+ reap_if_needed "${plank_pid:-}"
+ log "starting plank"
+ runuser -u node -- env DISPLAY="$DISPLAY" XDG_CONFIG_HOME="$XDG_CONFIG_HOME" \
+ plank >>/tmp/plank.log 2>&1 &
+ plank_pid=$!
}
start_x11vnc() {
reap_if_needed "${x11vnc_pid:-}"
log "starting x11vnc"
- x11vnc -display "$DISPLAY" -rfbport 5900 -forever -shared -nopw >>/tmp/x11vnc.log 2>&1 &
+ x11vnc -display "$DISPLAY" -rfbport 5900 -forever -shared -nopw -quiet >>/tmp/x11vnc.log 2>&1 &
x11vnc_pid=$!
}
@@ -60,8 +71,13 @@ start_websockify() {
trap cleanup INT TERM
+# Apply desktop profile on first boot
+log "applying desktop profile"
+runuser -u node -- /opt/desktop/scripts/apply-desktop-profile.sh 2>&1 || true
+
+# Start Xvfb
log "starting Xvfb"
-Xvfb "$DISPLAY" -screen 0 1280x800x24 >/tmp/xvfb.log 2>&1 &
+Xvfb "$DISPLAY" -screen 0 1280x800x24 -ac >/tmp/xvfb.log 2>&1 &
xvfb_pid=$!
ready=0
@@ -83,24 +99,37 @@ if [ "$ready" -ne 1 ]; then
exit 1
fi
-start_openbox
-start_xterm
+# Disable screensaver/DPMS
+xset -display "$DISPLAY" -dpms s off s noblank >/dev/null 2>&1 || true
+
+# Start desktop stack
+start_xfce
+sleep 2
+start_plank
start_x11vnc
start_websockify
+# Clipboard sync
+if command -v autocutsel >/dev/null 2>&1; then
+ runuser -u node -- env DISPLAY="$DISPLAY" autocutsel -selection CLIPBOARD -fork >>/tmp/autocutsel.log 2>&1 || true
+ runuser -u node -- env DISPLAY="$DISPLAY" autocutsel -selection PRIMARY -fork >>/tmp/autocutsel.log 2>&1 || true
+fi
+
+# Monitor and restart dead processes
while true; do
if ! pid_running "$xvfb_pid"; then
log "Xvfb exited; stopping desktop session"
wait "$xvfb_pid" >/dev/null 2>&1 || true
exit 1
fi
- if ! pid_running "${openbox_pid:-}"; then
- log "openbox exited; restarting"
- start_openbox
+ if ! pid_running "${xfce_pid:-}"; then
+ log "xfce4-session exited; restarting"
+ start_xfce
+ sleep 2
fi
- if ! pid_running "${xterm_pid:-}"; then
- log "xterm exited; restarting"
- start_xterm
+ if ! pid_running "${plank_pid:-}"; then
+ log "plank exited; restarting"
+ start_plank
fi
if ! pid_running "${x11vnc_pid:-}"; then
log "x11vnc exited; restarting"
diff --git a/microagent-init.sh b/microagent-init.sh
index 284a43e..bd3e164 100644
--- a/microagent-init.sh
+++ b/microagent-init.sh
@@ -96,6 +96,11 @@ if [ -f /etc/microagent/authorized_keys ]; then
install -m 0600 -o node -g node /etc/microagent/authorized_keys /home/node/.ssh/authorized_keys
fi
+if [ -f /etc/microagent/trusted_user_ca_keys ]; then
+ log "using injected trusted user CA keys"
+ chmod 0644 /etc/microagent/trusted_user_ca_keys
+fi
+
if command -v jitterentropy-rngd >/dev/null 2>&1; then
log "starting jitterentropy-rngd"
jitterentropy-rngd -v >/var/log/jitterentropy.log 2>&1 &
diff --git a/sshd_config b/sshd_config
index 27a7e83..caebe74 100644
--- a/sshd_config
+++ b/sshd_config
@@ -10,6 +10,7 @@ PasswordAuthentication no
KbdInteractiveAuthentication no
UsePAM no
AuthorizedKeysFile .ssh/authorized_keys
+TrustedUserCAKeys /etc/microagent/trusted_user_ca_keys
StrictModes no
SyslogFacility AUTH