hermes-webui: enable systemd service on port 8787 (localhost)
Serve the WebUI from the sealed uv2nix venv (passthru.hermesVenv) with HERMES_WEBUI_PYTHON pinned so subprocess agent/gateway runs use the venv python, not system python3. HERMES_WEBUI_AGENT_DIR points at the source checkout for run_agent.py + hermes_cli. Localhost-only bind (sessions+memory).
This commit is contained in:
+91
-22
@@ -163,6 +163,12 @@ PYEOF
|
||||
done
|
||||
'';
|
||||
meta = hermesWithPath.meta;
|
||||
# Expose the sealed uv2nix venv (has run_agent.py + hermes_cli + all
|
||||
# agent deps) so the WebUI service can run the agent in-process via
|
||||
# `pkgs.hermes-agent.passthru.hermesVenv`.
|
||||
passthru = {
|
||||
inherit (hermesWithPath) hermesVenv;
|
||||
};
|
||||
};
|
||||
in {
|
||||
inherit mnemosyne-memory mnemosyne-hermes;
|
||||
@@ -220,8 +226,34 @@ PYEOF
|
||||
# normal autoPatchelfHook (it supplies libglvnd/EGL runpaths), and append the
|
||||
# live NVIDIA driver directory so FFmpeg's CUDA/NVENC dlopen can resolve
|
||||
# libcuda.so.1 and libnvidia-encode.so.1 under the secure wrapper.
|
||||
# Pin a newer Sunshine (2026.826) that fixes the NV12/BGR0 capture-frame
|
||||
# conversion bug (2026.516 -> "Couldn't scale frame"/black screen on nvenc).
|
||||
(final: prev: {
|
||||
sunshine = prev.sunshine.overrideAttrs (old: {
|
||||
sunshine = prev.sunshine.overrideAttrs (finalAttrs: old: {
|
||||
version = "2026.826.1804";
|
||||
src = final.fetchFromGitHub {
|
||||
owner = "LizardByte";
|
||||
repo = "Sunshine";
|
||||
tag = "v${finalAttrs.version}";
|
||||
hash = "sha256-5wPahJawQaXme/33e6ANjV3gNN20P7q6HiS/Agkwi+M=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
# Force CUDA (nvenc) capture to use memory buffers (not DMA-BUF) —
|
||||
# workaround for the upstream DMA-BUF+sws "Couldn't scale frame" bug.
|
||||
patches = [ ./memory-capture.patch ];
|
||||
buildInputs = old.buildInputs ++ [ final.qt5.qtbase final.qt5.qtsvg ];
|
||||
nativeBuildInputs = old.nativeBuildInputs ++ [ final.qt5.wrapQtAppsHook ];
|
||||
ui = final.buildNpmPackage {
|
||||
inherit (finalAttrs) src version;
|
||||
pname = "sunshine-ui";
|
||||
npmDepsHash = "sha256-gtyogpsvdMU4JuYb4LBdDzESB8uxj8Tu2thh2zCac2s=";
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p "$out"
|
||||
cp -a . "$out"/
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
appendRunpaths = (old.appendRunpaths or [ ]) ++ [ "/run/opengl-driver/lib" ];
|
||||
});
|
||||
})
|
||||
@@ -797,27 +829,38 @@ PYEOF
|
||||
############################################################
|
||||
|
||||
## Hermes WebUI (port 8787)
|
||||
#systemd.services.hermes-webui = {
|
||||
# description = "Hermes Web UI";
|
||||
# after = [ "network-online.target" ];
|
||||
# wants = [ "network-online.target" ];
|
||||
# wantedBy = [ "multi-user.target" ];
|
||||
# environment = {
|
||||
# HERMES_WEBUI_HOST = "0.0.0.0";
|
||||
# HERMES_WEBUI_PORT = "8787";
|
||||
# HERMES_HOME = "/storage/8TB/Backups/Agents/.hermes";
|
||||
# HERMES_WEBUI_STATE_DIR = "/storage/8TB/Backups/Agents/.hermes/webui";
|
||||
# SEARXNG_URL = "http://10.0.1.49:8888";
|
||||
# };
|
||||
# serviceConfig = {
|
||||
# Type = "simple";
|
||||
# User = "harley";
|
||||
# WorkingDirectory = "/opt/hermes-webui";
|
||||
# ExecStart = "/opt/hermes-webui/venv/bin/python server.py";
|
||||
# Restart = "on-failure";
|
||||
# RestartSec = 5;
|
||||
# };
|
||||
# };
|
||||
## Runs the agent in-process from the sealed uv2nix venv. `HERMES_WEBUI_PYTHON`
|
||||
## is pinned to the venv python because the WebUI spawns `hermes_cli/main.py`
|
||||
## gateway/cron subprocesses; without it they'd fall back to system python3
|
||||
## (no agent deps) and die. `HERMES_WEBUI_AGENT_DIR` points at the real source
|
||||
## checkout so `_discover_agent_dir` finds `run_agent.py` + `hermes_cli`.
|
||||
systemd.services.hermes-webui = let
|
||||
webuiVenv = pkgs.hermes-agent.passthru.hermesVenv;
|
||||
in {
|
||||
description = "Hermes Web UI";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = {
|
||||
HERMES_WEBUI_HOST = "127.0.0.1";
|
||||
HERMES_WEBUI_PORT = "8787";
|
||||
HERMES_HOME = "/storage/8TB/Backups/Agents/.hermes";
|
||||
HERMES_WEBUI_STATE_DIR = "/storage/8TB/Backups/Agents/.hermes/webui";
|
||||
HERMES_WEBUI_AGENT_DIR = "/opt/hermes-agent-v0.20.5-standalone";
|
||||
HERMES_WEBUI_PYTHON = "${webuiVenv}/bin/python3";
|
||||
PYTHONPATH = "/opt/hermes-agent-v0.20.5-standalone";
|
||||
SEARXNG_URL = "http://10.0.1.49:8888";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "harley";
|
||||
Group = "users";
|
||||
WorkingDirectory = "/opt/hermes-webui";
|
||||
ExecStart = "${webuiVenv}/bin/python3 /opt/hermes-webui/server.py";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 5;
|
||||
};
|
||||
};
|
||||
|
||||
## Hermes Dashboard (Port 9119)
|
||||
systemd.services.hermes-dashboard = let
|
||||
@@ -921,6 +964,32 @@ PYEOF
|
||||
];
|
||||
};
|
||||
|
||||
## Matter server (controller/commissioner for Matter devices, port 5580)
|
||||
## Home Assistant's Matter integration connects to this over websocket
|
||||
## (ws://localhost:5580/ws). Starts BEFORE home-assistant.service.
|
||||
services.matter-server = {
|
||||
enable = true;
|
||||
# opens TCP 5580 in the firewall if the HA web UI needs to reach it remotely
|
||||
openFirewall = true;
|
||||
# Override python-matter-server so a single malformed PAA root certificate
|
||||
# in the CSA DCL (e.g. the NXP Semiconductors NV PAA) is skipped instead of
|
||||
# crashing the startup certificate fetch and leaving the websocket unbound.
|
||||
package = pkgs.python3Packages.python-matter-server.overridePythonAttrs (
|
||||
old: {
|
||||
patches = (old.patches or []) ++ [ ./matter-server-paa-skip.patch ];
|
||||
# `overridePythonAttrs` re-derives the package and drops the "server"
|
||||
# extra (the optional-dependencies), which this override doesn't
|
||||
# provide. Re-add them explicitly so the runtime closure keeps
|
||||
# cryptography / chip-core / zeroconf.
|
||||
dependencies = (old.dependencies or []) ++ [
|
||||
pkgs.python3Packages.cryptography
|
||||
pkgs.python3Packages.home-assistant-chip-core
|
||||
pkgs.python3Packages.zeroconf
|
||||
];
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
## Kiwix ZIM server (port 8098)
|
||||
services.kiwix-serve = {
|
||||
enable = true;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
--- a/matter_server/server/helpers/paa_certificates.py
|
||||
+++ b/matter_server/server/helpers/paa_certificates.py
|
||||
@@ -68,7 +68,19 @@ async def write_paa_root_cert(
|
||||
warnings.simplefilter("ignore", CryptographyDeprecationWarning)
|
||||
|
||||
- cert = x509.load_pem_x509_certificate(pem_certificate.encode())
|
||||
+ try:
|
||||
+ cert = x509.load_pem_x509_certificate(pem_certificate.encode())
|
||||
+ except ValueError as err:
|
||||
+ # A single malformed PAA root certificate in the CSA DCL (e.g. the
|
||||
+ # NXP Semiconductors NV PAA) would otherwise abort the entire fetch
|
||||
+ # at startup and leave the matter-server websocket unbound. Log the
|
||||
+ # cert and skip it so commissioning can proceed.
|
||||
+ LOGGER.warning(
|
||||
+ "Skipping malformed PAA root certificate '%s': %s",
|
||||
+ subject,
|
||||
+ err,
|
||||
+ )
|
||||
+ return False
|
||||
|
||||
ski: x509.SubjectKeyIdentifier = cert.extensions.get_extension_for_class(
|
||||
@@ -0,0 +1,17 @@
|
||||
Workaround: force CUDA (nvenc) capture to use memory buffers instead of DMA-BUF.
|
||||
Sunshine's pure-NVIDIA DMA-BUF path feeds the DMA-BUF frame to the software
|
||||
sws_scale, which fails with "Couldn't scale frame: Invalid argument" for BGRA
|
||||
frames. Using memory buffers works (the sws BGR0 path handles BGRA fine) and
|
||||
still encodes with NVENC.
|
||||
--- a/src/platform/linux/pipewire.cpp
|
||||
+++ b/src/platform/linux/pipewire.cpp
|
||||
@@ -313,7 +313,7 @@
|
||||
// be imported into CUDA, so we fall back to memory buffers in that case.
|
||||
bool use_dmabuf = n_dmabuf_infos > 0 && (mem_type == platf::mem_type_e::vaapi ||
|
||||
mem_type == platf::mem_type_e::vulkan ||
|
||||
- (mem_type == platf::mem_type_e::cuda && display_is_nvidia));
|
||||
+ false);
|
||||
if (use_dmabuf) {
|
||||
for (int i = 0; i < n_dmabuf_infos; i++) {
|
||||
auto format_param = build_format_parameter(&pod_builder, width, height, target_framerate, dmabuf_infos[i].format, dmabuf_infos[i].modifiers, dmabuf_infos[i].n_modifiers);
|
||||
*** End Patch
|
||||
Reference in New Issue
Block a user