# Keeping `fjx supervise` alive `fjx supervise start ` is a long-running daemon. It drains in-flight ticks on SIGTERM and reloads each project's `fjx.json` on SIGHUP (see [`supervise`](./cli-reference.md#supervise)). For unattended operation it needs a process supervisor that restarts it on crash and on host reboot. Three concrete recipes follow. Pick the one that matches the host. Replace `/srv/fjx/projects/{a,b}` with the actual project directories and `fjx` with the absolute path to the binary (`which fjx`). ## systemd (Linux) User-level unit — runs as the invoking user without root. Drop into `~/.config/systemd/user/fjx-supervise.service`: ```ini [Unit] Description=fjx supervisor After=network-online.target docker.service Wants=network-online.target [Service] Type=simple ExecStart=/usr/local/bin/fjx supervise start /srv/fjx/projects/a /srv/fjx/projects/b ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=5s KillSignal=SIGTERM TimeoutStopSec=120s Environment=FJX_SUPERVISE_SOCK=%t/fjx-supervise.sock [Install] WantedBy=default.target ``` Enable and start: ```sh systemctl --user daemon-reload systemctl --user enable --now fjx-supervise.service loginctl enable-linger $USER # survive logout journalctl --user -u fjx-supervise -f ``` `systemctl --user reload fjx-supervise` triggers SIGHUP — the daemon re-reads each project's `fjx.json` without restarting (see [fjx.json](./fjx-json.md)). `TimeoutStopSec=120s` gives in-flight ticks room to drain before systemd escalates to SIGKILL; raise it if `--tick-timeout` is higher. ## launchd (macOS) User agent — drop into `~/Library/LaunchAgents/net.tfks.fjx-supervise.plist`: ```xml Labelnet.tfks.fjx-supervise ProgramArguments /usr/local/bin/fjx supervise start /Users/me/fjx/projects/a /Users/me/fjx/projects/b RunAtLoad KeepAlive SuccessfulExit StandardOutPath/Users/me/Library/Logs/fjx-supervise.log StandardErrorPath/Users/me/Library/Logs/fjx-supervise.log ``` Load and inspect: ```sh launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/net.tfks.fjx-supervise.plist launchctl print gui/$(id -u)/net.tfks.fjx-supervise tail -f ~/Library/Logs/fjx-supervise.log ``` launchd has no "reload" verb. Send SIGHUP directly to pick up `fjx.json` changes: ```sh kill -HUP $(launchctl print gui/$(id -u)/net.tfks.fjx-supervise | awk '/pid =/ {print $3}') ``` Or use the control socket: `fjx supervise reload`. ## tmux (development, ad-hoc hosts) When systemd or launchd isn't available — a shared dev VM, a remote box accessed only over SSH — a detached tmux session is the lowest-ceremony option. It does **not** survive a host reboot; pair it with an `@reboot` cron entry or just re-launch by hand. ```sh tmux new-session -d -s fjx-supervise \ 'fjx supervise start /srv/fjx/projects/a /srv/fjx/projects/b 2>&1 | tee -a ~/fjx-supervise.log' tmux attach -t fjx-supervise # watch it tmux send-keys -t fjx-supervise C-c # graceful stop (SIGINT drains like SIGTERM) ``` For auto-restart on crash, wrap the command in a `while true` loop with a short sleep — but at that point systemd/launchd is the better tool. ## Control socket The daemon exposes a Unix socket for the `status`, `pause`, `resume`, `kill`, and `reload` subcommands. Default path is `$XDG_RUNTIME_DIR/fjx-supervise-$USER.sock`, overridable with `FJX_SUPERVISE_SOCK` or `--socket`. Pin the same path in the service file and in operator shell sessions so `fjx supervise status` finds the running daemon. ## Verifying After bringing the supervisor up, sanity-check from another shell: ```sh fjx supervise status # lists in-flight ticks; empty list is fine on a cold start fjx supervise reload # round-trip the control socket ``` If `status` errors with "connection refused", the daemon isn't running or `FJX_SUPERVISE_SOCK` doesn't match.