From 7390492d8b23c0902fd403cceb3067de1a279d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 24 Mar 2026 12:57:52 -0700 Subject: [PATCH 1/3] feat(glyph): add agentsview service with PostgreSQL backend - Add agentsview database and user to PostgreSQL - Add systemd service for agentsview pg serve (team dashboard on :8095) - Add systemd timer to push local glyph sessions every 10 minutes - Tailscale subnet trusted for passwordless PG access Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/agentsview.nix | 56 +++++++++++++++++++++++++++++ hosts/glyph/services/db.nix | 8 +++-- hosts/glyph/services/default.nix | 1 + 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 hosts/glyph/services/agentsview.nix diff --git a/hosts/glyph/services/agentsview.nix b/hosts/glyph/services/agentsview.nix new file mode 100644 index 00000000..c8ac9a1f --- /dev/null +++ b/hosts/glyph/services/agentsview.nix @@ -0,0 +1,56 @@ +{ + config, + pkgs, + ... +}: let + port = 8095; + configFile = pkgs.writeText "agentsview-config.toml" '' + [pg] + url = "postgres://agentsview@localhost:5432/agentsview?sslmode=disable" + machine_name = "glyph" + ''; +in { + # Serve the aggregated team dashboard from PostgreSQL + systemd.services.agentsview = { + description = "Agentsview team dashboard"; + after = ["postgresql.service"]; + requires = ["postgresql.service"]; + wantedBy = ["multi-user.target"]; + serviceConfig = { + ExecStart = "${pkgs.agentsview}/bin/agentsview pg serve -host 127.0.0.1 -port ${toString port}"; + DynamicUser = true; + User = "agentsview"; + Group = "agentsview"; + StateDirectory = "agentsview"; + RuntimeDirectory = "agentsview"; + Environment = "HOME=/var/lib/agentsview"; + ExecStartPre = "+${pkgs.coreutils}/bin/install -Dm640 -o agentsview -g agentsview ${configFile} /var/lib/agentsview/.agentsview/config.toml"; + Restart = "on-failure"; + RestartSec = 5; + }; + }; + + # Push local glyph sessions to PostgreSQL every 10 minutes + systemd.timers.agentsview-pg-push = { + description = "Push agentsview sessions to PostgreSQL"; + wantedBy = ["timers.target"]; + timerConfig = { + OnBootSec = "2min"; + OnUnitActiveSec = "10min"; + Persistent = true; + }; + }; + + systemd.services.agentsview-pg-push = { + description = "Push agentsview sessions to PostgreSQL"; + after = ["postgresql.service"]; + requires = ["postgresql.service"]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.agentsview}/bin/agentsview pg push"; + User = "mu"; + Group = "users"; + Environment = "HOME=/home/mu"; + }; + }; +} diff --git a/hosts/glyph/services/db.nix b/hosts/glyph/services/db.nix index bc35dfb5..37229061 100644 --- a/hosts/glyph/services/db.nix +++ b/hosts/glyph/services/db.nix @@ -12,12 +12,16 @@ port = 5432; max_connections = 150; }; - ensureDatabases = ["atticd" "grafana" "open-webui" "pocketid"]; + ensureDatabases = ["agentsview" "atticd" "grafana" "open-webui" "pocketid"]; ensureUsers = [ { name = "mu"; ensureClauses.superuser = true; } + { + name = "agentsview"; + ensureDBOwnership = true; + } { name = "atticd"; ensureDBOwnership = true; @@ -39,6 +43,6 @@ services.postgresqlBackup = { enable = true; - databases = ["atticd" "grafana" "open-webui" "pocketid"]; + databases = ["agentsview" "atticd" "grafana" "open-webui" "pocketid"]; }; } diff --git a/hosts/glyph/services/default.nix b/hosts/glyph/services/default.nix index 59bc0e8b..9fe13a73 100644 --- a/hosts/glyph/services/default.nix +++ b/hosts/glyph/services/default.nix @@ -4,6 +4,7 @@ ... }: { imports = [ + ./agentsview.nix ./attic.nix ./db.nix ./avahi.nix From ef659c17c11e0c637d9eadc3380e24ae775d0fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 24 Mar 2026 15:35:17 -0700 Subject: [PATCH 2/3] fix(glyph): fix agentsview service permission and push issues - Use preStart instead of ExecStartPre with + prefix so config dir is created with correct ownership under DynamicUser/StateDirectory - Wrap pg push in a script that exits after sync completes, since agentsview starts a file watcher and web server after pushing Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/agentsview.nix | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/hosts/glyph/services/agentsview.nix b/hosts/glyph/services/agentsview.nix index c8ac9a1f..232f2d3a 100644 --- a/hosts/glyph/services/agentsview.nix +++ b/hosts/glyph/services/agentsview.nix @@ -9,6 +9,15 @@ url = "postgres://agentsview@localhost:5432/agentsview?sslmode=disable" machine_name = "glyph" ''; + + # pg push starts a watcher after syncing; wrap it to exit after sync + pgPushScript = pkgs.writeShellScript "agentsview-pg-push" '' + ${pkgs.agentsview}/bin/agentsview pg push & + PID=$! + # Wait for sync to complete, then kill the watcher + sleep 15 + kill $PID 2>/dev/null || true + ''; in { # Serve the aggregated team dashboard from PostgreSQL systemd.services.agentsview = { @@ -16,6 +25,10 @@ in { after = ["postgresql.service"]; requires = ["postgresql.service"]; wantedBy = ["multi-user.target"]; + preStart = '' + mkdir -p /var/lib/agentsview/.agentsview + cp ${configFile} /var/lib/agentsview/.agentsview/config.toml + ''; serviceConfig = { ExecStart = "${pkgs.agentsview}/bin/agentsview pg serve -host 127.0.0.1 -port ${toString port}"; DynamicUser = true; @@ -24,7 +37,6 @@ in { StateDirectory = "agentsview"; RuntimeDirectory = "agentsview"; Environment = "HOME=/var/lib/agentsview"; - ExecStartPre = "+${pkgs.coreutils}/bin/install -Dm640 -o agentsview -g agentsview ${configFile} /var/lib/agentsview/.agentsview/config.toml"; Restart = "on-failure"; RestartSec = 5; }; @@ -47,10 +59,11 @@ in { requires = ["postgresql.service"]; serviceConfig = { Type = "oneshot"; - ExecStart = "${pkgs.agentsview}/bin/agentsview pg push"; + ExecStart = pgPushScript; User = "mu"; Group = "users"; Environment = "HOME=/home/mu"; + TimeoutStartSec = 60; }; }; } From 72e86f561cdee49618c28e67b1c4f21e3fbdc947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=9C=BF=20corey=20=28they/them=29?= Date: Tue, 24 Mar 2026 15:44:01 -0700 Subject: [PATCH 3/3] fix(glyph): fix agentsview config dir ownership Use ExecStartPre with + prefix (runs as root) to create .agentsview/ directory and chown it to the DynamicUser, so agentsview can write config.json for its cursor secret. Co-Authored-By: Claude Opus 4.6 --- hosts/glyph/services/agentsview.nix | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hosts/glyph/services/agentsview.nix b/hosts/glyph/services/agentsview.nix index 232f2d3a..a83c098e 100644 --- a/hosts/glyph/services/agentsview.nix +++ b/hosts/glyph/services/agentsview.nix @@ -25,10 +25,6 @@ in { after = ["postgresql.service"]; requires = ["postgresql.service"]; wantedBy = ["multi-user.target"]; - preStart = '' - mkdir -p /var/lib/agentsview/.agentsview - cp ${configFile} /var/lib/agentsview/.agentsview/config.toml - ''; serviceConfig = { ExecStart = "${pkgs.agentsview}/bin/agentsview pg serve -host 127.0.0.1 -port ${toString port}"; DynamicUser = true; @@ -37,6 +33,13 @@ in { StateDirectory = "agentsview"; RuntimeDirectory = "agentsview"; Environment = "HOME=/var/lib/agentsview"; + ExecStartPre = let + script = pkgs.writeShellScript "agentsview-setup" '' + mkdir -p /var/lib/agentsview/.agentsview + cp ${configFile} /var/lib/agentsview/.agentsview/config.toml + chown -R agentsview:agentsview /var/lib/agentsview/.agentsview + ''; + in "+${script}"; Restart = "on-failure"; RestartSec = 5; };