The host handler `clusev-wg.sh serve-request` aborted under `set -euo pipefail`
BEFORE writing run/wg-result-<id>.json, so the dashboard waited ~30s and showed
"Keine Antwort vom Host" even though the systemd watcher was running correctly.
The write-bridge had therefore never actually worked on a real deployment.
Two root causes, both fixed:
1. Field extraction: each `id/action/name/endpoint/port/subnet` value is pulled
with `grep -oE | head | sed`. A field that is empty or absent (an "auto"
endpoint at setup; gate/add-peer/remove-peer payloads that carry no
endpoint/port/subnet) makes grep fail → with pipefail the whole pipeline
fails → `set -e` aborts the handler before _write_result. Appended `|| true`
to every extraction so a non-match yields "" instead of killing the handler.
2. Action dispatch: gate-up/gate-down/gate-ssh-on/off and set-endpoint/port/
subnet (and remove-peer) called helpers that run require_setup, which `die`s
(exit) when the tunnel isn't configured yet — aborting serve-request before
the reply. Each handler now runs in a subshell `( ... )` so an internal exit
becomes a caught non-zero and still yields a clean failure result.
(add-peer/setup were already isolated via their $(...) command substitution.)
Verified end-to-end: all ten actions now write a result for minimal/empty-field
payloads instead of timing out. New smoke test docker/wg/serve-request.test.sh
(root-bypassed, no WireGuard needed) guards both regressions. shellcheck clean,
348 PHP tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>