From ba04a5a360957a4ca58ff03910fb4119f79e4927 Mon Sep 17 00:00:00 2001 From: damocles Date: Wed, 20 May 2026 11:20:37 +0200 Subject: [PATCH] =?UTF-8?q?nix:=20hyperhive.westonRdp.enable=20=E2=80=94?= =?UTF-8?q?=20opt-in=20weston=20+=20RDP=20backend=20per=20agent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 18 +++++++ nix/templates/harness-base.nix | 5 ++ nix/templates/weston-rdp.nix | 97 ++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 nix/templates/weston-rdp.nix diff --git a/CLAUDE.md b/CLAUDE.md index c60a726..b306ca1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -130,6 +130,8 @@ nix/ templates/harness-base.nix shared scaffolding for sub-agents + manager templates/agent-base.nix sub-agent nixosConfiguration templates/manager.nix manager nixosConfiguration + templates/weston-rdp.nix optional `hyperhive.westonRdp.enable` + — weston + RDP backend systemd unit docs/ conventions.md naming, identity=socket, async forms, commit style @@ -183,6 +185,22 @@ read them à la carte. In-flight or recent context that hasn't earned a section yet. Prune freely. +- **Just landed:** `hyperhive.westonRdp.enable` option. New + `nix/templates/weston-rdp.nix` declares a per-agent bool; + enabling it runs weston with the RDP backend as a systemd + service (software/pixman render, self-signed TLS cert + generated first-boot under `/var/lib/weston`). Imported by + `harness-base.nix` so every agent has the option; an agent + opts in from its own `agent.nix`. Design note: it's a FLAT + per-agent option, not `hyperhive.agents..*` — each + agent is its own nixosConfiguration with no cross-agent + view, so the `` indirection is meaningless. The unit + is `Type = "simple"` with an always-exit-0 `ExecStartPre` so + it can never abort `nixos-container update` (a `Type=notify` + weston that never signals READY would fail activation every + reload — the trap `tea-login` documents). A misconfigured + weston degrades to a restart loop in `journalctl`, not a + blocked rebuild. - **Just landed:** `get_logs` now resolves the machine name. `journalctl -M` wants the *machine* name (`h-gui`), not the logical agent name (`gui`) — `get_logs` was the one manager diff --git a/nix/templates/harness-base.nix b/nix/templates/harness-base.nix index 26d059e..d160192 100644 --- a/nix/templates/harness-base.nix +++ b/nix/templates/harness-base.nix @@ -10,6 +10,11 @@ # this. The systemd service that actually runs the harness binary # differs per role and lives in the child module. + # Optional feature modules. Each declares its own `hyperhive.*` + # option(s), default-off, so every agent has them available but + # only opts in from its own `agent.nix`. + imports = [ ./weston-rdp.nix ]; + options.hyperhive.allowedRecipients = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ ]; diff --git a/nix/templates/weston-rdp.nix b/nix/templates/weston-rdp.nix new file mode 100644 index 0000000..c646218 --- /dev/null +++ b/nix/templates/weston-rdp.nix @@ -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..*` + # 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 ]; + }; +}