Part of the docs-migration chore (issue #708). Remove GitHub issue numbers from inline comments, option descriptions, and rustdoc — these are contextless noise for anyone reading the code without access to the original discussions. Replace with prose that captures the same rationale directly. No functional change. Build still clean (cargo check passes).
137 lines
6.2 KiB
Nix
137 lines
6.2 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
{
|
|
# 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.
|
|
#
|
|
# 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/agent.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 `/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 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 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
|
|
session sufficient pam_permit.so
|
|
'';
|
|
|
|
systemd.services.weston = {
|
|
description = "Weston Wayland compositor (VNC backend)";
|
|
after = [ "network.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
StateDirectory = "weston";
|
|
Environment = "XDG_RUNTIME_DIR=/run/user/0";
|
|
# Wrapper script: computes the deterministic VNC port, writes
|
|
# /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.
|
|
# Read from /etc/hostname (always present in NixOS containers)
|
|
# 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-//')
|
|
hash=2166136261
|
|
for byte in $(${pkgs.coreutils}/bin/printf '%s' "$AGENT_NAME" \
|
|
| ${pkgs.coreutils}/bin/od -An -tu1 \
|
|
| ${pkgs.coreutils}/bin/tr -s ' \n' ' '); do
|
|
[ -n "$byte" ] || continue
|
|
hash=$(( ((hash ^ byte) * 16777619) & 4294967295 ))
|
|
done
|
|
VNC_PORT=$((15900 + hash % 900))
|
|
|
|
# Marker file the harness reads at startup. Also records the
|
|
# fixed Wayland socket name (`wayland-0`) so other tooling can
|
|
# read it without inspecting the env-var injection below.
|
|
${pkgs.coreutils}/bin/mkdir -p /etc/hyperhive
|
|
${pkgs.coreutils}/bin/printf '{"vnc_port":%d,"auth":"none","wayland_display":"wayland-0"}\n' \
|
|
"$VNC_PORT" > /etc/hyperhive/gui.json || true
|
|
|
|
# --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"
|
|
|
|
# --socket=wayland-0: pin the compositor's Wayland socket name
|
|
# to `wayland-0` (weston default is to pick any free name such
|
|
# as `wayland-1`). Pinning lets the WAYLAND_DISPLAY=wayland-0
|
|
# global env injection below (see systemd.globalEnvironment) take
|
|
# effect unconditionally — any Wayland client launched by any
|
|
# systemd service in this container automatically connects to
|
|
# this compositor instead of failing or starting a second
|
|
# isolated display (fixes double-screen: VNC showing blank
|
|
# weston desktop while services render on a different seat).
|
|
exec ${pkgs.weston}/bin/weston \
|
|
--config="$WESTON_INI" \
|
|
--backend=vnc-backend.so \
|
|
--renderer=pixman \
|
|
--port="$VNC_PORT" \
|
|
--socket=wayland-0 \
|
|
--disable-transport-layer-security
|
|
'';
|
|
Restart = "on-failure";
|
|
RestartSec = "5s";
|
|
};
|
|
};
|
|
|
|
# Expose the compositor's socket to every systemd service in the
|
|
# container so Wayland clients (e.g. bitburner started via
|
|
# `systemd.services.*` in agent.nix) can find the compositor
|
|
# without per-service wiring. `systemd.globalEnvironment` is the
|
|
# correct path — it sets DefaultEnvironment in systemd.conf,
|
|
# reaching all units started by PID 1. `environment.variables`
|
|
# goes to /etc/environment (PAM sessions only) and is NOT visible
|
|
# to systemd service units. Without these vars a service starting
|
|
# a Wayland client would either fail to connect (libwayland falls
|
|
# back to a headless display) or open a second compositor — VNC
|
|
# shows a blank desktop.
|
|
systemd.globalEnvironment = {
|
|
WAYLAND_DISPLAY = "wayland-0";
|
|
XDG_RUNTIME_DIR = "/run/user/0";
|
|
};
|
|
|
|
# weston on the agent's interactive PATH so claude can run Wayland
|
|
# clients / weston-info against the compositor.
|
|
environment.systemPackages = [ pkgs.weston ];
|
|
};
|
|
}
|