The backend binds to `backend.host` immediately. The bind fails when the
target is not ready, because uvicorn cannot bind a name that does not
resolve, or an address that no interface holds. A unit that starts at boot
loses this race against the daemon that supplies the target, such as
tailscaled.
A bind to a Tailscale MagicDNS name shows the problem. The name is the
correct bind target, because the dashboard refuses each request with a Host
header that is different from the address that the server bound to, and a
shared machine has a different address in each tailnet. But the name does
not resolve until tailscaled is up, so the unit fails at each boot until
`Restart=on-failure` finds the moment when the name works.
A systemd user unit cannot order itself after a system unit. `After=` and
`Requires=` are silent no-ops across that boundary. Thus the wait is a poll,
and not a dependency.
This change adds three options to `services.hermes-agent.backend` on both
the NixOS module and the Home Manager module:
- `waitFor` — `null` (the default, unchanged behavior), `"hostname"`, or
`"interface"`
- `interfaceName` — the interface to take the address from
- `waitTimeout` — the time in seconds before the unit stops
With `waitFor`, ExecStart becomes a launcher that polls for the target and
then execs hermes. `exec` keeps hermes as the MainPID, so the restart logic
of systemd sees the real process. A timeout stops the unit with an error. It
does not bind a fallback address, because a fallback can expose the backend
more widely than the user intends.
The default is not changed. Without `waitFor`, ExecStart is the same
command line as before.