CB-594: make supervision and a working fleet possible at the same time #86

Merged
ltms merged 1 commits from worker/cb594-96bead-8 into main 2026-08-16 17:32:57 +02:00
Member

Fixes #80.

What changed

  1. scripts/bridged-launchd-wrapper.sh (new) — launchd execs this instead of java directly. It execs a login shell (zsh -l), which sources ${SHARED_ENV}/tools/secrets.sh, and that shell execs the real command in its place (one process throughout). Verified empirically: run from a stripped env (env -i, no login-shell vars at all) it still resolves both WORKER_GITEA_TOKEN and AI_GATEWAY_TOKEN.

  2. Bridged.java — logs at startup which required secret env vars resolved and which are MISSING, by name only (never a value/prefix/length). The required set (Bridged.requiredSecretEnvVars, package-private + pure, unit tested in RequiredSecretEnvVarsTest) is derived from the loaded config: every non-subscription profile's tokenEnv, plus every profile's gitTokenEnv where set — not a hard-coded list. A missing var only warns; the daemon still boots.

  3. deploy/dev.ltms.bridged.plist — every CHANGEME replaced with this host's real paths (JAVA_HOME resolved via jenv's actual JDK 25.0.3 home, not /usr/libexec/java_home which reported the unrelated Applet-plugin JVM). ProgramArguments now points at the wrapper. StandardOut/ErrorPath both point at bridged/bridged.out, the same file scripts/redeploy-bridged.sh already tails, instead of an orphaned logs/ path nothing else referenced.

  4. scripts/redeploy-bridged.sh — detects whether the launchd agent is loaded (launchctl list) and reports it in --check (installed vs loaded are reported separately; --check stays read-only). When loaded, stop/start use launchctl unload/load instead of a raw kill + manual nohup, so only one supervisor ever touches the process. Reason: a bare SIGTERM makes this JVM exit 143 even with its shutdown hook running to completion (measured with a throwaway Java process carrying an equivalent shutdown hook, SIGTERM'd from a shell that could wait on it directly — exit 143, every time). KeepAlive.SuccessfulExit=false reads any nonzero exit as a crash and would restart the OLD jar before the script's own start step runs.

Verification

  • mvn -f bridged/pom.xml clean install: BUILD SUCCESS, Tests run: 813, Failures: 0, Errors: 0, Skipped: 0 (unpiped).
  • scripts/redeploy-bridged.sh --check run live: correctly reports the agent as not installed / not loaded (it has never been installed on this host, per the ticket), tokens resolve in a login shell, read-only confirmed (nothing changed).
  • Wrapper script tested directly (not via launchd) with a plain command and with a stripped environment; both work.
  • Did NOT run launchctl load/bootstrap, did not touch ~/Library/LaunchAgents, did not stop/restart the live daemon, did not edit secrets.sh — all out of scope per the ticket's hard limits.

Install command for the operator (not yet run)

cp deploy/dev.ltms.bridged.plist ~/Library/LaunchAgents/
launchctl load -w ~/Library/LaunchAgents/dev.ltms.bridged.plist
launchctl list | grep bridged

Out of scope, noticed but not touched

  • BridgedConfig.java has a pre-existing deprecation warning at compile time ("uses or overrides a deprecated API") unrelated to this change.
  • The plist's existing systemd sibling (deploy/bridged.service) was not reviewed for the same launchd-only KeepAlive-vs-script race; Linux gateways (CB-308) may need the equivalent fix if that unit is ever supervised similarly.
Fixes #80. ## What changed 1. **scripts/bridged-launchd-wrapper.sh** (new) — launchd execs this instead of java directly. It execs a login shell (`zsh -l`), which sources ${SHARED_ENV}/tools/secrets.sh, and that shell execs the real command in its place (one process throughout). Verified empirically: run from a stripped env (`env -i`, no login-shell vars at all) it still resolves both WORKER_GITEA_TOKEN and AI_GATEWAY_TOKEN. 2. **Bridged.java** — logs at startup which required secret env vars resolved and which are MISSING, by name only (never a value/prefix/length). The required set (`Bridged.requiredSecretEnvVars`, package-private + pure, unit tested in RequiredSecretEnvVarsTest) is derived from the loaded config: every non-subscription profile's `tokenEnv`, plus every profile's `gitTokenEnv` where set — not a hard-coded list. A missing var only warns; the daemon still boots. 3. **deploy/dev.ltms.bridged.plist** — every CHANGEME replaced with this host's real paths (JAVA_HOME resolved via jenv's actual JDK 25.0.3 home, not /usr/libexec/java_home which reported the unrelated Applet-plugin JVM). ProgramArguments now points at the wrapper. StandardOut/ErrorPath both point at bridged/bridged.out, the same file scripts/redeploy-bridged.sh already tails, instead of an orphaned logs/ path nothing else referenced. 4. **scripts/redeploy-bridged.sh** — detects whether the launchd agent is loaded (`launchctl list`) and reports it in --check (installed vs loaded are reported separately; --check stays read-only). When loaded, stop/start use `launchctl unload`/`load` instead of a raw kill + manual nohup, so only one supervisor ever touches the process. Reason: a bare SIGTERM makes this JVM exit 143 even with its shutdown hook running to completion (measured with a throwaway Java process carrying an equivalent shutdown hook, SIGTERM'd from a shell that could `wait` on it directly — exit 143, every time). KeepAlive.SuccessfulExit=false reads any nonzero exit as a crash and would restart the OLD jar before the script's own start step runs. ## Verification - `mvn -f bridged/pom.xml clean install`: BUILD SUCCESS, Tests run: 813, Failures: 0, Errors: 0, Skipped: 0 (unpiped). - `scripts/redeploy-bridged.sh --check` run live: correctly reports the agent as not installed / not loaded (it has never been installed on this host, per the ticket), tokens resolve in a login shell, read-only confirmed (nothing changed). - Wrapper script tested directly (not via launchd) with a plain command and with a stripped environment; both work. - Did NOT run `launchctl load/bootstrap`, did not touch ~/Library/LaunchAgents, did not stop/restart the live daemon, did not edit secrets.sh — all out of scope per the ticket's hard limits. ## Install command for the operator (not yet run) ```bash cp deploy/dev.ltms.bridged.plist ~/Library/LaunchAgents/ launchctl load -w ~/Library/LaunchAgents/dev.ltms.bridged.plist launchctl list | grep bridged ``` ## Out of scope, noticed but not touched - BridgedConfig.java has a pre-existing deprecation warning at compile time ("uses or overrides a deprecated API") unrelated to this change. - The plist's existing systemd sibling (deploy/bridged.service) was not reviewed for the same launchd-only KeepAlive-vs-script race; Linux gateways (CB-308) may need the equivalent fix if that unit is ever supervised similarly.
agent added 1 commit 2026-08-16 17:13:44 +02:00
CB-594: make supervision and a working fleet possible at the same time
CI / contract (pull_request) Successful in 44s
CI / build (pull_request) Successful in 1m40s
3ba6d6784c
Adds scripts/bridged-launchd-wrapper.sh so the launchd-run daemon still gets
WORKER_GITEA_TOKEN/AI_GATEWAY_TOKEN by execing through a login shell (launchd
never sources secrets.sh itself). bridged now logs at startup which required
token env vars (derived from each profile's tokenEnv/gitTokenEnv, not a
hand-written list) resolved or are MISSING, by name only. Fills in the real
paths in deploy/dev.ltms.bridged.plist for this host and points it at the
wrapper. scripts/redeploy-bridged.sh now detects a loaded launchd agent and
uses launchctl unload/load instead of a raw kill+nohup, because a bare
SIGTERM exits this JVM at 143 (measured) which KeepAlive.SuccessfulExit=false
reads as a crash and would race the script's own restart; --check reports
installed/loaded state and stays read-only.
ltms merged commit 613ece92dc into main 2026-08-16 17:32:57 +02:00
Sign in to join this conversation.