7b7231fdbe
Supply chain: resolvePackage() used to pass any punktfunk-plugin-* or foreign-scope name straight to bun add — a typo or a squatted look-alike on npmjs.org would install operator-privileged code. Only the @punktfunk scope (pinned to the Gitea registry by the bunfig scope map) resolves by default now; anything else throws with an explanation unless --allow-public-registry is passed, and even then installs print a loud warning. Removal never gates — uninstalling stays safe regardless of origin. systemd (user unit): free hardening for well-behaved plugins — NoNewPrivileges, PrivateTmp, ProtectSystem=strict with ReadWritePaths=%h (plugin state and download dirs under $HOME keep working), and RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6. Plugins writing outside $HOME, and distros that restrict unprivileged user namespaces for user units, are handled via a documented systemctl --user edit drop-in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
50 lines
2.7 KiB
Desktop File
50 lines
2.7 KiB
Desktop File
# 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
|