{
  writeAshScript,
  liminix,
  svc,
  lib,
  serviceFns,
  output-template,
}:
{
  command,
  name,
  debug,
  username,
  password,
  lcpEcho,
  ppp-options,
  dependencies ? [ ],
}:
let
  inherit (lib)
    optional
    optionals
    escapeShellArgs
    concatStringsSep
    ;
  inherit (liminix.services) longrun;
  inherit (builtins) toJSON toString typeOf;

  ip-up = writeAshScript "ip-up" { } ''
    exec >&5 2>&5
    . ${serviceFns} 
    in_outputs ${name}
    echo $1 > ifname
    echo $2 > tty
    echo $3 > speed
    echo $4 > address
    echo $5 > peer-address
    set +o nounset
    if test -n "''${DNS1}" ;then echo ''${DNS1} > ns1 ; fi
    if test -n "''${DNS2}" ;then echo ''${DNS2} > ns2 ; fi
    test -e ipv6-address && echo >/proc/self/fd/10
  '';
  ip6-up = writeAshScript "ip6-up" { } ''
    exec >&5 2>&5
    . ${serviceFns} 
    in_outputs ${name}
    echo $4 > ipv6-address
    echo $5 > ipv6-peer-address
    test -e ifname && echo >/proc/self/fd/10
  '';
  literal_or_output =
    let
      v =
        o:
        (
          {
            string = toJSON;
            int = toJSON;
            lambda = (o: "output(${toJSON (o "service")}, ${toJSON (o "path")})");
          }
          .${typeOf o}
        )
          o;
    in
    o: "{{ ${v o} }}";

  ppp-options' =
    [
      "+ipv6"
      "noauth"
    ]
    ++ optional debug "debug"
    ++ optionals (username != null) [
      "name"
      (literal_or_output username)
    ]
    ++ optionals (password != null) [
      "password"
      (literal_or_output password)
    ]
    ++ optional lcpEcho.adaptive "lcp-echo-adaptive"
    ++ optionals (lcpEcho.interval != null) [
      "lcp-echo-interval"
      (toString lcpEcho.interval)
    ]
    ++ optionals (lcpEcho.failure != null) [
      "lcp-echo-failure"
      (toString lcpEcho.failure)
    ]
    ++ ppp-options
    ++ [
      "ip-up-script"
      ip-up
      "ipv6-up-script"
      ip6-up
      "ipparam"
      name
      "nodetach"
      # usepeerdns requests DNS servers from peer (which is good),
      # then attempts to write them to /nix/store/xxxx/ppp/resolv.conf
      # which causes an unsightly but inconsequential error message
      "usepeerdns"
      "nodefaultroute"
      "logfd"
      "2"
    ];
  service = longrun {
    inherit name;
    run = ''
      mkdir -p /run/${name}
      chmod 0700 /run/${name}
      in_outputs ${name}
      echo ${escapeShellArgs ppp-options'} | ${output-template}/bin/output-template '{{' '}}' > /run/${name}/ppp-options
      fdmove -c 5 2 \
      ${command}
    '';
    notification-fd = 10;
    timeout-up =
      if lcpEcho.failure != null then (10 + lcpEcho.failure * lcpEcho.interval) * 1000 else 60 * 1000;
    inherit dependencies;
  };
in
svc.secrets.subscriber.build {
  watch = lib.filter (n: typeOf n == "lambda") [
    username
    password
  ];
  inherit service;
}