A moved mgmt port left every plugin and the tray dialing 47990 in silence; the Windows runner task now also writes a log file #314

Merged
enricobuehler merged 2 commits from worktree-windows-runner-log-file into main 2026-08-18 21:16:57 +00:00
Owner

Field report 2026-08-18 (Windows host on 0.30): PunktfunkScripting task Running, Playnite + Steam plugins installed, library empty, and "no logs at all for plugins" — nowhere on the box. Confirmed cause: the operator had moved the management API off 47990 (PUNKTFUNK_MGMT_BIND, the supported way to share a machine with Sunshine/Apollo).

Why it was silent

The web console followed the move — the host publishes the port it really bound to <config_dir>/mgmt-endpoint on every start (mgmt::publish_endpoint). Nothing else read that file:

  • Plugin runner / SDK resolved PUNKTFUNK_MGMT_URL → literal https://127.0.0.1:47990. The runner is a scheduled task (Windows) / systemd unit that inherits nothing from host.env — on Windows it cannot even read it — so every plugin and the runner's own log shipper (its only log door) dialed a dead port forever. Task Running, plugins never registering, empty grid, nothing to send when asked for logs.
  • Tray defaulted --mgmt-port to 47990 and told the operator to edit the autostart command line. Nobody does; it reports a running host as unreachable.

Changes

c0dcac7f — follow the published port, one source, two readers, no new file

  • sdk/src/config.ts::publishedMgmtUrl reads mgmt-endpoint; resolveConfig order is now {url}PUNKTFUNK_MGMT_URL → published file → 47990. Every plugin connect() follows on every platform, no unit/task changes. runner-cli.ts also exports it into PUNKTFUNK_MGMT_URL before any plugin loads, so a plugin still carrying an older vendored @punktfunk/host follows too (on Windows reconcileSharedSdk cannot refresh the read-only tree). An explicit PUNKTFUNK_MGMT_URL still wins.
  • pf_paths::published_mgmt_port (std-only leaf; the tray now depends on it) parses the same line. Tray mgmt_portOption<u16>: --mgmt-port pins, None re-reads the file on every poll tick, so a host restarted on a new port is picked up without relaunching the tray.
  • Docs/comments: configuration.md, troubleshooting.md, host.env.example, SDK README, installer tray comment, publish_endpoint doc.

