{ config, pkgs, lib, ... }: let inherit (lib) mkOption types; inherit (pkgs) tailscale; cfg = config.foundation; tailnet-wait-for-ip = pkgs.writeShellScriptBin "tailnet-wait-for-ip.sh" '' echo "waiting for tailscale to acquire tailnet ip address..." while ! ${lib.getExe tailscale} ip -1; do echo "tailnet ip not yet available... sleeping for 1 second." sleep 1 done ip=$(${lib.getExe tailscale} ip -1) echo "acquired tailnet address! ip: $ip" exit 0 ''; in { options.foundation = { tailnetServices = mkOption { type = with types; listOf str; default = [ ]; example = [ "nginx" ]; description = '' services that depend on the tailnet. will be launched only after tailscaled.service is fully up and online. ''; }; }; config = let tailnetWaitOnlineService = { enable = true; after = [ "tailscaled.service" "network.target" ]; # kill service if tailscaled dies. bindsTo = [ "tailscaled.service" ]; serviceConfig = { # unit is marked as active after script exits. Type = "oneshot"; RemainAfterExit = true; # consider connection failed after 3 minutes. TimeoutStartSec = "3m"; ExecStart = "${tailnet-wait-for-ip}/bin/tailnet-wait-for-ip.sh"; }; description = '' wait for tailscale device to be online and ready ''; }; tailnetOnlineTarget = { enable = true; after = [ "tailnet-wait-online.service" "tailscaled.service" ]; requires = [ "tailnet-wait-online.service" ]; bindsTo = [ "tailscaled.service" ]; description = "tailnet is online"; }; tailnetDependantUnit = { after = [ "tailnet-online.target" ]; requires = [ "tailnet-online.target" ]; }; tailnetDependantServices = lib.genAttrs cfg.tailnetServices (x: tailnetDependantUnit); in { systemd = { services = { "tailnet-wait-online" = tailnetWaitOnlineService; } // tailnetDependantServices; targets = { "tailnet-online" = tailnetOnlineTarget; }; }; }; }