feat(packaging): nix web console + plugin/script runner everywhere; fix tray zbus/tokio crash
ci / web (push) Successful in 58s
apple / swift (push) Successful in 1m24s
ci / docs-site (push) Successful in 59s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 17s
decky / build-publish (push) Successful in 25s
windows-host / package (push) Successful in 19m46s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 17s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 18s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 17s
android / android (push) Successful in 16m12s
arch / build-publish (push) Successful in 12m14s
ci / bench (push) Successful in 5m27s
apple / screenshots (push) Successful in 6m32s
ci / rust (push) Successful in 19m26s
deb / build-publish (push) Successful in 12m58s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 24m51s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 24m51s
ci / web (push) Successful in 58s
apple / swift (push) Successful in 1m24s
ci / docs-site (push) Successful in 59s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 17s
decky / build-publish (push) Successful in 25s
windows-host / package (push) Successful in 19m46s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 17s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 17s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 18s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 17s
android / android (push) Successful in 16m12s
arch / build-publish (push) Successful in 12m14s
ci / bench (push) Successful in 5m27s
apple / screenshots (push) Successful in 6m32s
ci / rust (push) Successful in 19m26s
deb / build-publish (push) Successful in 12m58s
docker / deploy-docs (push) Successful in 24s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 24m51s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 24m51s
Ship the management web console and the plugin/script runner across the packaging
matrix, and fix a Linux tray startup crash.
- nix: add punktfunk-web (Nitro SSR on bun) and punktfunk-scripting (Effect runner),
with services.punktfunk.{web,scripting} modules. Both bun node_modules come from
fixed-output derivations pinned to real deps hashes; patchShebangs the web
node_modules so orval/paraglide/vite run in the sandbox. Validated on x86_64-linux
(nix build .#punktfunk-{web,scripting,tray,host} all green).
- punktfunk-scripting now ships for deb/rpm/arch/nix/windows, mirroring the web-console
mechanics (vendored bun on deb/rpm/arch, pkgs.bun on nix; one self-contained
`bun build --target=bun` bundle). Opt-in: host Recommends it, but the systemd --user
unit ships un-started (Windows registers its scheduled task disabled).
- fix(tray): build punktfunk-tray in its OWN cargo invocation on nix/rpm/arch. Cargo
unifies features across one `cargo build`, so co-building the tray with the host pulls
the host's ashpd->zbus/tokio onto the tray's shared zbus and it panics at startup
("there is no reactor running, must be called from the context of a Tokio 1.x
runtime"). Isolated, the tray's zbus stays on async-io. The .deb already did this;
runtime-verified on nix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
# NixOS integration for punktfunk — the declarative equivalent of everything the RPM/deb do in
|
||||
# their %install + %post (packaging/rpm/punktfunk.spec, packaging/debian/build-deb.sh):
|
||||
# the systemd *user* service, the uinput/uhid/vhci udev rules, the vhci-hcd autoload, the 32 MB
|
||||
# UDP socket-buffer sysctls, the firewall openers, and the `input`-group membership for virtual
|
||||
# gamepads.
|
||||
# UDP socket-buffer sysctls, the firewall openers, the `input`-group membership for virtual
|
||||
# gamepads, the management web console (`services.punktfunk.web`, on by default with the host — the
|
||||
# RPM/deb Recommends), and the opt-in plugin/script runner (`services.punktfunk.scripting`).
|
||||
#
|
||||
# Usage (flake):
|
||||
# { inputs.punktfunk.url = "git+https://git.unom.io/unom/punktfunk";
|
||||
@@ -149,6 +150,84 @@ in
|
||||
description = "Open UDP 5353 (mDNS) so the client can auto-discover hosts on the LAN.";
|
||||
};
|
||||
};
|
||||
|
||||
# The management web console (SPAKE2 PIN pairing + host status) — the browser UI every client
|
||||
# needs. Ships by DEFAULT alongside the host (mirrors the RPM's `Recommends: punktfunk-web` and
|
||||
# the .deb the host package pulls in), auto-wired to the host's mgmt token + identity cert.
|
||||
web = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = cfg.host.enable;
|
||||
defaultText = literalExpression "config.services.punktfunk.host.enable";
|
||||
description = ''
|
||||
Run the management web console as a `systemd --user` service on TCP 47992 (HTTPS). Enabled
|
||||
by default whenever the host is enabled — set to `false` for a console-less host. It
|
||||
auto-wires to `~/.config/punktfunk/{mgmt-token,cert.pem,key.pem}` (written by the host's
|
||||
`serve`) and generates a login password on first start.
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = self.packages.${system}.punktfunk-web;
|
||||
defaultText = literalExpression "punktfunk.packages.\${system}.punktfunk-web";
|
||||
description = "The punktfunk-web package (the bun-built Nitro SSR console bundle).";
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = cfg.host.openFirewall;
|
||||
defaultText = literalExpression "config.services.punktfunk.host.openFirewall";
|
||||
description = "Open TCP 47992 so the console is reachable from other devices on the LAN.";
|
||||
};
|
||||
|
||||
autoStart = mkOption {
|
||||
type = types.bool;
|
||||
default = cfg.host.autoStart;
|
||||
defaultText = literalExpression "config.services.punktfunk.host.autoStart";
|
||||
description = ''
|
||||
Start the console automatically in every user's graphical session (adds it to the user
|
||||
`default.target`). Follows the host's `autoStart` by default — for a login-less appliance,
|
||||
enable lingering for the user as well.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# The plugin/script runner — host automation on bun. Ships with the host (the RPM/deb Recommends
|
||||
# it), but running it is OPT-IN: the `systemd --user` unit is defined yet NOT added to
|
||||
# `default.target`, because the runner is inert until you add scripts/plugins. Turn it on with
|
||||
# `systemctl --user enable --now punktfunk-scripting`.
|
||||
scripting = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = cfg.host.enable;
|
||||
defaultText = literalExpression "config.services.punktfunk.host.enable";
|
||||
description = ''
|
||||
Install the plugin/script runner and define its `systemd --user` unit
|
||||
(`punktfunk-scripting`). Enabled by default whenever the host is — but the unit is not
|
||||
auto-started (see `autoStart`), since the runner does nothing until you add scripts to
|
||||
`~/.config/punktfunk/scripts` or install `punktfunk-plugin-*` packages under
|
||||
`~/.config/punktfunk/plugins`. A plugin auto-wires to the host's mgmt token + identity cert.
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = self.packages.${system}.punktfunk-scripting;
|
||||
defaultText = literalExpression "punktfunk.packages.\${system}.punktfunk-scripting";
|
||||
description = "The punktfunk-scripting package (the bun-bundled Effect SDK runner).";
|
||||
};
|
||||
|
||||
autoStart = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Start the runner automatically in every user's graphical session (adds it to the user
|
||||
`default.target`). Off by default even when the host auto-starts — running arbitrary
|
||||
operator scripts/plugins is a deliberate opt-in; enable it once you have automation to run.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkMerge [
|
||||
@@ -226,5 +305,91 @@ in
|
||||
allowedUDPPorts = [ 5353 ];
|
||||
};
|
||||
})
|
||||
|
||||
# --- web console ---------------------------------------------------------------------------
|
||||
# The declarative equivalent of the punktfunk-web .deb / RPM subpackage: the two systemd --user
|
||||
# units (the console + its first-run password generator) plus the firewall opener, all auto-wired
|
||||
# to the host's per-user mgmt token + identity cert (no env editing on a packaged install).
|
||||
(mkIf cfg.web.enable {
|
||||
environment.systemPackages = [ cfg.web.package ];
|
||||
|
||||
networking.firewall = mkIf cfg.web.openFirewall {
|
||||
allowedTCPPorts = [ 47992 ]; # console HTTPS (packaging/linux/punktfunk-web.xml)
|
||||
};
|
||||
|
||||
# First-run setup: generate the console login password once, in the user's config dir, and
|
||||
# surface it to the --user journal. Self-gates via ConditionPathExists (mirrors
|
||||
# scripts/punktfunk-web-init.service).
|
||||
systemd.user.services.punktfunk-web-init = {
|
||||
description = "punktfunk web console first-run setup (login password)";
|
||||
documentation = [ "https://git.unom.io/unom/punktfunk" ];
|
||||
unitConfig.ConditionPathExists = "!%h/.config/punktfunk/web-password";
|
||||
path = [ pkgs.coreutils ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = "${cfg.web.package}/share/punktfunk-web/web-init.sh";
|
||||
};
|
||||
};
|
||||
|
||||
# The console itself: Nitro SSR on bun, HTTPS on 47992 with the host's identity cert, proxying
|
||||
# the host's loopback mgmt API with the bearer token injected server-side. mgmt-token is
|
||||
# REQUIRED (the host's `serve` writes it) — if absent the unit fails and Restart retries until
|
||||
# the host has created it; web-password is optional ('-'). Mirrors scripts/punktfunk-web.service.
|
||||
systemd.user.services.punktfunk-web = {
|
||||
description = "punktfunk management web console";
|
||||
documentation = [ "https://git.unom.io/unom/punktfunk" ];
|
||||
after = [ "punktfunk-web-init.service" "punktfunk-host.service" ];
|
||||
wants = [ "punktfunk-web-init.service" ];
|
||||
wantedBy = optional cfg.web.autoStart "default.target";
|
||||
environment = {
|
||||
PUNKTFUNK_MGMT_URL = "https://127.0.0.1:47990";
|
||||
PORT = "47992";
|
||||
HOST = "0.0.0.0";
|
||||
# Serve HTTPS with the host's own identity cert (the anchor native clients already pin) and
|
||||
# mark the session cookie Secure. The host's `serve` writes these PEMs.
|
||||
PUNKTFUNK_UI_TLS_CERT = "%h/.config/punktfunk/cert.pem";
|
||||
PUNKTFUNK_UI_TLS_KEY = "%h/.config/punktfunk/key.pem";
|
||||
PUNKTFUNK_UI_SECURE = "1";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
EnvironmentFile = [
|
||||
"%h/.config/punktfunk/mgmt-token"
|
||||
"-%h/.config/punktfunk/web-password"
|
||||
];
|
||||
ExecStart = "${cfg.web.package}/bin/punktfunk-web-server";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 2;
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
# --- plugin/script runner ------------------------------------------------------------------
|
||||
# Installs the runner + defines its opt-in `systemd --user` unit (mirrors the deb/rpm
|
||||
# punktfunk-scripting subpackage). NOT auto-started unless `scripting.autoStart` is set.
|
||||
(mkIf cfg.scripting.enable {
|
||||
environment.systemPackages = [ cfg.scripting.package ];
|
||||
|
||||
systemd.user.services.punktfunk-scripting = {
|
||||
description = "punktfunk plugin/script runner";
|
||||
documentation = [ "https://git.unom.io/unom/punktfunk" ];
|
||||
# Plugins talk to the host's loopback mgmt API; order after it (soft — the runner backs off
|
||||
# and retries per unit, so this is ordering only, not a hard requirement).
|
||||
after = [ "punktfunk-host.service" ];
|
||||
wantedBy = optional cfg.scripting.autoStart "default.target";
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = "${cfg.scripting.package}/bin/punktfunk-scripting";
|
||||
Restart = "on-failure";
|
||||
RestartSec = 2;
|
||||
# Deliver SIGTERM to the runner (it orchestrates the structural shutdown of its unit
|
||||
# fibers) and give it room to run their finalizers before the cgroup is reaped.
|
||||
KillMode = "mixed";
|
||||
KillSignal = "SIGTERM";
|
||||
TimeoutStopSec = 30;
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user