nix: hyperhive.westonRdp.enable — opt-in weston + RDP backend per agent

This commit is contained in:
damocles 2026-05-20 11:20:37 +02:00
parent 49f4e9cc89
commit ba04a5a360
3 changed files with 120 additions and 0 deletions

View file

@ -0,0 +1,97 @@
{
pkgs,
lib,
config,
...
}:
{
# Optional Weston (the reference Wayland compositor) with the RDP
# backend, surfaced as a per-agent hyperhive option. An agent turns
# it on from its own `agent.nix`:
#
# hyperhive.westonRdp.enable = true;
#
# Imported by `harness-base.nix`, so every sub-agent + the manager
# has the option available; only those that flip it on get the
# service. This is a flat per-agent option (evaluated inside that
# agent's own container build) — NOT a `hyperhive.agents.<name>.*`
# registry, which can't work: each agent is its own
# nixosConfiguration and has no cross-agent view.
options.hyperhive.westonRdp.enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Run Weston with the RDP backend as a systemd service, for
remote-desktop access to the agent's container. A self-signed
TLS cert/key pair is generated on first start under
`/var/lib/weston`. Renders in software (pixman) no GPU, DRM,
or VT access, so no extra container capabilities are needed.
The unit is deliberately built so enabling it can NEVER abort
the agent's `nixos-container update`: `Type = "simple"` (so
`switch-to-configuration` doesn't block on weston readiness)
and an `ExecStartPre` that always exits 0. A misconfigured
weston degrades to a restart loop visible in `journalctl` it
does not block the rebuild. (Same reasoning as the `tea-login`
unit in `harness-base.nix`.)
Host networking means the RDP port (3389) is reachable on the
host. With more than one RDP-enabled agent the port would
collide add a port knob to this option before that happens.
'';
};
config = lib.mkIf config.hyperhive.westonRdp.enable {
systemd.services.weston = {
description = "Weston Wayland compositor (RDP backend)";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
# `simple`, not `notify`: switch-to-configuration must not
# wait on weston signalling readiness. A `notify` unit that
# never sends READY=1 fails after TimeoutStartSec, which
# aborts `nixos-container update` on every reload — exactly
# the trap `tea-login` documents.
Type = "simple";
# Creates /var/lib/weston (0700 root) before ExecStartPre.
StateDirectory = "weston";
Environment = "XDG_RUNTIME_DIR=/run/user/0";
# Runtime dir + first-boot cert generation. No `set -e`, and
# it always exits 0: a cert hiccup must not fail the unit
# start (that would abort the rebuild). If the cert/key end
# up missing, weston's own ExecStart fails into the Restart
# loop instead — graceful degradation, not a blocked deploy.
ExecStartPre = pkgs.writeShellScript "weston-rdp-setup" ''
mkdir -p /run/user/0 && chmod 700 /run/user/0 || true
CERT=/var/lib/weston/weston-rdp.crt
KEY=/var/lib/weston/weston-rdp.key
if [ ! -f "$CERT" ] || [ ! -f "$KEY" ]; then
if ${pkgs.openssl}/bin/openssl req -x509 -newkey rsa:2048 \
-keyout "$KEY" -out "$CERT" -days 3650 -nodes \
-subj "/CN=localhost" 2>/dev/null; then
chmod 600 "$KEY" 2>/dev/null || true
chmod 644 "$CERT" 2>/dev/null || true
else
echo "weston-rdp-setup: cert generation failed; weston will retry"
fi
fi
exit 0
'';
ExecStart = lib.concatStringsSep " " [
"${pkgs.weston}/bin/weston"
"--backend=rdp-backend.so"
"--renderer=pixman"
"--rdp-tls-cert=/var/lib/weston/weston-rdp.crt"
"--rdp-tls-key=/var/lib/weston/weston-rdp.key"
];
Restart = "on-failure";
RestartSec = "5s";
};
};
# weston on the agent's interactive PATH too, so claude can run
# Wayland clients / `weston-info` against the compositor.
environment.systemPackages = [ pkgs.weston ];
};
}