From bfe921c2540094d1bf05c7fa7ae6e6ef0b21bf06 Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 17 Aug 2026 21:15:18 +0200 Subject: [PATCH] feat(#3405): declarative grafana plugins for the swarm dashboard Plugin management is server-admin scoped, and on an SSO hive nobody holds that role: auto_assign_org_role grants an org role, and the built-in local admin that does hold server admin cannot log in because the login form is disabled whenever SSO is configured. Two individually-correct decisions leaving no path to the plugin UI at all. Declarative is the way through rather than a workaround for it -- plugins land in the store and in git, survive a rebuild and a state reset, and the container needs no runtime egress to grafana.com. mkIf rather than passing the list through: upstream's default is null, while an empty list is a real value pointing the plugin path at an empty store dir, so a hive that sets nothing must keep seeing null. --- nix/host-modules/swarm-grafana.nix | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/nix/host-modules/swarm-grafana.nix b/nix/host-modules/swarm-grafana.nix index 878f4b1f..79a99907 100644 --- a/nix/host-modules/swarm-grafana.nix +++ b/nix/host-modules/swarm-grafana.nix @@ -174,6 +174,30 @@ in ''; }; }; + + plugins = lib.mkOption { + type = lib.types.listOf lib.types.package; + default = [ ]; + example = lib.literalExpression "[ pkgs.grafanaPlugins.grafana-piechart-panel ]"; + description = '' + Grafana plugins to install, as packages. Declarative rather than + installed through the UI, which is the only shape that works here: + plugin management is **server-admin** scoped, and on an SSO hive + nobody holds that role — `users.auto_assign_org_role` grants an + *org* role, and the built-in local admin that does hold server + admin cannot log in because the login form is disabled whenever + SSO is configured. + + That is a deliberate pair of decisions rather than an oversight, + and this option is the way through it: plugins live in the store + and in git, so they survive a container rebuild and a state reset, + and the container needs no runtime egress to grafana.com. + + Empty by default, which leaves grafana's own plugin handling + untouched. Setting it takes over the plugin directory entirely — + anything installed by other means stops being visible. + ''; + }; }; config = lib.mkIf (hyperhiveCfg.enable && cfg.enable) { @@ -437,6 +461,25 @@ in enable = true; package = cfg.package; + # Passed through unconditionally, empty default included. + # Upstream distinguishes `null` from `[ ]`, and both differences + # favour always handing it a list: + # + # - `null` points the plugin path at grafana's mutable + # `/plugins`; any list points it at a store path. + # Switching on the day someone adds their first plugin would + # bury that change inside an unrelated one. + # - upstream defaults its plugin update-check to + # `declarativePlugins == null`, so a list also stops the + # container phoning grafana.com. That is the no-runtime-egress + # property this option exists for — it should not arrive only + # once a plugin happens to be listed. + # + # Nothing is taken over by claiming the directory on a hive with no + # plugins: manual installation is already impossible here (see the + # option's description), so there is nothing in it to lose. + declarativePlugins = cfg.plugins; + settings = { server = { # Already upstream's default (measured), but pinned rather