ci / bun-nix (pull_request) Successful in 30s
ci / docs-drift (pull_request) Successful in 34s
ci / docs-site (pull_request) Successful in 1m36s
ci / web (pull_request) Successful in 1m51s
apple / swift (pull_request) Successful in 2m9s
apple / distribute (pull_request) Skipped
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Successful in 2m44s
android / android (pull_request) Successful in 6m46s
ci / rust (pull_request) Successful in 7m24s
nix / flake (pull_request) Successful in 8m27s
On a fresh install the web console's FIRST start always failed at the systemd
level:
punktfunk-web.service: Failed to load environment files: No such file or directory
punktfunk-web.service: Failed to spawn 'start' task: No such file or directory
Failed with result 'resources'.
`punktfunk-web.service` declares `After=punktfunk-host.service`, but that was
never a readiness gate: the host is `Type=simple`, so systemd considers it
started the instant it is SPAWNED — seconds before it writes anything. The
console's mandatory `EnvironmentFile=%h/.config/punktfunk/mgmt-token` then read
a file that did not exist yet. `StartLimitIntervalSec=0` meant the restart two
seconds later succeeded, so the console did come up — but the first enable
printed a hard failure on a perfectly good install, and every caller that
watches the exit status believed it.
Field report 2026-08-28 (Omarchy): `punktfunk-omarchy setup` enables the host
and the console back to back, so it lost that race every single time and
reported "Failed to start punktfunk management web console" on an install where
pacman, the repo and all three packages were fine.
The gate now lives in punktfunk-web-init.service, which the console already
orders after (`After=` + `Wants=`) and which is `Type=oneshot` — so blocking
there is the readiness gate the ordering already claimed to be. No new unit, no
new directive, no change to the host's startup path.
It waits for the console's OWN precondition rather than a proxy for it, which
matters because the two files it needs are written far apart: `mgmt-token` goes
out early in `serve` (main.rs, before the listeners) while the identity cert
comes LAST, inside `mgmt::run` -> `identity::load_or_adopt`. Waiting on the
token alone would only have moved the failure to the cert. The check mirrors
web/nitro-entry/tls-paths.mjs exactly: a token, plus a non-empty cert/key pair
from one directory — native, or legacy for a host that never took the identity
split.
Also drops `ConditionPathExists=!%h/.config/punktfunk/web-password` from
web-init. That skipped the unit from the second boot onward, which is precisely
when the wait must still run, and it tied a host-readiness gate to the presence
of an unrelated password file. web-init.sh is idempotent instead, and in steady
state (every start after the host's first run) it returns without sleeping.
Timing out is not fatal — it exits 0 so the console still starts and the
existing Restart backstop takes over, but says WHY, where the bare systemd error
it replaces named a missing file and never the host that owed it.
Mirrored into the NixOS module, and module-check.nix now asserts the absence of
the path condition it used to assert the presence of. All four Linux packages
(deb, RPM, Arch, and Bazzite via the RPM) ship the same two files, so the fix
reaches every one of them from here.
check-docs-drift.sh gate 8 runs the real script against a faked config dir:
it waits when nothing is there, still waits when only the token is there (the
leg a token-only fix would have missed), returns instantly for both the native
and the legacy identity, and stops waiting as soon as the files land. Each case
proved non-vacuous by reverting the fix and watching it go red.
32 lines
1.6 KiB
Desktop File
32 lines
1.6 KiB
Desktop File
# punktfunk web console pre-start — systemd USER one-shot.
|
|
#
|
|
# Two jobs, both of which must happen BEFORE the console's first exec:
|
|
#
|
|
# 1. Generate the console login password (PUNKTFUNK_UI_PASSWORD) in the streaming user's
|
|
# ~/.config/punktfunk on first start, surfaced to the --user journal for retrieval. A .deb
|
|
# postinst runs as root (wrong $HOME), so credential generation must happen as the user —
|
|
# hence this unit.
|
|
# 2. Block until the HOST has written the files the console cannot start without (mgmt-token +
|
|
# its identity cert/key). punktfunk-web.service's `After=punktfunk-host.service` does not do
|
|
# this: the host is Type=simple, so systemd calls it started the instant it is spawned. Being
|
|
# Type=oneshot, THIS unit is the ordering the console already declares — see web-init.sh.
|
|
#
|
|
# Pulled in by punktfunk-web.service (Wants= + After=); also runnable directly.
|
|
#
|
|
# Deliberately NOT gated on `ConditionPathExists=!%h/.config/punktfunk/web-password`, which is what
|
|
# it carried while step 1 was its only job. That condition skips the unit from the second boot
|
|
# onward — which is exactly when step 2 must still run, and it would tie a host-readiness gate to
|
|
# the presence of an unrelated password file. The script is idempotent instead: it writes the
|
|
# password only when absent, and in steady state the host's files already exist, so it returns
|
|
# without sleeping.
|
|
[Unit]
|
|
Description=punktfunk web console pre-start (login password; waits for the host)
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
RemainAfterExit=yes
|
|
ExecStart=/usr/share/punktfunk-web/web-init.sh
|
|
|
|
[Install]
|
|
WantedBy=default.target
|