anchor.diff:              };
   
               password = lib.mkOption {
  -              type = with lib.types; nullOr path;
  -              default = null;
  -              description = ''
  -                The path to a file containing the SMTP server password.
  -              '';
  +              type = with lib.types; nullOr (either path (attrsOf path));
  +              default = null;
  +              description = ''
  +                The SMTP server password.
  +
  +                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; };
               };
   
               from = lib.mkOption {
  
positive.code:                 umask u=rwx,g=,o=
                  cp ${parsedmarcConfig} /run/parsedmarc/parsedmarc.ini
                  chown parsedmarc:parsedmarc /run/parsedmarc/parsedmarc.ini
                  ${mkSecretReplacement cfg.settings.smtp.password}
                  ${mkSecretReplacement cfg.settings.imap.password}
                  ${mkSecretReplacement cfg.settings.elasticsearch.password}
                '' + lib.optionalString cfg.provision.localMail.enable ''
                  openssl rand -hex 64 >/run/parsedmarc/dmarc_user_passwd
                  replace-secret '@imap-password@' '/run/parsedmarc/dmarc_user_passwd' /run/parsedmarc/parsedmarc.ini
  
negative.code:         # 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: ! 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: ''
            replace-secret ${lib.escapeShellArgs [ (hashString "sha256" file) file "/run/parsedmarc/parsedmarc.ini" ]}
          '';
          secretReplacements = lib.concatMapStrings mkSecretReplacement secretPaths;
        in
          {
            wantedBy = [ "multi-user.target" ];
  