{
  liminix
, svc
, hostapd
, output-template
, writeText
, lib
}:
{ interface, params} :
let
  inherit (liminix.services) longrun;
  inherit (lib) concatStringsSep mapAttrsToList unique ;
  inherit (builtins) map filter attrValues length head typeOf;

  # This is not a friendly interface to configuring a wireless AP: it
  # just passes everything straight through to the hostapd config.
  # When we've worked out what the sensible options are to expose,
  # we'll add them as top-level attributes and rename params to
  # extraParams

  name = "${interface.name}.hostapd";
  defaults =  {
    driver = "nl80211";
    logger_syslog = "-1";
    logger_syslog_level = 1;
    ctrl_interface = "/run/${name}";
    ctrl_interface_group = 0;
  };
  attrs = defaults // params ;
  literal_or_output = o: ({
    string = builtins.toJSON;
    int = builtins.toJSON;
    lambda = (o: "output(${builtins.toJSON (o "service")}, ${builtins.toJSON (o "path")})");
  }.${builtins.typeOf o}) o;

  conf =
    (writeText "hostapd.conf.in"
      ((concatStringsSep
        "\n"
        (mapAttrsToList
          (n : v : "${n}={{ ${literal_or_output v} }}")
          attrs)) + "\n"));
  service = longrun {
    inherit name;
    dependencies = [ interface ];
    run = ''
      mkdir -p /run/${name}
      chmod 0700 /run/${name}
      ${output-template}/bin/output-template '{{' '}}' < ${conf} > /run/${name}/hostapd.conf
      exec ${hostapd}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}/hostapd.pid -S /run/${name}/hostapd.conf
    '';
  };
  watch = filter (f: typeOf f == "set") (attrValues attrs);
in svc.secrets.subscriber.build {
  inherit watch;
  inherit service;
  action = "restart-all";
}