c7c9500e — the Windows runner task writes a log file
scripting-run.cmd redirects stdout+stderr to %ProgramData%\punktfunk\plugin-state\runner.log (previous run kept as runner.log.1). plugin-state is the one dir plugins enable makes writable for LocalService and it inherits Users-read. Writability is probed with copy /y nul first; if not writable (task started before plugins enable ever ran) the runner starts unlogged as before rather than not at all. No goto (file is LF; cmd's label scan is unreliable there). Console empty-Plugins hint (en/de) and the plugin docs name the file.

Sweep for the same bug elsewhere

Web console (windows::service::spawn_web, systemd unit, NixOS module) already sourced the file; host CLI, plugin-kit (goes through the SDK), gaming-mode console and native clients derive the port from discovery / the Welcome. No other literal on a loopback path. The console's web port (47992) is not operator-configurable, so the tray's literal there is not the same class.

Verified

  • SDK: 83 tests pass (4 new: absent file → default, published line followed, env wins, blank = unset), tsc --noEmit clean, biome clean.
  • pf-paths unit test; cargo fmt --all --check clean; cargo clippy -p pf-paths --all-targets -D warnings clean.
  • cargo check -p punktfunk-tray -p pf-paths on Linux (docker rust:1.96-bookworm) — the tray is cfg-gated off macOS.
  • ⚠ Not built or run on Windows from here (no Windows box reachable); the .cmd change is verified by reading only — copy nul as a write probe, if defined blocks, leading redirect on echo.
Field report 2026-08-18 (Windows host on 0.30): `PunktfunkScripting` task Running, Playnite + Steam plugins installed, library empty, and "no logs at all for plugins" — nowhere on the box. Confirmed cause: the operator had moved the management API off 47990 (`PUNKTFUNK_MGMT_BIND`, the supported way to share a machine with Sunshine/Apollo). ## Why it was silent The web console followed the move — the host publishes the port it *really* bound to `<config_dir>/mgmt-endpoint` on every start (`mgmt::publish_endpoint`). Nothing else read that file: - **Plugin runner / SDK** resolved `PUNKTFUNK_MGMT_URL` → literal `https://127.0.0.1:47990`. The runner is a scheduled task (Windows) / systemd unit that inherits nothing from `host.env` — on Windows it cannot even read it — so every plugin *and the runner's own log shipper* (its only log door) dialed a dead port forever. Task Running, plugins never registering, empty grid, nothing to send when asked for logs. - **Tray** defaulted `--mgmt-port` to 47990 and told the operator to edit the autostart command line. Nobody does; it reports a running host as unreachable. ## Changes **`c0dcac7f` — follow the published port, one source, two readers, no new file** - `sdk/src/config.ts::publishedMgmtUrl` reads `mgmt-endpoint`; `resolveConfig` order is now `{url}` → `PUNKTFUNK_MGMT_URL` → published file → 47990. Every plugin `connect()` follows on every platform, no unit/task changes. `runner-cli.ts` also exports it into `PUNKTFUNK_MGMT_URL` before any plugin loads, so a plugin still carrying an older vendored `@punktfunk/host` follows too (on Windows `reconcileSharedSdk` cannot refresh the read-only tree). An explicit `PUNKTFUNK_MGMT_URL` still wins. - `pf_paths::published_mgmt_port` (std-only leaf; the tray now depends on it) parses the same line. Tray `mgmt_port` → `Option<u16>`: `--mgmt-port` pins, `None` re-reads the file on every poll tick, so a host restarted on a new port is picked up without relaunching the tray. - Docs/comments: `configuration.md`, `troubleshooting.md`, `host.env.example`, SDK README, installer tray comment, `publish_endpoint` doc. **`c7c9500e` — the Windows runner task writes a log file** `scripting-run.cmd` redirects stdout+stderr to `%ProgramData%\punktfunk\plugin-state\runner.log` (previous run kept as `runner.log.1`). `plugin-state` is the one dir `plugins enable` makes writable for LocalService and it inherits Users-read. Writability is probed with `copy /y nul` first; if not writable (task started before `plugins enable` ever ran) the runner starts unlogged as before rather than not at all. No `goto` (file is LF; cmd's label scan is unreliable there). Console empty-Plugins hint (en/de) and the plugin docs name the file. ## Sweep for the same bug elsewhere Web console (`windows::service::spawn_web`, systemd unit, NixOS module) already sourced the file; host CLI, plugin-kit (goes through the SDK), gaming-mode console and native clients derive the port from discovery / the `Welcome`. No other literal on a loopback path. The console's web port (47992) is not operator-configurable, so the tray's literal there is not the same class. ## Verified - SDK: 83 tests pass (4 new: absent file → default, published line followed, env wins, blank = unset), `tsc --noEmit` clean, biome clean. - `pf-paths` unit test; `cargo fmt --all --check` clean; `cargo clippy -p pf-paths --all-targets -D warnings` clean. - `cargo check -p punktfunk-tray -p pf-paths` on Linux (docker `rust:1.96-bookworm`) — the tray is cfg-gated off macOS. - ⚠ Not built or run on Windows from here (no Windows box reachable); the `.cmd` change is verified by reading only — `copy nul` as a write probe, `if defined` blocks, leading redirect on `echo`.
enricobuehler added 2 commits 2026-08-18 20:43:33 +00:00
Field report 2026-08-18, Windows host on 0.30: PunktfunkScripting task Running, Playnite and
Steam plugins installed, library empty, and "no logs at all for plugins" — nowhere on the box.

That is by construction, not by accident. The runner's only log door is the log shipper, which
tees console output to `POST /plugins/logs` over the mgmt API; the scheduled task itself had no
console and no file. So every failure that stops the runner reaching the host — LocalService
lost its read grant on plugin-token / native-cert.pem, a moved mgmt bind, a TLS pin miss, a
401 — is exactly the failure the shipper cannot report, and it leaves the same picture:
task Running, plugins never registering, an empty grid, and nothing to send when asked for logs.

`scripting-run.cmd` now redirects the runner's stdout+stderr to
`%ProgramData%\punktfunk\plugin-state\runner.log`, keeping the previous run as `runner.log.1`.
plugin-state is the one directory `plugins enable` makes writable for LocalService, and it
inherits Users-read from the config dir, so the operator can `type` it from any prompt.
Writability is probed with `copy /y nul` first; if the dir is not writable (the task was started
by the installer before `plugins enable` ever ran) the runner starts unlogged as before rather
than not at all. No `goto`: the file is stored LF and cmd's label scan is unreliable there.

The console's empty-Plugins hint (en/de) and the plugin docs now name the file; the log-ship
header no longer claims the task writes no file. Verified by reading only — no Windows box
reachable from here; the cmd semantics used (`copy nul` as a write probe, `if defined` blocks,
leading redirect on `echo`) are the boring ones.
fix(sdk,tray): follow the mgmt port the host actually bound — a moved PUNKTFUNK_MGMT_BIND left every plugin and the tray dialing 47990
ci / bun-nix (pull_request) Successful in 27s
ci / rust (pull_request) Failing after 33s
ci / web (pull_request) Successful in 1m10s
ci / docs-site (pull_request) Successful in 1m37s
nix / flake (pull_request) Failing after 1m28s
apple / swift (pull_request) Successful in 2m13s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Successful in 5m33s
windows-client / client (x64, , x86_64-pc-windows-msvc, C:\t) (pull_request) Successful in 6m47s
android / android (pull_request) Successful in 7m48s
windows-client / client (arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (pull_request) Successful in 3m6s
c0dcac7fa2
Field report 2026-08-18, confirmed: the operator had moved the management API off 47990
(`PUNKTFUNK_MGMT_BIND` in host.env — the supported way to share a box with Sunshine/Apollo). The
web console followed, because it reads `<config_dir>/mgmt-endpoint`, the one line the host
publishes on every start with the port it REALLY bound. Nothing else did:

- The plugin runner / SDK resolved `PUNKTFUNK_MGMT_URL` → literal `https://127.0.0.1:47990`.
  The runner is a scheduled task (Windows) / systemd unit that inherits nothing from host.env —
  on Windows it cannot even read it — so every plugin, and the runner's own log shipper, dialed a
  dead port forever. Task Running, plugins never registering, empty library, and "no logs at all".
- The tray defaulted `--mgmt-port` to 47990 and told the operator to edit the autostart command
  line if they moved the bind. Nobody knows to do that; the tray reports a running host as
  unreachable.

One source, two readers, no new file:
- `sdk/src/config.ts::publishedMgmtUrl` reads `mgmt-endpoint`; `resolveConfig` uses it after the
  env override and before the 47990 default. Every plugin `connect()` follows, on every platform,
  with no unit/task changes. `runner-cli.ts` additionally exports it into `PUNKTFUNK_MGMT_URL`
  before any plugin loads, so a plugin still carrying an older vendored `@punktfunk/host` follows
  too (on Windows `reconcileSharedSdk` cannot refresh the read-only tree, so old copies can
  outlive several host upgrades). An explicit PUNKTFUNK_MGMT_URL still wins.
- `pf_paths::published_mgmt_port` (std-only leaf; the tray now depends on it) parses the same
  line. The tray's `mgmt_port` becomes `Option<u16>`: `--mgmt-port` pins, `None` re-reads the file
  on every poll tick, so a host restarted on a new port is picked up without relaunching the tray.

Swept the rest: the web console (`windows::service::spawn_web`, the systemd unit, NixOS module)
already sourced the file; the host CLI, plugin-kit (goes through the SDK), gaming-mode console
and native clients derive the port from discovery / the Welcome — no other literal remained on a
loopback path. The console's web port (47992) is not operator-configurable, so the tray's
literal there is not the same bug.

Verified: SDK 83 tests pass (4 new: absent file → default, published line followed, env wins,
blank = unset), `tsc` clean, biome clean; `pf-paths` unit test; `cargo fmt --check` clean;
`cargo clippy -p pf-paths -D warnings` clean; `cargo check -p punktfunk-tray -p pf-paths` on
Linux (docker rust:1.96) — the tray is cfg-gated off macOS. Not built on Windows from here.
enricobuehler merged commit d5f2c63367 into main 2026-08-18 21:16:57 +00:00
enricobuehler deleted branch worktree-windows-runner-log-file 2026-08-18 21:16:59 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#314