anchor.diff:          # lists, empty attrsets and null. This makes it possible to
           # list interesting options in `settings` without them always
           # ending up in the resulting config.
  -        filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! builtins.elem v [ null [] {} ])) cfg.settings;
  +        filteredConfig = lib.converge (lib.filterAttrsRecursive (_: v: ! elem v [ null [] {} ])) cfg.settings;
  +
  +        # Extract secrets (attributes set to an attrset with a
  +        # "_secret" key) from the settings and generate the commands
  +        # to run to perform the secret replacements.
  +        secretPaths = lib.catAttrs "_secret" (lib.collect isSecret filteredConfig);
           parsedmarcConfig = ini.generate "parsedmarc.ini" filteredConfig;
  -        mkSecretReplacement = file:
  -          lib.optionalString (file != null) ''
  -            replace-secret '${file}' '${file}' /run/parsedmarc/parsedmarc.ini
  -          '';
  +        mkSecretReplacement = file: ''
  +          replace-secret ${lib.escapeShellArgs [ (hashString "sha256" file) file "/run/parsedmarc/parsedmarc.ini" ]}
  +        '';
  +        secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
         in
           {
             wantedBy = [ "multi-user.target" ];
  
positive.code:     };
  
      settings = lib.mkOption {
        description = ''
          Configuration parameters to set in
          <filename>parsedmarc.ini</filename>. For a full list of
          available parameters, see
          <link xlink:href="https://domainaware.github.io/parsedmarc/#configuration-file" />.
        '';
  
        type = lib.types.submodule {
  
negative.code:             };
  
              password = lib.mkOption {
                type = with lib.types; nullOr (either path (attrsOf path));
                default = null;
                description = ''
                  The password to use when connecting to Elasticsearch,
                  if required.
  
                  Always handled as a secret whether the value is
                  wrapped in a <literal>{ _secret = ...; }</literal>
                  attrset or not (refer to <xref
                  linkend="opt-services.parsedmarc.settings" /> for
                  details).
                '';
                apply = x: if isAttrs x || x == null then x else { _secret = x; };
              };
  
              ssl = lib.mkOption {
  