Commit Graph

3 Commits

Author SHA1 Message Date
enricobuehler 45c3b96907 feat(store): plugin store host module — signed catalogs, tiered trust, install jobs
The index is the verification gate: a catalog entry pins one exact version
plus that version's tarball integrity hash, so 'verified on every release'
is a property of the data rather than a promise about process. Nothing can
express 'track latest' for a catalogued plugin.

- store/index.rs   signed index parse + ed25519 verify (ring), validate-and-drop
                   per entry, semver minHost/advisory ranges
- store/sources.rs built-in unom source (compiled-in URL + two key slots for
                   rotation) + operator sources in plugin-sources.json
- store/catalog.rs https fetch with size/timeout/redirect caps, signature before
                   parse, last-good disk cache (stale-but-usable when offline)
- store/jobs.rs    single-flight install/uninstall: registry-integrity preflight
                   against the pin, spawn the runner CLI with live log capture,
                   post-install version check with rollback, provenance record,
                   runner restart (discovery is startup-only)
- store/manifest.rs install provenance; absence means CLI-installed
- mgmt/store.rs    12 routes under /api/v1/store, denied to the plugin token
                   (a plugin that can install plugins is an escalation primitive)

Also generalizes runner discovery and listInstalled from @punktfunk/plugin-*
to ANY scope's plugin-*: catalog entries must be scoped so the scope can map
to that entry's registry, so a third-party plugin necessarily arrives under
its own scope and would otherwise install but never run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 20:48:02 +02:00
enricobuehler 7b7231fdbe feat(security): gate public-registry plugin installs + sandbox the runner unit
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>
2026-07-20 00:45:27 +02:00
enricobuehler f0c511c8fa feat(plugins): punktfunk-host plugins CLI — add/remove/list/enable/disable/status
One-liner plugin management replacing the manual scripting-dir + bunfig +
bun-add ritual: package ops forward to the bun runner (new sdk plugins
module + runner-cli subcommands, 11 tests green), enable/disable/status
drive the systemd unit on Linux and the PunktfunkScripting scheduled task
on Windows (installer support in the ISS). Docs page rewritten as .mdx
with per-platform Tabs (registered in mdx.tsx).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 16:34:43 +02:00