feat(security): give the de-privileged runner a writable plugin-state dir
The LocalService runner cannot write anywhere under %ProgramData%\punktfunk (the config dir is Users-read-only), so a state-writing plugin's saveCache / config-edit / first-run mkdir all fail EPERM — proven on-glass (rom-manager only looked fine because its state dir was pre-created by an admin run and a 0-title reconcile skipped the write). Add the one writable grant the model was missing, keeping the split crisp — code dirs RX+WA, secrets R, and now a dedicated state root RW: - plugins enable / build-scripting.ps1: create %ProgramData%\punktfunk plugin-state and grant LocalService (OI)(CI)(M); disable revokes. Users stay read-only, so another non-admin still can't tamper with a plugin's launch templates. - SDK: export pluginStateDir(name) -> <config_dir>/plugin-state/<name>. Same path on Linux (the systemd --user runner owns the config dir, writable with no grant), so plugins use one branch-free helper. Plugins must persist under pluginStateDir(), not straight under the config dir. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,27 @@ export default definePlugin({
|
||||
|
||||
In v1 a plugin is a script you run (see below); the managed runner package is a later step.
|
||||
|
||||
### Persisting state — `pluginStateDir`
|
||||
|
||||
A plugin that keeps config or a cache must write it under `pluginStateDir("<your-name>")`, **not**
|
||||
directly under the config dir:
|
||||
|
||||
```ts
|
||||
import { pluginStateDir } from "@punktfunk/host";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
const dir = pluginStateDir("rom-manager"); // <config_dir>/plugin-state/rom-manager
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
fs.writeFileSync(path.join(dir, "cache.json"), data);
|
||||
```
|
||||
|
||||
This matters on Windows: the managed runner is de-privileged (`NT AUTHORITY\LocalService`) and the
|
||||
config dir is locked read-only, so a write straight under it fails with `EPERM`. `punktfunk-host
|
||||
plugins enable` grants the runner write on exactly `plugin-state` — the config dir and your plugin's
|
||||
*code* stay read-only. On Linux the runner owns the whole config dir, so the same path is writable
|
||||
with no special step.
|
||||
|
||||
### A plugin UI in the console — `servePluginUi`
|
||||
|
||||
A plugin can surface a web UI **inside the punktfunk console** — no second password or port for the
|
||||
|
||||
Reference in New Issue
Block a user