# punktfunk plugin/script runner — systemd USER unit (bun runtime, OPT-IN). # # Runs the operator's automation under supervision: loose files in ~/.config/punktfunk/scripts/ and # installed `punktfunk-plugin-*` packages under ~/.config/punktfunk/plugins/. Each unit is an Effect # fiber (a plugin restarts on failure with capped-jittered backoff; a bare script is one-shot). # SIGTERM interrupts the whole tree STRUCTURALLY, so every plugin's scoped finalizers run before # exit (clean deregister / preset release) — hence the generous stop timeout below. # # OPT-IN — unlike punktfunk-web, the package does NOT auto-enable this: the runner does nothing until # you add scripts or install plugins. Turn it on once you have automation to run: # systemctl --user enable --now punktfunk-scripting # # Auto-wired like the console: a plugin's connect() reads the host's SCOPED plugin token + identity # cert from ~/.config/punktfunk/{plugin-token,cert.pem} (written by the host's `serve`) — no env # editing. The plugin token authorizes the plugin surface but not hook registration or pairing # administration; a script that needs the admin surface sets PUNKTFUNK_MGMT_TOKEN explicitly. [Unit] 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 (ordering only, no Requires): # the runner supervises each unit with backoff, so a plugin started before the host simply retries. After=punktfunk-host.service [Service] Type=simple ExecStart=/usr/bin/punktfunk-scripting Restart=on-failure RestartSec=2 # Deliver the stop signal to the runner process itself (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 # Sandbox: free hardening for well-behaved plugins. The filesystem is read-only outside the home # directory (ReadWritePaths keeps plugin state, download dirs, and ~/.config/punktfunk writable); # /tmp is private; no setuid re-escalation; sockets limited to what automation actually uses # (loopback mgmt API, LAN/IPv6 webhooks, unix sockets). A plugin that must write OUTSIDE $HOME # (e.g. a library on another mount) gets a drop-in: # systemctl --user edit punktfunk-scripting → [Service]\nReadWritePaths=/mnt/games # NOTE: the mount-namespace options (ProtectSystem/PrivateTmp) need unprivileged user namespaces # for a *user* unit; on kernels/distros that restrict those, drop them via the same drop-in. NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ReadWritePaths=%h RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 [Install] WantedBy=default.target