docs/gotchas.md: extract nix/{assets,docs,templates/weston-vnc} prose (#718 batch 3)

- assets.nix: cargo-cache-invalidation rationale → "Split asset
  derivations away from the rust workspace" section.
- templates/weston-vnc.nix: port allocation, weston bind-address
  quirk, PAM service name, Type=simple choice, idle-time=0 →
  "Weston VNC compositor (per-agent hyperhive.gui.enable)" section.
- docs/default.nix: rendering pipeline + subtree-pick + output-tree
  history → "Nix options reference" section.

In-code comments trimmed to short purpose statements + docs pointers.
description = '' blocks (operator-facing options docs) preserved per
iris #718.

`nix flake check` + `nix build .#docs` clean.
This commit is contained in:
atlas 2026-05-31 15:18:48 +02:00 committed by mara
commit db2a48cde6
4 changed files with 170 additions and 174 deletions

View file

@ -5,66 +5,41 @@
...
}:
{
# Optional Weston (the reference Wayland compositor) with the VNC
# backend, surfaced as a per-agent hyperhive option. An agent turns
# it on from its own `agent.nix`:
# Optional Weston (Wayland compositor) with the VNC backend,
# surfaced as a per-agent `hyperhive.gui.enable` option. Imported
# from harness-base.nix so every sub-agent + the manager sees the
# option; only those that flip it on get the service.
#
# hyperhive.gui.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.
#
# VNC port selection: a deterministic FNV-1a hash of the agent name
# (derived from the container hostname at runtime) maps into the
# range [15900, 16799], mirroring lifecycle::agent_web_port. The
# computed port is written to `/etc/hyperhive/gui.json` at service
# start; the harness (issue #51) reads that file to know where to
# relay WebSocket connections.
#
# Note: weston's VNC backend does not expose a CLI bind-address flag
# (unlike the RDP backend's `--address`), so VNC listens on all
# interfaces. The harness WebSocket relay (issue #51) connects only
# via 127.0.0.1, and the host firewall should block external access
# to the VNC port range. A future weston.ini `[vnc] address=` can
# restrict this once upstream supports it.
# Port allocation, weston bind-address quirk, PAM service name, the
# Type=simple choice, idle-time=0: all in
# docs/gotchas.md::Weston VNC compositor.
# Harness-side WebSocket relay shape: docs/web-ui.md::Per-agent
# endpoints (`/screen` + `/screen/ws`).
options.hyperhive.gui.enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Run Weston with the VNC backend as a systemd service, for
in-browser GUI access via the harness WebSocket relay (see
issue #51). Renders in software (pixman) — no GPU, DRM,
or VT access, so no extra container capabilities are needed.
in-browser GUI access via the harness `/screen/ws` WebSocket
relay. Renders in software (pixman) no GPU, DRM, or VT
access, so no extra container capabilities are needed.
The VNC port is deterministic: FNV-1a hash of the agent name
(taken from the container hostname) mapped into [15900, 16799].
The port and auth mode are written to `/etc/hyperhive/gui.json`
at service start so the harness can relay connections.
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 the ExecStart script always tries to exec weston after
setup 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`.)
The VNC port is a deterministic FNV-1a hash of the agent name
mapped into `[15900, 16799]`, written to
`/etc/hyperhive/gui.json` at service start so the harness can
relay connections without a separate config flag. The unit is
`Type = "simple"` so a misconfigured weston degrades to a
restart loop instead of blocking `nixos-container update`.
'';
};
config = lib.mkIf config.hyperhive.gui.enable {
# neatvnc 0.9 always calls the PAM auth callback (weston_authenticate_user)
# for Apple-DH (type 30), regardless of weston.ini auth-method=none.
# pam_permit.so makes the PAM service accept any credentials so the
# browser's empty Apple-DH credentials always pass.
#
# The service name is "weston-remote-access" — that is the literal string
# passed to pam_start() inside libweston (libweston/auth.c). Using "weston"
# instead silently falls back to the system default and rejects auth.
# neatvnc ≥ 0.9 always calls the PAM auth callback for Apple-DH
# (type 30), regardless of weston.ini auth-method=none.
# pam_permit.so accepts the browser's empty Apple-DH credentials.
# Service name MUST be the literal `weston-remote-access` — that's
# the string libweston passes to pam_start() in libweston/auth.c.
security.pam.services."weston-remote-access".text = ''
auth sufficient pam_permit.so
account sufficient pam_permit.so
@ -76,29 +51,21 @@
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
# `simple`, not `notify`: switch-to-configuration must not
# wait on weston signalling readiness (same reasoning as the
# `tea-login` unit in harness-base.nix).
Type = "simple";
# Creates /var/lib/weston (0700 root) at start.
StateDirectory = "weston";
Environment = "XDG_RUNTIME_DIR=/run/user/0";
# Wrapper script: computes the deterministic VNC port, writes
# /etc/hyperhive/gui.json for the harness (issue #51), then
# execs weston. Using `exec` keeps the PID stable so systemd
# tracks the weston process correctly under Type=simple.
# Any failure before the exec triggers Restart=on-failure
# (graceful degradation) rather than blocking the rebuild.
# /etc/hyperhive/gui.json for the harness, then execs weston.
# `exec` keeps the PID stable so systemd tracks the weston
# process correctly under Type=simple.
ExecStart = pkgs.writeShellScript "weston-vnc" ''
mkdir -p /run/user/0 && chmod 700 /run/user/0 || true
# --- Compute deterministic VNC port via FNV-1a ---
# Agent name = container hostname with leading "h-" stripped,
# mirroring lifecycle::agent_web_port in hive-c0re/src/lifecycle.rs.
# Agent name = container hostname with leading `h-` stripped.
# Read from /etc/hostname (always present in NixOS containers)
# to avoid a dependency on the `hostname` binary (which lives in
# pkgs.inetutils, not pkgs.coreutils).
# VNC_PORT_BASE=15900, VNC_PORT_RANGE=900 → [15900, 16799].
# to avoid depending on `hostname` (lives in pkgs.inetutils,
# not pkgs.coreutils).
RAW_HOST=$(${pkgs.coreutils}/bin/cat /etc/hostname)
AGENT_NAME=$(${pkgs.coreutils}/bin/printf '%s' "$RAW_HOST" \
| ${pkgs.gnused}/bin/sed 's/^h-//')
@ -111,30 +78,15 @@
done
VNC_PORT=$((15900 + hash % 900))
# --- Write gui.json marker ---
# The harness reads this at startup (issue #51) to know the
# VNC port and auth mode for the WebSocket relay.
# Marker file the harness reads at startup.
${pkgs.coreutils}/bin/mkdir -p /etc/hyperhive
${pkgs.coreutils}/bin/printf '{"vnc_port":%d,"auth":"none"}\n' \
"$VNC_PORT" > /etc/hyperhive/gui.json || true
# neatvnc ≥ 0.9 advertises RSA-AES and Apple-DH security types
# when auth is compiled in. The browser client handles Apple-DH
# (type 30) with empty credentials.
#
# weston.ini [vnc] auth-method=none: weston uses an always-accept
# auth callback instead of PAM. Without this, weston defaults to
# PAM authentication which rejects empty credentials (SecurityResult=1).
#
# --disable-transport-layer-security prevents the VeNCrypt TLS
# wrapper; plain auth types (incl. type 30) are advertised directly.
# [core] idle-time=0 disables weston's idle timeout (default
# 300s). Without it the VNC desktop fades to black after 5 min
# idle and desktop-shell shows its click-to-unlock lock screen
# — useless for an agent desktop viewed over /screen (issue
# #180). idle-time=0 → the idle timer is updated with a 0ms
# delay, which wl_event_source_timer_update treats as "disarm",
# so the compositor never goes idle and never locks.
# --disable-transport-layer-security: skips the VeNCrypt TLS
# wrapper so plain auth types (incl. Apple-DH type 30) are
# advertised directly. [core] idle-time=0 disables the
# compositor's 300s idle/lock screen.
WESTON_INI=$(${pkgs.coreutils}/bin/mktemp /tmp/weston-XXXXXX.ini)
${pkgs.coreutils}/bin/printf '[core]\nidle-time=0\n\n[vnc]\nauth-method=none\n' > "$WESTON_INI"
@ -150,8 +102,8 @@
};
};
# weston on the agent's interactive PATH too, so claude can run
# Wayland clients / `weston-info` against the compositor.
# weston on the agent's interactive PATH so claude can run Wayland
# clients / weston-info against the compositor.
environment.systemPackages = [ pkgs.weston ];
};
}