{ ... }: {
  flake.nixosModules.systemCachingAttic =
    { config, lib, pkgs, options, ... }:
    let
      cfg = config.chiasson.system.caching.attic;
    in
    {
      options.chiasson.system.caching.attic = with lib; {
        enable = mkOption {
          type = types.bool;
          default = false;
          description = "Enable Attic cache integration for this host (substituters + trusted key).";
        };

        cacheName = mkOption {
          type = types.str;
          default = "";
          example = "nixos-new";
          description = "Attic cache name; required when `chiasson.system.caching.attic.enable = true`.";
        };

        endpoint = mkOption {
          type = types.str;
          default = "";
          example = "http://nix-server:8080";
          description = "Attic API endpoint base URL (no cache segment).";
        };

        publicKey = mkOption {
          type = types.str;
          default = "";
          example = "nixos-new:WcnO6s4aVkB6CKRaPPpKvHLZykWXASV6c+/Ssg8uQEY=";
          description = "Cache public signing key from `attic cache info`.";
        };

        tokenFile = mkOption {
          type = types.nullOr types.str;
          default = null;
          example = "/run/secrets/caching/attic/token";
          description = ''
            Default token path; push/cli token options override when set.
          '';
        };

        push = {
          enable = mkEnableOption ''
            Post-build hook → Attic push (needs a token file somewhere).
          '';

          tokenFile = mkOption {
            type = types.nullOr types.str;
            default = null;
            example = "/run/secrets/caching/attic/push-token";
            description = ''
              Push token file (sops path). Falls back to `chiasson.system.caching.attic.tokenFile`.
            '';
          };

          retries = mkOption {
            type = types.int;
            default = 3;
            description = "Attempts per push before giving up (handles transient Attic/network stalls).";
          };

          background = mkOption {
            type = types.bool;
            default = true;
            description = ''
              Run `attic push` in the background so the build finishes immediately.
              Failures/timeouts are logged; they do not fail the build.
            '';
          };

          timeoutSec = mkOption {
            type = types.int;
            default = 600;
            description = "Kill `attic push` after this many seconds (background or foreground).";
          };

          uploadJobs = mkOption {
            type = types.int;
            default = 3;
            description = "Parallel upload workers (`attic push -j`). Lower if the server stalls under load.";
          };

          logFile = mkOption {
            type = types.str;
            default = "";
            description = ''
              Append push logs here. Empty → `$XDG_RUNTIME_DIR/nix-attic-push.log`
              (or `/tmp/nix-attic-push-$UID.log`).
            '';
          };

          excludedPatterns = mkOption {
            type = types.listOf types.str;
            default = [ ];
            example = [ "wallpaper" "hm_wallpapers" ".iso" "-source" "large-assets" ];
            description = "Substring patterns: closures matching any pattern are skipped for Attic push.";
          };
        };

        userCli = {
          enable = mkOption {
            type = types.bool;
            default = false;
            description = ''
              Install `attic-client` + HM `~/.config/attic/config.toml` when a token path resolves.
            '';
          };

          serverName = mkOption {
            type = types.str;
            default = "attic";
            description = "Local label for this Attic server (default-server and [servers.<name>] in config.toml).";
          };

          tokenFile = mkOption {
            type = types.nullOr types.str;
            default = null;
            example = "/run/secrets/caching/attic/cli-token";
            description = ''
              CLI token file; falls back to `chiasson.system.caching.attic.tokenFile`. No file -> no config.toml.
            '';
          };

        };
      };

      config = let
        endpointBase = lib.strings.removeSuffix "/" cfg.endpoint;
        enabled = cfg.enable && cfg.cacheName != "" && endpointBase != "" && cfg.publicKey != "";
        cacheUrl = "${endpointBase}/${cfg.cacheName}";
        pushTokenFile = if cfg.push.tokenFile != null then cfg.push.tokenFile else cfg.tokenFile;
        atticTomlServersSection = "[servers.ci]";
        hmAtticCliModule =
          { lib, osConfig ? { }, ... }:
          let
            ac = osConfig.chiasson.system.caching.attic or { };
            cli = ac.userCli or { };
            cliTokenFile = if (cli.tokenFile or null) != null then cli.tokenFile else (ac.tokenFile or null);
            hmEnabled =
              (cli.enable or false)
              && (ac.cacheName or "") != ""
              && (lib.removeSuffix "/" (ac.endpoint or "")) != ""
              && cliTokenFile != null;
            srv = cli.serverName or "attic";
            ep = lib.removeSuffix "/" ac.endpoint;
          in
          {
            xdg.configFile."attic/config.toml" = lib.mkIf hmEnabled {
              text = ''
                default-server = ${builtins.toJSON srv}

                [servers.${srv}]
                endpoint = ${builtins.toJSON ep}
                token-file = ${builtins.toJSON cliTokenFile}
              '';
            };
          };
      in
      lib.mkMerge [
        {
          assertions = [
            {
              assertion = !cfg.push.enable || pushTokenFile != null;
              message = "chiasson.system.caching.attic.push: when push.enable is true, set push.tokenFile or chiasson.system.caching.attic.tokenFile.";
            }
            {
              assertion = !cfg.push.enable || enabled;
              message = "chiasson.system.caching.attic.push.enable requires a fully configured chiasson.system.caching.attic (enable, cacheName, endpoint, publicKey).";
            }
          ];
        }

        (lib.mkIf enabled {
          nix.settings = {
            substituters = lib.mkAfter [ cacheUrl ];
            trusted-public-keys = lib.mkAfter [ cfg.publicKey ];
          };

          nix.settings.post-build-hook = lib.mkIf cfg.push.enable (pkgs.writeShellScript "upload-to-attic" ''
            set -eu
            set -f

            export PATH="${lib.makeBinPath [
              pkgs.attic-client
              pkgs.nix
              pkgs.gnused
              pkgs.coreutils
            ]}:$PATH"

            echo "attic: hook start drv=''${DRV_PATH:-<unknown>}" >&2
            echo "attic: endpoint=${lib.escapeShellArg endpointBase} cache=${lib.escapeShellArg cfg.cacheName}" >&2

            token_path=${lib.escapeShellArg pushTokenFile}
            if [ ! -r "$token_path" ]; then
              echo "attic: skipping push (token not readable at $token_path)" >&2
              exit 0
            fi

            ATTIC_TOKEN="$(tr -d '\n' < "$token_path")"
            if [ -z "$ATTIC_TOKEN" ]; then
              echo "attic: skipping push (token is empty)" >&2
              exit 0
            fi

            push_paths=""
            skipped_roots=0
            pushed_roots=0
            seen_roots=0
            for path in $OUT_PATHS; do
              seen_roots=$((seen_roots + 1))
              skip=0
              skip_reason=""

              closure_paths="$(nix-store -qR "$path" 2>/dev/null || true)"
              for candidate in "$path" $closure_paths; do
                while IFS= read -r pat; do
                  pat="$(printf '%s' "$pat" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
                  [ -z "$pat" ] && continue
                  case "$candidate" in
                    *"$pat"*)
                      skip=1
                      skip_reason="$pat ($candidate)"
                      break
                      ;;
                  esac
                done << 'EXCLUDE_PATTERNS'
            ${lib.concatStringsSep "\n" cfg.push.excludedPatterns}
            EXCLUDE_PATTERNS
                if [ "$skip" -eq 1 ]; then
                  break
                fi
              done

              if [ "$skip" -eq 0 ]; then
                push_paths="$push_paths $path"
                pushed_roots=$((pushed_roots + 1))
              else
                skipped_roots=$((skipped_roots + 1))
                echo "attic: skipping root $path (matches exclude pattern via $skip_reason)" >&2
              fi
            done

            echo "attic: summary seen=$seen_roots selected=$pushed_roots skipped=$skipped_roots" >&2

            if [ -z "$push_paths" ]; then
              echo "attic: nothing selected for push" >&2
              exit 0
            fi

            runtime_dir="''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
            attic_config_home="$runtime_dir/nix-attic-hook"
            export XDG_CONFIG_HOME="$attic_config_home"
            mkdir -p "$attic_config_home/attic"
            {
              printf '%s\n' 'default-server = "ci"'
              printf '\n'
              printf '%s\n' ${builtins.toJSON atticTomlServersSection}
              printf 'endpoint = %s\n' ${builtins.toJSON endpointBase}
              printf 'token = "%s"\n' "$ATTIC_TOKEN"
            } > "$attic_config_home/attic/config.toml"

            log_file=${lib.escapeShellArg cfg.push.logFile}
            if [ -z "$log_file" ]; then
              log_file="$runtime_dir/nix-attic-push.log"
            fi
            mkdir -p "$(dirname "$log_file")"

            push_cmd() {
              attempt=1
              max_attempts=${toString cfg.push.retries}
              while [ "$attempt" -le "$max_attempts" ]; do
                echo "attic: push attempt $attempt/$max_attempts $(date -Is) paths:$push_paths" >&2
                if timeout ${toString cfg.push.timeoutSec} \
                  attic push -j ${toString cfg.push.uploadJobs} \
                  ${lib.escapeShellArg "ci:${cfg.cacheName}"} \
                  $push_paths; then
                  echo "attic: push succeeded $(date -Is)" >&2
                  return 0
                fi
                echo "attic: push failed or timed out (attempt $attempt/$max_attempts)" >&2
                attempt=$((attempt + 1))
                [ "$attempt" -le "$max_attempts" ] && sleep 5
              done
              return 1
            }

            ${lib.optionalString cfg.push.background ''
            echo "attic: scheduling background push → $log_file" >&2
            (
              push_cmd
            ) >> "$log_file" 2>&1 &
            exit 0
            ''}
            ${lib.optionalString (!cfg.push.background) ''
            push_cmd 2>&1 | tee -a "$log_file"
            exit 0
            ''}
          '');

          environment.systemPackages = lib.mkIf cfg.userCli.enable [ pkgs.attic-client ];
        })
        (lib.optionalAttrs (lib.hasAttrByPath [ "home-manager" "sharedModules" ] options) {
          "home-manager".sharedModules = lib.mkIf (enabled && cfg.userCli.enable) [ hmAtticCliModule ];
        })
      ];
    };
}
