Wire HDHomeRun observations and recover Forge OP Stack

This commit is contained in:
every.channel 2026-05-03 20:24:04 -07:00
parent 8065860449
commit 0d86104762
No known key found for this signature in database
18 changed files with 1613 additions and 58 deletions

View file

@ -309,6 +309,60 @@ in
description = "Pass `EVERY_CHANNEL_NBC_NO_SANDBOX=1` for Chrome worker sessions.";
};
mvpdProvider = lib.mkOption {
type = lib.types.str;
default = "Verizon Fios";
description = "MVPD provider name used when the NBC worker must choose a TV provider.";
};
mvpdUsernameFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Optional root-managed file containing the MVPD username for unattended NBC login.";
};
mvpdPasswordFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = "Optional root-managed file containing the MVPD password for unattended NBC login.";
};
isolateWithUserNetns = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Launch NBC browser-backed workers inside a rootless user+network namespace backed by
slirp4netns. This keeps the Chrome / ec-node process tree in its own network context
while still using the host's active upstream route.
'';
};
requireMullvad = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Refuse to start NBC browser-backed workers until `mullvad status` reports a connected
tunnel. This assumes the host Mullvad daemon is already logged in and connected.
'';
};
mullvadWaitSeconds = lib.mkOption {
type = lib.types.ints.positive;
default = 90;
description = "Maximum time to wait for Mullvad connectivity before failing an NBC worker start.";
};
mullvadLocation = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "USA";
description = ''
Optional case-insensitive substring that must appear in `mullvad status` before an NBC
worker starts. Use this to pin workers to a country or city family without committing the
operational login material itself.
'';
};
vnc = {
enable = lib.mkOption {
type = lib.types.bool;
@ -435,6 +489,11 @@ in
pkgs.iproute2
cfg.package
]
++ lib.optionals (isNbc && cfg.nbc.requireMullvad) [ pkgs.mullvad-vpn ]
++ lib.optionals (isNbc && cfg.nbc.isolateWithUserNetns) [
pkgs.slirp4netns
pkgs.util-linux
]
++ lib.optionals cfg.hdhomerun.autoDiscover [ pkgs.jq cfg.discoveryPackage ];
text =
let
@ -452,10 +511,75 @@ in
controlDiscoveryStr = if cfg.control.discovery == null then "" else cfg.control.discovery;
controlIrohSecretStr = if cfg.control.irohSecret == null then "" else cfg.control.irohSecret;
controlGossipPeerLines = lib.concatMapStrings (peer: "cmd+=(--gossip-peer ${lib.escapeShellArg peer})\n") cfg.control.gossipPeers;
nbcMullvadLocationStr = if cfg.nbc.mullvadLocation == null then "" else cfg.nbc.mullvadLocation;
in
''
set -euo pipefail
wait_for_mullvad() {
local wait_seconds status expected
wait_seconds=${toString cfg.nbc.mullvadWaitSeconds}
expected=${lib.escapeShellArg nbcMullvadLocationStr}
for _ in $(seq 1 "$wait_seconds"); do
status="$(mullvad status 2>/dev/null || true)"
if [[ "$status" == Connected* ]]; then
if [[ -z "$expected" ]] || printf '%s\n' "$status" | grep -Fqi -- "$expected"; then
return 0
fi
fi
sleep 1
done
echo "ec-node: Mullvad was not connected${lib.optionalString (cfg.nbc.mullvadLocation != null) " to the expected location"} within ${toString cfg.nbc.mullvadWaitSeconds}s" >&2
mullvad status >&2 || true
return 1
}
run_in_user_netns() {
local tmpdir pid_file ready_fifo ns_pid slirp_pid status
tmpdir="$(mktemp -d /tmp/${unit}.usernet.XXXXXX)"
pid_file="$tmpdir/pid"
ready_fifo="$tmpdir/ready"
mkfifo "$ready_fifo"
# shellcheck disable=SC2016
unshare --user --map-root-user --net ${pkgs.bash}/bin/bash -lc '
set -euo pipefail
ip link set lo up
echo $$ > "$1"
read -r _ < "$2"
shift 2
exec "$@"
' bash "$pid_file" "$ready_fifo" "''${cmd[@]}" &
ns_pid=$!
for _ in $(seq 1 50); do
[[ -s "$pid_file" ]] && break
sleep 0.1
done
if [[ ! -s "$pid_file" ]]; then
echo "ec-node: timed out waiting for NBC user-netns PID" >&2
kill "$ns_pid" 2>/dev/null || true
rm -rf "$tmpdir"
return 1
fi
slirp4netns --configure --mtu=1500 "$(cat "$pid_file")" tap0 >"$tmpdir/slirp.log" 2>&1 &
slirp_pid=$!
sleep 1
printf 'go\n' > "$ready_fifo"
set +e
wait "$ns_pid"
status=$?
set -e
kill "$slirp_pid" 2>/dev/null || true
wait "$slirp_pid" 2>/dev/null || true
rm -rf "$tmpdir"
return "$status"
}
nbc_url=${lib.escapeShellArg nbcUrlStr}
input=""
if [[ -z "$nbc_url" ]]; then
@ -596,7 +720,16 @@ in
# quickly during activation.
trap 'exit 0' INT TERM
while true; do
"''${cmd[@]}" || true
${lib.optionalString (isNbc && cfg.nbc.requireMullvad) ''
if ! wait_for_mullvad; then
sleep 2
continue
fi
''}
${lib.optionalString (isNbc && cfg.nbc.isolateWithUserNetns) "run_in_user_netns || true"}
${lib.optionalString (!isNbc || !cfg.nbc.isolateWithUserNetns) ''
"''${cmd[@]}" || true
''}
sleep 2
done
'';
@ -609,10 +742,12 @@ in
wantedBy = [ "multi-user.target" ];
after =
[ "network-online.target" ]
++ lib.optionals isNbc [ "every-channel-nbc-display.service" ];
++ lib.optionals isNbc [ "every-channel-nbc-display.service" ]
++ lib.optionals (isNbc && cfg.nbc.requireMullvad) [ "mullvad-daemon.service" ];
wants =
[ "network-online.target" ]
++ lib.optionals isNbc [ "every-channel-nbc-display.service" ];
++ lib.optionals isNbc [ "every-channel-nbc-display.service" ]
++ lib.optionals (isNbc && cfg.nbc.requireMullvad) [ "mullvad-daemon.service" ];
# Keep the unit from entering "failed" due to rapid restarts (deploy-flake treats
# failed units during `switch-to-configuration test` as a deployment failure).
@ -652,13 +787,22 @@ in
environment =
cfg.environment
// lib.optionalAttrs isNbc {
DISPLAY = cfg.nbc.display;
EVERY_CHANNEL_NBC_CHROME_PATH = cfg.nbc.chromeBinary;
EVERY_CHANNEL_NBC_PROFILE_DIR = cfg.nbc.profileDir;
EVERY_CHANNEL_NBC_NO_SANDBOX = if cfg.nbc.noSandbox then "1" else "0";
HOME = "/var/lib/every-channel";
};
// lib.optionalAttrs isNbc (
{
DISPLAY = cfg.nbc.display;
EVERY_CHANNEL_NBC_CHROME_PATH = cfg.nbc.chromeBinary;
EVERY_CHANNEL_NBC_MVPD_PROVIDER = cfg.nbc.mvpdProvider;
EVERY_CHANNEL_NBC_PROFILE_DIR = cfg.nbc.profileDir;
EVERY_CHANNEL_NBC_NO_SANDBOX = if cfg.nbc.noSandbox then "1" else "0";
HOME = "/var/lib/every-channel";
}
// lib.optionalAttrs (cfg.nbc.mvpdUsernameFile != null) {
EVERY_CHANNEL_NBC_MVPD_USERNAME_FILE = toString cfg.nbc.mvpdUsernameFile;
}
// lib.optionalAttrs (cfg.nbc.mvpdPasswordFile != null) {
EVERY_CHANNEL_NBC_MVPD_PASSWORD_FILE = toString cfg.nbc.mvpdPasswordFile;
}
);
};
})
cfg.broadcasts)