Compare commits
No commits in common. "29df223650a48974510b00d2e85609d132c7cc4e" and "0b237d7d8c18b98e83d3fcc1828695adeb5fbc05" have entirely different histories.
29df223650
...
0b237d7d8c
3 changed files with 19 additions and 145 deletions
37
CLAUDE.md
37
CLAUDE.md
|
|
@ -145,10 +145,8 @@ nix/
|
||||||
templates/harness-base.nix shared scaffolding for sub-agents + manager
|
templates/harness-base.nix shared scaffolding for sub-agents + manager
|
||||||
templates/agent-base.nix sub-agent nixosConfiguration
|
templates/agent-base.nix sub-agent nixosConfiguration
|
||||||
templates/manager.nix manager nixosConfiguration
|
templates/manager.nix manager nixosConfiguration
|
||||||
templates/weston-vnc.nix optional `hyperhive.gui.enable`
|
templates/weston-rdp.nix optional `hyperhive.westonRdp.enable`
|
||||||
— weston + VNC backend systemd unit; writes
|
— weston + RDP backend systemd unit
|
||||||
/etc/hyperhive/gui.json (vnc_port + auth) for
|
|
||||||
the harness WebSocket relay (issue #51)
|
|
||||||
|
|
||||||
docs/
|
docs/
|
||||||
conventions.md naming, identity=socket, async forms, commit style
|
conventions.md naming, identity=socket, async forms, commit style
|
||||||
|
|
@ -257,21 +255,22 @@ Prune freely.
|
||||||
appends nothing if the journal can't be read. The manager's
|
appends nothing if the journal can't be read. The manager's
|
||||||
`update` tool / rebuild errors now carry the failing-unit
|
`update` tool / rebuild errors now carry the failing-unit
|
||||||
detail without a second `get_logs` call.
|
detail without a second `get_logs` call.
|
||||||
- **Just landed:** `hyperhive.gui.enable` option (replaces
|
- **Just landed:** `hyperhive.westonRdp.enable` option. New
|
||||||
`hyperhive.westonRdp.enable`). New
|
`nix/templates/weston-rdp.nix` declares a per-agent bool;
|
||||||
`nix/templates/weston-vnc.nix` declares a per-agent bool;
|
enabling it runs weston with the RDP backend as a systemd
|
||||||
enabling it runs weston with the VNC backend as a systemd
|
service (software/pixman render, self-signed TLS cert
|
||||||
service (software/pixman render). Port is deterministic:
|
generated first-boot under `/var/lib/weston`). Imported by
|
||||||
FNV-1a hash of the agent name (from hostname) in
|
`harness-base.nix` so every agent has the option; an agent
|
||||||
[15900, 16799], mirroring `lifecycle::agent_web_port`.
|
opts in from its own `agent.nix`. Design note: it's a FLAT
|
||||||
The ExecStart wrapper script computes the port, writes
|
per-agent option, not `hyperhive.agents.<name>.*` — each
|
||||||
`/etc/hyperhive/gui.json` (`{ "vnc_port": N, "auth": "none" }`)
|
agent is its own nixosConfiguration with no cross-agent
|
||||||
for the harness WebSocket relay (issue #51), then execs
|
view, so the `<name>` indirection is meaningless. The unit
|
||||||
weston. Imported by `harness-base.nix`; an agent opts in
|
is `Type = "simple"` with an always-exit-0 `ExecStartPre` so
|
||||||
from its own `agent.nix`. `Type = "simple"` so it can never
|
it can never abort `nixos-container update` (a `Type=notify`
|
||||||
abort `nixos-container update`. A misconfigured weston
|
weston that never signals READY would fail activation every
|
||||||
degrades to a restart loop in `journalctl`, not a blocked
|
reload — the trap `tea-login` documents). A misconfigured
|
||||||
rebuild. Old `weston-rdp.nix` / `westonRdp.enable` removed.
|
weston degrades to a restart loop in `journalctl`, not a
|
||||||
|
blocked rebuild.
|
||||||
- **Just landed:** `get_logs` now resolves the machine name.
|
- **Just landed:** `get_logs` now resolves the machine name.
|
||||||
`journalctl -M` wants the *machine* name (`h-gui`), not the
|
`journalctl -M` wants the *machine* name (`h-gui`), not the
|
||||||
logical agent name (`gui`) — `get_logs` was the one manager
|
logical agent name (`gui`) — `get_logs` was the one manager
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
# Optional feature modules. Each declares its own `hyperhive.*`
|
# Optional feature modules. Each declares its own `hyperhive.*`
|
||||||
# option(s), default-off, so every agent has them available but
|
# option(s), default-off, so every agent has them available but
|
||||||
# only opts in from its own `agent.nix`.
|
# only opts in from its own `agent.nix`.
|
||||||
imports = [ ./weston-vnc.nix ];
|
imports = [ ./weston-rdp.nix ];
|
||||||
|
|
||||||
options.hyperhive.allowedBashPatterns = lib.mkOption {
|
options.hyperhive.allowedBashPatterns = lib.mkOption {
|
||||||
type = lib.types.listOf lib.types.str;
|
type = lib.types.listOf lib.types.str;
|
||||||
|
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
config,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
# 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`:
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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`.)
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
config = lib.mkIf config.hyperhive.gui.enable {
|
|
||||||
systemd.services.weston = {
|
|
||||||
description = "Weston Wayland compositor (VNC backend)";
|
|
||||||
after = [ "network.target" ];
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
serviceConfig = {
|
|
||||||
# `simple`, not `notify`: switch-to-configuration must not
|
|
||||||
# wait on weston signalling readiness. See weston-rdp.nix and
|
|
||||||
# the `tea-login` unit for the same reasoning.
|
|
||||||
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.
|
|
||||||
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.
|
|
||||||
# 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].
|
|
||||||
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))
|
|
||||||
|
|
||||||
# --- Write gui.json marker ---
|
|
||||||
# The harness reads this at startup (issue #51) to know the
|
|
||||||
# VNC port and auth mode for the WebSocket relay.
|
|
||||||
${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: VNC is loopback-only
|
|
||||||
# (relayed by the harness WebSocket proxy); TLS would require
|
|
||||||
# cert generation and adds no real security benefit here.
|
|
||||||
exec ${pkgs.weston}/bin/weston \
|
|
||||||
--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 too, so claude can run
|
|
||||||
# Wayland clients / `weston-info` against the compositor.
|
|
||||||
environment.systemPackages = [ pkgs.weston ];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue