hyperhive/nix/templates/weston-vnc.nix
iris c7360cf0bb docs(#727): split docs/web-ui.md into shape / dashboard / agent
docs/web-ui.md (1315 lines) split into three sub-files:
- docs/web-ui/shape.md   — shared SPA skeleton, SSE multiplexing,
  Worker-death self-heal, terminal pane, listener bind, relative
  paths, atomic repaint, side panel
- docs/web-ui/dashboard.md — SW4RM/Y3R/SYST3M/SCH3DUL3S/S3TT1NGS
  tabs, container row, topology tree, selection bar, approval card,
  dashboard endpoints + event channel
- docs/web-ui/agent.md   — header, terminal, composer, inbox, live
  view, slash commands, per-agent endpoints, stats page

docs/web-ui.md replaced with a thin index linking all three.

Section anchors in docs (gateway.md, gotchas.md), Rust doc comments
(hive-ag3nt/src/web_ui.rs), and nix/templates/weston-vnc.nix updated
to point at the correct sub-file. README and CLAUDE.md file-map
updated with sub-file links. Inline // comments in frontend source
left unchanged (they reference the index which redirects to the right
sub-file).
2026-05-31 21:37:04 +02:00

109 lines
4.5 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.
${pkgs.coreutils}/bin/mkdir -p /etc/hyperhive
${pkgs.coreutils}/bin/printf '{"vnc_port":%d,"auth":"none"}\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"
exec ${pkgs.weston}/bin/weston \
--config="$WESTON_INI" \
--backend=vnc-backend.so \
--renderer=pixman \
--port="$VNC_PORT" \
--disable-transport-layer-security
'';
Restart = "on-failure";
RestartSec = "5s";
};
};
# weston on the agent's interactive PATH so claude can run Wayland
# clients / weston-info against the compositor.
environment.systemPackages = [ pkgs.weston ];
};
}