diff --git a/.eslintrc.json b/.eslintrc.json
index 6499d3b..5966041 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -29,6 +29,7 @@
         "accessibility": "no-public"
       }
     ],
+    "@typescript-eslint/no-base-to-string": "error",
     "@typescript-eslint/no-require-imports": "error",
     "@typescript-eslint/array-type": "error",
     "@typescript-eslint/await-thenable": "error",
diff --git a/action.yml b/action.yml
index 44a4a7f..8de381d 100644
--- a/action.yml
+++ b/action.yml
@@ -16,7 +16,7 @@ inputs:
     default: https://cache.nixos.org
   diagnostic-endpoint:
     description: "Diagnostic endpoint url where diagnostics and performance data is sent. To disable set this to an empty string."
-    default: "https://install.determinate.systems/magic-nix-cache/perf"
+    default: "-"
   use-flakehub:
     description: "Whether to upload build results to FlakeHub Cache."
     default: true
diff --git a/dist/index.js b/dist/index.js
index f2c2d70..efd3b8e 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -86693,6 +86693,8 @@ var exec = __nccwpck_require__(7775);
 var external_os_ = __nccwpck_require__(2037);
 ;// CONCATENATED MODULE: external "node:crypto"
 const external_node_crypto_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:crypto");
+;// CONCATENATED MODULE: external "node:dns/promises"
+const external_node_dns_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:dns/promises");
 // EXTERNAL MODULE: ./node_modules/.pnpm/@actions+cache@3.2.4/node_modules/@actions/cache/lib/cache.js
 var cache = __nccwpck_require__(6878);
 ;// CONCATENATED MODULE: ./node_modules/.pnpm/@sindresorhus+is@6.3.1/node_modules/@sindresorhus/is/dist/index.js
@@ -94089,7 +94091,7 @@ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createReq
 const external_node_stream_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:stream/promises");
 ;// CONCATENATED MODULE: external "node:zlib"
 const external_node_zlib_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:zlib");
-;// CONCATENATED MODULE: ./node_modules/.pnpm/github.com+DeterminateSystems+detsys-ts@5fcb0532d85556ebc2de286e483885976531339d_uqngfub4ls4loys67iy653x57e/node_modules/detsys-ts/dist/index.js
+;// CONCATENATED MODULE: ./node_modules/.pnpm/github.com+DeterminateSystems+detsys-ts@9d66d2c89c150f796165fdcc20b3be538807c0f4_46ybmzqhaua4eiwu3nj2qm63te/node_modules/detsys-ts/dist/index.js
 var __defProp = Object.defineProperty;
 var __export = (target, all) => {
   for (var name in all)
@@ -94175,9 +94177,9 @@ async function readAsyncOsReleaseFile(fileList, options) {
 ${fileData}`);
       }
       break;
-    } catch (error2) {
+    } catch (error3) {
       if (options.debug) {
-        console.error(error2);
+        console.error(error3);
       }
     }
   }
@@ -94199,9 +94201,9 @@ function readSyncOsreleaseFile(releaseFileList, options) {
 ${fileData}`);
       }
       break;
-    } catch (error2) {
+    } catch (error3) {
       if (options.debug) {
-        console.error(error2);
+        console.error(error3);
       }
     }
   }
@@ -94393,6 +94395,192 @@ function hashEnvironmentVariables(prefix, variables) {
   return `${prefix}-${hash.digest("hex")}`;
 }
 
+// src/errors.ts
+function stringifyError(e) {
+  if (e instanceof Error) {
+    return e.message;
+  } else if (typeof e === "string") {
+    return e;
+  } else {
+    return JSON.stringify(e);
+  }
+}
+
+// src/ids-host.ts
+
+
+var DEFAULT_LOOKUP = "_detsys_ids._tcp.install.determinate.systems.";
+var ALLOWED_SUFFIXES = [
+  ".install.determinate.systems",
+  ".install.detsys.dev"
+];
+var DEFAULT_IDS_HOST = "https://install.determinate.systems";
+var LOOKUP = process.env["IDS_LOOKUP"] ?? DEFAULT_LOOKUP;
+var IdsHost = class {
+  constructor(idsProjectName, diagnosticsSuffix, runtimeDiagnosticsUrl) {
+    this.idsProjectName = idsProjectName;
+    this.diagnosticsSuffix = diagnosticsSuffix;
+    this.runtimeDiagnosticsUrl = runtimeDiagnosticsUrl;
+  }
+  markCurrentHostBroken() {
+    this.prioritizedURLs?.shift();
+  }
+  setPrioritizedUrls(urls) {
+    this.prioritizedURLs = urls;
+  }
+  async getRootUrl() {
+    const idsHost = process.env["IDS_HOST"];
+    if (idsHost !== void 0) {
+      try {
+        return new URL(idsHost);
+      } catch (err) {
+        core.error(
+          `IDS_HOST environment variable is not a valid URL. Ignoring. ${stringifyError(err)}`
+        );
+      }
+    }
+    let url = void 0;
+    try {
+      const urls = await this.getUrlsByPreference();
+      url = urls[0];
+    } catch (err) {
+      core.error(
+        `Error collecting IDS URLs by preference: ${stringifyError(err)}`
+      );
+    }
+    if (url === void 0) {
+      url = new URL(DEFAULT_IDS_HOST);
+    }
+    return new URL(url);
+  }
+  async getDiagnosticsUrl() {
+    if (this.runtimeDiagnosticsUrl === "") {
+      return void 0;
+    }
+    if (this.runtimeDiagnosticsUrl !== "-" && this.runtimeDiagnosticsUrl !== void 0) {
+      try {
+        return new URL(this.runtimeDiagnosticsUrl);
+      } catch (err) {
+        core.info(
+          `User-provided diagnostic endpoint ignored: not a valid URL: ${stringifyError(err)}`
+        );
+      }
+    }
+    try {
+      const diagnosticUrl = await this.getRootUrl();
+      diagnosticUrl.pathname += this.idsProjectName;
+      diagnosticUrl.pathname += "/";
+      diagnosticUrl.pathname += this.diagnosticsSuffix || "diagnostics";
+      return diagnosticUrl;
+    } catch (err) {
+      core.info(
+        `Generated diagnostic endpoint ignored, and diagnostics are disabled: not a valid URL: ${stringifyError(err)}`
+      );
+      return void 0;
+    }
+  }
+  async getUrlsByPreference() {
+    if (this.prioritizedURLs === void 0) {
+      this.prioritizedURLs = orderRecordsByPriorityWeight(
+        await discoverServiceRecords()
+      ).flatMap((record) => recordToUrl(record) || []);
+    }
+    return this.prioritizedURLs;
+  }
+};
+function recordToUrl(record) {
+  const urlStr = `https://${record.name}:${record.port}`;
+  try {
+    return new URL(urlStr);
+  } catch (err) {
+    core.debug(
+      `Record ${JSON.stringify(record)} produced an invalid URL: ${urlStr} (${err})`
+    );
+    return void 0;
+  }
+}
+async function discoverServiceRecords() {
+  return await discoverServicesStub((0,external_node_dns_promises_namespaceObject.resolveSrv)(LOOKUP), 1e3);
+}
+async function discoverServicesStub(lookup, timeout) {
+  const defaultFallback = new Promise(
+    (resolve, _reject) => {
+      setTimeout(resolve, timeout, []);
+    }
+  );
+  let records;
+  try {
+    records = await Promise.race([lookup, defaultFallback]);
+  } catch (reason) {
+    core.debug(`Error resolving SRV records: ${stringifyError(reason)}`);
+    records = [];
+  }
+  const acceptableRecords = records.filter((record) => {
+    for (const suffix of ALLOWED_SUFFIXES) {
+      if (record.name.endsWith(suffix)) {
+        return true;
+      }
+    }
+    core.debug(
+      `Unacceptable domain due to an invalid suffix: ${record.name}`
+    );
+    return false;
+  });
+  if (acceptableRecords.length === 0) {
+    core.debug(`No records found for ${LOOKUP}`);
+  } else {
+    core.debug(
+      `Resolved ${LOOKUP} to ${JSON.stringify(acceptableRecords)}`
+    );
+  }
+  return acceptableRecords;
+}
+function orderRecordsByPriorityWeight(records) {
+  const byPriorityWeight = /* @__PURE__ */ new Map();
+  for (const record of records) {
+    const existing = byPriorityWeight.get(record.priority);
+    if (existing) {
+      existing.push(record);
+    } else {
+      byPriorityWeight.set(record.priority, [record]);
+    }
+  }
+  const prioritizedRecords = [];
+  const keys = Array.from(byPriorityWeight.keys()).sort(
+    (a, b) => a - b
+  );
+  for (const priority of keys) {
+    const recordsByPrio = byPriorityWeight.get(priority);
+    if (recordsByPrio === void 0) {
+      continue;
+    }
+    prioritizedRecords.push(...weightedRandom(recordsByPrio));
+  }
+  return prioritizedRecords;
+}
+function weightedRandom(records) {
+  const scratchRecords = records.slice();
+  const result = [];
+  while (scratchRecords.length > 0) {
+    const weights = [];
+    {
+      for (let i = 0; i < scratchRecords.length; i++) {
+        weights.push(
+          scratchRecords[i].weight + (i > 0 ? scratchRecords[i - 1].weight : 0)
+        );
+      }
+    }
+    const point = Math.random() * weights[weights.length - 1];
+    for (let selectedIndex = 0; selectedIndex < weights.length; selectedIndex++) {
+      if (weights[selectedIndex] > point) {
+        result.push(scratchRecords.splice(selectedIndex, 1)[0]);
+        break;
+      }
+    }
+  }
+  return result;
+}
+
 // src/inputs.ts
 var inputs_exports = {};
 __export(inputs_exports, {
@@ -94553,21 +94741,6 @@ function noisilyGetInput(suffix, legacyPrefix) {
 
 
 
-
-// src/errors.ts
-function stringifyError(e) {
-  if (e instanceof Error) {
-    return e.message;
-  } else if (typeof e === "string") {
-    return e;
-  } else {
-    return JSON.stringify(e);
-  }
-}
-
-// src/index.ts
-var DEFAULT_IDS_HOST = "https://install.determinate.systems";
-var IDS_HOST = process.env["IDS_HOST"] ?? DEFAULT_IDS_HOST;
 var EVENT_EXCEPTION = "exception";
 var EVENT_ARTIFACT_CACHE_HIT = "artifact_cache_hit";
 var EVENT_ARTIFACT_CACHE_MISS = "artifact_cache_miss";
@@ -94601,6 +94774,13 @@ var DetSysAction = class {
   }
   constructor(actionOptions) {
     this.actionOptions = makeOptionsConfident(actionOptions);
+    this.idsHost = new IdsHost(
+      this.actionOptions.idsProjectName,
+      actionOptions.diagnosticsSuffix,
+      // Note: we don't use actionsCore.getInput('diagnostic-endpoint') on purpose:
+      // getInput silently converts absent data to an empty string.
+      process.env["INPUT_DIAGNOSTIC-ENDPOINT"]
+    );
     this.exceptionAttachments = /* @__PURE__ */ new Map();
     this.nixStoreTrust = "unknown";
     this.strictMode = getBool("_internal-strict-mode");
@@ -94612,9 +94792,9 @@ var DetSysAction = class {
       },
       hooks: {
         beforeRetry: [
-          (error2, retryCount) => {
+          (error3, retryCount) => {
             core.info(
-              `Retrying after error ${error2.code}, retry #: ${retryCount}`
+              `Retrying after error ${error3.code}, retry #: ${retryCount}`
             );
           }
         ]
@@ -94691,8 +94871,8 @@ var DetSysAction = class {
    * Execute the Action as defined.
    */
   execute() {
-    this.executeAsync().catch((error2) => {
-      console.log(error2);
+    this.executeAsync().catch((error3) => {
+      console.log(error3);
       process.exitCode = 1;
     });
   }
@@ -94703,8 +94883,8 @@ var DetSysAction = class {
   addFact(key, value) {
     this.facts[key] = value;
   }
-  getDiagnosticsUrl() {
-    return this.actionOptions.diagnosticsUrl;
+  async getDiagnosticsUrl() {
+    return await this.idsHost.getDiagnosticsUrl();
   }
   getUniqueId() {
     return this.identity.run_differentiator || process.env.RUNNER_TRACKING_ID || (0,external_node_crypto_namespaceObject.randomUUID)();
@@ -94817,8 +94997,8 @@ var DetSysAction = class {
       `Downloading ${this.actionOptions.name} for ${this.architectureFetchSuffix}`
     );
     try {
-      core.info(`Fetching from ${this.getSourceUrl()}`);
-      const correlatedUrl = this.getSourceUrl();
+      core.info(`Fetching from ${await this.getSourceUrl()}`);
+      const correlatedUrl = await this.getSourceUrl();
       correlatedUrl.searchParams.set("ci", "github");
       correlatedUrl.searchParams.set(
         "correlation",
@@ -94829,7 +95009,7 @@ var DetSysAction = class {
         const v = versionCheckup.headers.etag;
         this.addFact(FACT_SOURCE_URL_ETAG, v);
         core.debug(
-          `Checking the tool cache for ${this.getSourceUrl()} at ${v}`
+          `Checking the tool cache for ${await this.getSourceUrl()} at ${v}`
         );
         const cached = await this.getCachedVersion(v);
         if (cached) {
@@ -94877,13 +95057,13 @@ var DetSysAction = class {
     this.recordEvent(`complete_${this.executionPhase}`);
     await this.submitEvents();
   }
-  getSourceUrl() {
+  async getSourceUrl() {
     const p = this.sourceParameters;
     if (p.url) {
       this.addFact(FACT_SOURCE_URL, p.url);
       return new URL(p.url);
     }
-    const fetchUrl = new URL(IDS_HOST);
+    const fetchUrl = await this.idsHost.getRootUrl();
     fetchUrl.pathname += this.actionOptions.idsProjectName;
     if (p.tag) {
       fetchUrl.pathname += `/tag/${p.tag}`;
@@ -95039,7 +95219,8 @@ var DetSysAction = class {
     }
   }
   async submitEvents() {
-    if (this.actionOptions.diagnosticsUrl === void 0) {
+    const diagnosticsUrl = await this.idsHost.getDiagnosticsUrl();
+    if (diagnosticsUrl === void 0) {
       core.debug(
         "Diagnostics are disabled. Not sending the following events:"
       );
@@ -95052,7 +95233,7 @@ var DetSysAction = class {
       events: this.events
     };
     try {
-      await this.client.post(this.actionOptions.diagnosticsUrl, {
+      await this.client.post(diagnosticsUrl, {
         json: batch,
         timeout: {
           request: DIAGNOSTIC_ENDPOINT_TIMEOUT_MS
@@ -95062,12 +95243,28 @@ var DetSysAction = class {
       core.debug(
         `Error submitting diagnostics event: ${stringifyError2(e)}`
       );
+      this.idsHost.markCurrentHostBroken();
+      const secondaryDiagnosticsUrl = await this.idsHost.getDiagnosticsUrl();
+      if (secondaryDiagnosticsUrl !== void 0) {
+        try {
+          await this.client.post(secondaryDiagnosticsUrl, {
+            json: batch,
+            timeout: {
+              request: DIAGNOSTIC_ENDPOINT_TIMEOUT_MS
+            }
+          });
+        } catch (err) {
+          core.debug(
+            `Error submitting diagnostics event to secondary host (${secondaryDiagnosticsUrl}): ${stringifyError2(err)}`
+          );
+        }
+      }
     }
     this.events = [];
   }
 };
-function stringifyError2(error2) {
-  return error2 instanceof Error || typeof error2 == "string" ? error2.toString() : JSON.stringify(error2);
+function stringifyError2(error3) {
+  return error3 instanceof Error || typeof error3 == "string" ? error3.toString() : JSON.stringify(error3);
 }
 function makeOptionsConfident(actionOptions) {
   const idsProjectName = actionOptions.idsProjectName ?? actionOptions.name;
@@ -95077,72 +95274,12 @@ function makeOptionsConfident(actionOptions) {
     eventPrefix: actionOptions.eventPrefix || "action:",
     fetchStyle: actionOptions.fetchStyle,
     legacySourcePrefix: actionOptions.legacySourcePrefix,
-    requireNix: actionOptions.requireNix,
-    diagnosticsUrl: determineDiagnosticsUrl(
-      idsProjectName,
-      actionOptions.diagnosticsUrl
-    )
+    requireNix: actionOptions.requireNix
   };
   core.debug("idslib options:");
   core.debug(JSON.stringify(finalOpts, void 0, 2));
   return finalOpts;
 }
-function determineDiagnosticsUrl(idsProjectName, urlOption) {
-  if (urlOption === null) {
-    return void 0;
-  }
-  if (urlOption !== void 0) {
-    return urlOption;
-  }
-  {
-    const providedDiagnosticEndpoint = process.env["INPUT_DIAGNOSTIC-ENDPOINT"];
-    if (providedDiagnosticEndpoint === "") {
-      return void 0;
-    }
-    if (providedDiagnosticEndpoint !== void 0) {
-      try {
-        return mungeDiagnosticEndpoint(new URL(providedDiagnosticEndpoint));
-      } catch (e) {
-        core.info(
-          `User-provided diagnostic endpoint ignored: not a valid URL: ${stringifyError2(e)}`
-        );
-      }
-    }
-  }
-  try {
-    const diagnosticUrl = new URL(IDS_HOST);
-    diagnosticUrl.pathname += idsProjectName;
-    diagnosticUrl.pathname += "/diagnostics";
-    return diagnosticUrl;
-  } catch (e) {
-    core.info(
-      `Generated diagnostic endpoint ignored: not a valid URL: ${stringifyError2(e)}`
-    );
-  }
-  return void 0;
-}
-function mungeDiagnosticEndpoint(inputUrl) {
-  if (DEFAULT_IDS_HOST === IDS_HOST) {
-    return inputUrl;
-  }
-  try {
-    const defaultIdsHost = new URL(DEFAULT_IDS_HOST);
-    const currentIdsHost = new URL(IDS_HOST);
-    if (inputUrl.origin !== defaultIdsHost.origin) {
-      return inputUrl;
-    }
-    inputUrl.protocol = currentIdsHost.protocol;
-    inputUrl.host = currentIdsHost.host;
-    inputUrl.username = currentIdsHost.username;
-    inputUrl.password = currentIdsHost.password;
-    return inputUrl;
-  } catch (e) {
-    core.info(
-      `Default or overridden IDS host isn't a valid URL: ${stringifyError2(e)}`
-    );
-  }
-  return inputUrl;
-}
 
 /*!
  * linux-release-info
@@ -95233,7 +95370,8 @@ var MagicNixCacheAction = class extends DetSysAction {
       name: "magic-nix-cache",
       fetchStyle: "gh-env-style",
       idsProjectName: "magic-nix-cache-closure",
-      requireNix: "warn"
+      requireNix: "warn",
+      diagnosticsSuffix: "perf"
     });
     this.hostAndPort = inputs_exports.getString("listen");
     this.diffStore = inputs_exports.getBool("diff-store");
diff --git a/dist/index.js.map b/dist/index.js.map
index fcb78b8..8bcbffa 100644
--- a/dist/index.js.map
+++ b/dist/index.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/helpers.ts","../src/index.ts"],"sourcesContent":["import * as actionsCore from \"@actions/core\";\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport path from \"node:path\";\nimport { Tail } from \"tail\";\n\nexport function tailLog(daemonDir: string): Tail {\n  const log = new Tail(path.join(daemonDir, \"daemon.log\"));\n  actionsCore.debug(`tailing daemon.log...`);\n  log.on(\"line\", (line) => {\n    actionsCore.info(line);\n  });\n  return log;\n}\n\nexport async function netrcPath(): Promise<string> {\n  const expectedNetrcPath = path.join(\n    process.env[\"RUNNER_TEMP\"] || os.tmpdir(),\n    \"determinate-nix-installer-netrc\",\n  );\n  try {\n    await fs.access(expectedNetrcPath);\n    return expectedNetrcPath;\n  } catch {\n    // `nix-installer` was not used, the user may be registered with FlakeHub though.\n    const destinedNetrcPath = path.join(\n      process.env[\"RUNNER_TEMP\"] || os.tmpdir(),\n      \"magic-nix-cache-netrc\",\n    );\n    try {\n      await flakeHubLogin(destinedNetrcPath);\n    } catch (e) {\n      actionsCore.info(\"FlakeHub cache disabled.\");\n      actionsCore.debug(`Error while logging into FlakeHub: ${e}`);\n    }\n    return destinedNetrcPath;\n  }\n}\n\nasync function flakeHubLogin(netrc: string): Promise<void> {\n  const jwt = await actionsCore.getIDToken(\"api.flakehub.com\");\n\n  await fs.writeFile(\n    netrc,\n    [\n      `machine api.flakehub.com login flakehub password ${jwt}`,\n      `machine flakehub.com login flakehub password ${jwt}`,\n      `machine cache.flakehub.com login flakehub password ${jwt}`,\n    ].join(\"\\n\"),\n  );\n\n  actionsCore.info(\"Logged in to FlakeHub.\");\n}\n","import { netrcPath, tailLog } from \"./helpers.js\";\nimport * as actionsCore from \"@actions/core\";\nimport { DetSysAction, inputs } from \"detsys-ts\";\nimport got, { Got, Response } from \"got\";\nimport * as http from \"http\";\nimport { SpawnOptions, spawn } from \"node:child_process\";\nimport { mkdirSync, openSync, readFileSync } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { inspect } from \"node:util\";\n\n// The ENV_DAEMON_DIR is intended to determine if we \"own\" the daemon or not,\n// in the case that a user has put the magic nix cache into their workflow\n// twice.\nconst ENV_DAEMON_DIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\n\nconst FACT_ENV_VARS_PRESENT = \"required_env_vars_present\";\nconst FACT_DIFF_STORE_ENABLED = \"diff_store\";\nconst FACT_NOOP_MODE = \"noop_mode\";\n\nconst STATE_DAEMONDIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\nconst STATE_STARTED = \"MAGIC_NIX_CACHE_STARTED\";\nconst STARTED_HINT = \"true\";\n\nconst TEXT_NOOP =\n  \"Magic Nix Cache is already running, this workflow job is in noop mode. Is the Magic Nix Cache in the workflow twice?\";\nconst TEXT_TRUST_UNTRUSTED =\n  \"The Nix daemon does not consider the user running this workflow to be trusted. Magic Nix Cache is disabled.\";\nconst TEXT_TRUST_UNKNOWN =\n  \"The Nix daemon may not consider the user running this workflow to be trusted. Magic Nix Cache may not start correctly.\";\n\nclass MagicNixCacheAction extends DetSysAction {\n  private hostAndPort: string;\n  private diffStore: boolean;\n  private httpClient: Got;\n  private noopMode: boolean;\n  private daemonDir: string;\n  private daemonStarted: boolean;\n\n  constructor() {\n    super({\n      name: \"magic-nix-cache\",\n      fetchStyle: \"gh-env-style\",\n      idsProjectName: \"magic-nix-cache-closure\",\n      requireNix: \"warn\",\n    });\n\n    this.hostAndPort = inputs.getString(\"listen\");\n    this.diffStore = inputs.getBool(\"diff-store\");\n\n    this.addFact(FACT_DIFF_STORE_ENABLED, this.diffStore);\n\n    this.httpClient = got.extend({\n      retry: {\n        limit: 1,\n        methods: [\"POST\", \"GET\", \"PUT\", \"HEAD\", \"DELETE\", \"OPTIONS\", \"TRACE\"],\n      },\n      hooks: {\n        beforeRetry: [\n          (error, retryCount) => {\n            actionsCore.info(\n              `Retrying after error ${error.code}, retry #: ${retryCount}`,\n            );\n          },\n        ],\n      },\n    });\n\n    this.daemonStarted = actionsCore.getState(STATE_STARTED) === STARTED_HINT;\n\n    if (actionsCore.getState(STATE_DAEMONDIR) !== \"\") {\n      this.daemonDir = actionsCore.getState(STATE_DAEMONDIR);\n    } else {\n      this.daemonDir = this.getTemporaryName();\n      mkdirSync(this.daemonDir);\n      actionsCore.saveState(STATE_DAEMONDIR, this.daemonDir);\n    }\n\n    if (process.env[ENV_DAEMON_DIR] === undefined) {\n      this.noopMode = false;\n      actionsCore.exportVariable(ENV_DAEMON_DIR, this.daemonDir);\n    } else {\n      this.noopMode = process.env[ENV_DAEMON_DIR] !== this.daemonDir;\n    }\n    this.addFact(FACT_NOOP_MODE, this.noopMode);\n\n    this.stapleFile(\"daemon.log\", path.join(this.daemonDir, \"daemon.log\"));\n  }\n\n  async main(): Promise<void> {\n    if (this.noopMode) {\n      actionsCore.warning(TEXT_NOOP);\n      return;\n    }\n\n    if (this.nixStoreTrust === \"untrusted\") {\n      actionsCore.warning(TEXT_TRUST_UNTRUSTED);\n      return;\n    } else if (this.nixStoreTrust === \"unknown\") {\n      actionsCore.info(TEXT_TRUST_UNKNOWN);\n    }\n\n    await this.setUpAutoCache();\n    await this.notifyAutoCache();\n  }\n\n  async post(): Promise<void> {\n    if (this.noopMode) {\n      actionsCore.debug(TEXT_NOOP);\n      return;\n    }\n\n    if (this.nixStoreTrust === \"untrusted\") {\n      actionsCore.debug(TEXT_TRUST_UNTRUSTED);\n      return;\n    } else if (this.nixStoreTrust === \"unknown\") {\n      actionsCore.debug(TEXT_TRUST_UNKNOWN);\n    }\n\n    await this.tearDownAutoCache();\n  }\n\n  async setUpAutoCache(): Promise<void> {\n    const requiredEnv = [\n      \"ACTIONS_CACHE_URL\",\n      \"ACTIONS_RUNTIME_URL\",\n      \"ACTIONS_RUNTIME_TOKEN\",\n    ];\n\n    let anyMissing = false;\n    for (const n of requiredEnv) {\n      if (!process.env.hasOwnProperty(n)) {\n        anyMissing = true;\n        actionsCore.warning(\n          `Disabling automatic caching since required environment ${n} isn't available`,\n        );\n      }\n    }\n\n    this.addFact(FACT_ENV_VARS_PRESENT, !anyMissing);\n    if (anyMissing) {\n      return;\n    }\n\n    if (this.daemonStarted) {\n      actionsCore.debug(\"Already started.\");\n      return;\n    }\n\n    actionsCore.debug(\n      `GitHub Action Cache URL: ${process.env[\"ACTIONS_CACHE_URL\"]}`,\n    );\n\n    const daemonBin = await this.unpackClosure(\"magic-nix-cache\");\n\n    let runEnv;\n    if (actionsCore.isDebug()) {\n      runEnv = {\n        RUST_LOG: \"trace,magic_nix_cache=debug,gha_cache=debug\",\n        RUST_BACKTRACE: \"full\",\n        ...process.env,\n      };\n    } else {\n      runEnv = process.env;\n    }\n\n    const notifyPort = inputs.getString(\"startup-notification-port\");\n\n    const notifyPromise = new Promise<Promise<void>>((resolveListening) => {\n      const promise = new Promise<void>(async (resolveQuit) => {\n        const notifyServer = http.createServer((req, res) => {\n          if (req.method === \"POST\" && req.url === \"/\") {\n            actionsCore.debug(`Notify server shutting down.`);\n            res.writeHead(200, { \"Content-Type\": \"application/json\" });\n            res.end(\"{}\");\n            notifyServer.close(() => {\n              resolveQuit();\n            });\n          }\n        });\n\n        notifyServer.listen(notifyPort, () => {\n          actionsCore.debug(`Notify server running.`);\n          resolveListening(promise);\n        });\n      });\n    });\n\n    // Start tailing the daemon log.\n    const outputPath = `${this.daemonDir}/daemon.log`;\n    const output = openSync(outputPath, \"a\");\n    const log = tailLog(this.daemonDir);\n    const netrc = await netrcPath();\n    const nixConfPath = `${process.env[\"HOME\"]}/.config/nix/nix.conf`;\n    const upstreamCache = inputs.getString(\"upstream-cache\");\n    const diagnosticEndpoint = inputs.getString(\"diagnostic-endpoint\");\n    const useFlakeHub = inputs.getBool(\"use-flakehub\");\n    const flakeHubCacheServer = inputs.getString(\"flakehub-cache-server\");\n    const flakeHubApiServer = inputs.getString(\"flakehub-api-server\");\n    const flakeHubFlakeName = inputs.getString(\"flakehub-flake-name\");\n    const useGhaCache = inputs.getBool(\"use-gha-cache\");\n\n    const daemonCliFlags: string[] = [\n      \"--startup-notification-url\",\n      `http://127.0.0.1:${notifyPort}`,\n      \"--listen\",\n      this.hostAndPort,\n      \"--upstream\",\n      upstreamCache,\n      \"--diagnostic-endpoint\",\n      diagnosticEndpoint,\n      \"--nix-conf\",\n      nixConfPath,\n    ]\n      .concat(this.diffStore ? [\"--diff-store\"] : [])\n      .concat(\n        useFlakeHub\n          ? [\n              \"--use-flakehub\",\n              \"--flakehub-cache-server\",\n              flakeHubCacheServer,\n              \"--flakehub-api-server\",\n              flakeHubApiServer,\n              \"--flakehub-api-server-netrc\",\n              netrc,\n              \"--flakehub-flake-name\",\n              flakeHubFlakeName,\n            ]\n          : [],\n      )\n      .concat(useGhaCache ? [\"--use-gha-cache\"] : []);\n\n    const opts: SpawnOptions = {\n      stdio: [\"ignore\", output, output],\n      env: runEnv,\n      detached: true,\n    };\n\n    // Display the final command for debugging purposes\n    actionsCore.debug(\"Full daemon start command:\");\n    actionsCore.debug(`${daemonBin} ${daemonCliFlags.join(\" \")}`);\n\n    // Start the server. Once it is ready, it will notify us via the notification server.\n    const daemon = spawn(daemonBin, daemonCliFlags, opts);\n\n    this.daemonStarted = true;\n    actionsCore.saveState(STATE_STARTED, STARTED_HINT);\n\n    const pidFile = path.join(this.daemonDir, \"daemon.pid\");\n    await fs.writeFile(pidFile, `${daemon.pid}`);\n\n    actionsCore.info(\"Waiting for magic-nix-cache to start...\");\n\n    await new Promise<void>((resolve, reject) => {\n      notifyPromise\n        // eslint-disable-next-line github/no-then\n        .then((_value) => {\n          resolve();\n        })\n        // eslint-disable-next-line github/no-then\n        .catch((err) => {\n          const msg = `error in notifyPromise: ${err}`;\n          reject(new Error(msg));\n        });\n      daemon.on(\"exit\", async (code, signal) => {\n        let msg: string;\n        if (signal) {\n          msg = `Daemon was killed by signal ${signal}`;\n        } else if (code) {\n          msg = `Daemon exited with code ${code}`;\n        } else {\n          msg = \"Daemon unexpectedly exited\";\n        }\n        reject(new Error(msg));\n      });\n    });\n\n    daemon.unref();\n\n    actionsCore.info(\"Launched Magic Nix Cache\");\n\n    log.unwatch();\n  }\n\n  private async notifyAutoCache(): Promise<void> {\n    if (!this.daemonStarted) {\n      actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n      return;\n    }\n\n    try {\n      actionsCore.debug(`Indicating workflow start`);\n      const res: Response<string> = await this.httpClient.post(\n        `http://${this.hostAndPort}/api/workflow-start`,\n      );\n\n      actionsCore.debug(\n        `Response from POST to /api/workflow-start: (status: ${res.statusCode}, body: ${res.body})`,\n      );\n\n      if (res.statusCode !== 200) {\n        throw new Error(\n          `Failed to trigger workflow start hook; expected status 200 but got (status: ${res.statusCode}, body: ${res.body})`,\n        );\n      }\n\n      actionsCore.debug(`back from post: ${res.body}`);\n    } catch (e) {\n      actionsCore.info(`Error marking the workflow as started:`);\n      actionsCore.info(inspect(e));\n      actionsCore.info(`Magic Nix Cache may not be running for this workflow.`);\n      this.failOnError(`Magic Nix Cache failed to start: ${inspect(e)}`);\n    }\n  }\n\n  async tearDownAutoCache(): Promise<void> {\n    if (!this.daemonStarted) {\n      actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n      return;\n    }\n\n    const pidFile = path.join(this.daemonDir, \"daemon.pid\");\n    const pid = parseInt(await fs.readFile(pidFile, { encoding: \"ascii\" }));\n    actionsCore.debug(`found daemon pid: ${pid}`);\n    if (!pid) {\n      throw new Error(\"magic-nix-cache did not start successfully\");\n    }\n\n    const log = tailLog(this.daemonDir);\n\n    try {\n      actionsCore.debug(`about to post to localhost`);\n      const res: Response<string> = await this.httpClient.post(\n        `http://${this.hostAndPort}/api/workflow-finish`,\n      );\n\n      actionsCore.debug(\n        `Response from POST to /api/workflow-finish: (status: ${res.statusCode}, body: ${res.body})`,\n      );\n\n      if (res.statusCode !== 200) {\n        throw new Error(\n          `Failed to trigger workflow finish hook; expected status 200 but got (status: ${res.statusCode}, body: ${res.body})`,\n        );\n      }\n    } finally {\n      actionsCore.debug(`unwatching the daemon log`);\n      log.unwatch();\n    }\n\n    actionsCore.debug(`killing`);\n    try {\n      process.kill(pid, \"SIGTERM\");\n    } catch (e) {\n      if (typeof e === \"object\" && e && \"code\" in e && e.code !== \"ESRCH\") {\n        throw e;\n      }\n    } finally {\n      if (actionsCore.isDebug()) {\n        actionsCore.info(\"Entire log:\");\n        const entireLog = readFileSync(path.join(this.daemonDir, \"daemon.log\"));\n        actionsCore.info(entireLog.toString());\n      }\n    }\n  }\n}\n\nfunction main(): void {\n  new MagicNixCacheAction().execute();\n}\n\nmain();\n"],"mappings":"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","names":["actionsCore","fs","path"]}
\ No newline at end of file
+{"version":3,"sources":["../src/helpers.ts","../src/index.ts"],"sourcesContent":["import * as actionsCore from \"@actions/core\";\nimport * as fs from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport path from \"node:path\";\nimport { Tail } from \"tail\";\n\nexport function tailLog(daemonDir: string): Tail {\n  const log = new Tail(path.join(daemonDir, \"daemon.log\"));\n  actionsCore.debug(`tailing daemon.log...`);\n  log.on(\"line\", (line) => {\n    actionsCore.info(line);\n  });\n  return log;\n}\n\nexport async function netrcPath(): Promise<string> {\n  const expectedNetrcPath = path.join(\n    process.env[\"RUNNER_TEMP\"] || os.tmpdir(),\n    \"determinate-nix-installer-netrc\",\n  );\n  try {\n    await fs.access(expectedNetrcPath);\n    return expectedNetrcPath;\n  } catch {\n    // `nix-installer` was not used, the user may be registered with FlakeHub though.\n    const destinedNetrcPath = path.join(\n      process.env[\"RUNNER_TEMP\"] || os.tmpdir(),\n      \"magic-nix-cache-netrc\",\n    );\n    try {\n      await flakeHubLogin(destinedNetrcPath);\n    } catch (e) {\n      actionsCore.info(\"FlakeHub cache disabled.\");\n      actionsCore.debug(`Error while logging into FlakeHub: ${e}`);\n    }\n    return destinedNetrcPath;\n  }\n}\n\nasync function flakeHubLogin(netrc: string): Promise<void> {\n  const jwt = await actionsCore.getIDToken(\"api.flakehub.com\");\n\n  await fs.writeFile(\n    netrc,\n    [\n      `machine api.flakehub.com login flakehub password ${jwt}`,\n      `machine flakehub.com login flakehub password ${jwt}`,\n      `machine cache.flakehub.com login flakehub password ${jwt}`,\n    ].join(\"\\n\"),\n  );\n\n  actionsCore.info(\"Logged in to FlakeHub.\");\n}\n","import { netrcPath, tailLog } from \"./helpers.js\";\nimport * as actionsCore from \"@actions/core\";\nimport { DetSysAction, inputs } from \"detsys-ts\";\nimport got, { Got, Response } from \"got\";\nimport * as http from \"http\";\nimport { SpawnOptions, spawn } from \"node:child_process\";\nimport { mkdirSync, openSync, readFileSync } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { inspect } from \"node:util\";\n\n// The ENV_DAEMON_DIR is intended to determine if we \"own\" the daemon or not,\n// in the case that a user has put the magic nix cache into their workflow\n// twice.\nconst ENV_DAEMON_DIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\n\nconst FACT_ENV_VARS_PRESENT = \"required_env_vars_present\";\nconst FACT_DIFF_STORE_ENABLED = \"diff_store\";\nconst FACT_NOOP_MODE = \"noop_mode\";\n\nconst STATE_DAEMONDIR = \"MAGIC_NIX_CACHE_DAEMONDIR\";\nconst STATE_STARTED = \"MAGIC_NIX_CACHE_STARTED\";\nconst STARTED_HINT = \"true\";\n\nconst TEXT_NOOP =\n  \"Magic Nix Cache is already running, this workflow job is in noop mode. Is the Magic Nix Cache in the workflow twice?\";\nconst TEXT_TRUST_UNTRUSTED =\n  \"The Nix daemon does not consider the user running this workflow to be trusted. Magic Nix Cache is disabled.\";\nconst TEXT_TRUST_UNKNOWN =\n  \"The Nix daemon may not consider the user running this workflow to be trusted. Magic Nix Cache may not start correctly.\";\n\nclass MagicNixCacheAction extends DetSysAction {\n  private hostAndPort: string;\n  private diffStore: boolean;\n  private httpClient: Got;\n  private noopMode: boolean;\n  private daemonDir: string;\n  private daemonStarted: boolean;\n\n  constructor() {\n    super({\n      name: \"magic-nix-cache\",\n      fetchStyle: \"gh-env-style\",\n      idsProjectName: \"magic-nix-cache-closure\",\n      requireNix: \"warn\",\n      diagnosticsSuffix: \"perf\",\n    });\n\n    this.hostAndPort = inputs.getString(\"listen\");\n    this.diffStore = inputs.getBool(\"diff-store\");\n\n    this.addFact(FACT_DIFF_STORE_ENABLED, this.diffStore);\n\n    this.httpClient = got.extend({\n      retry: {\n        limit: 1,\n        methods: [\"POST\", \"GET\", \"PUT\", \"HEAD\", \"DELETE\", \"OPTIONS\", \"TRACE\"],\n      },\n      hooks: {\n        beforeRetry: [\n          (error, retryCount) => {\n            actionsCore.info(\n              `Retrying after error ${error.code}, retry #: ${retryCount}`,\n            );\n          },\n        ],\n      },\n    });\n\n    this.daemonStarted = actionsCore.getState(STATE_STARTED) === STARTED_HINT;\n\n    if (actionsCore.getState(STATE_DAEMONDIR) !== \"\") {\n      this.daemonDir = actionsCore.getState(STATE_DAEMONDIR);\n    } else {\n      this.daemonDir = this.getTemporaryName();\n      mkdirSync(this.daemonDir);\n      actionsCore.saveState(STATE_DAEMONDIR, this.daemonDir);\n    }\n\n    if (process.env[ENV_DAEMON_DIR] === undefined) {\n      this.noopMode = false;\n      actionsCore.exportVariable(ENV_DAEMON_DIR, this.daemonDir);\n    } else {\n      this.noopMode = process.env[ENV_DAEMON_DIR] !== this.daemonDir;\n    }\n    this.addFact(FACT_NOOP_MODE, this.noopMode);\n\n    this.stapleFile(\"daemon.log\", path.join(this.daemonDir, \"daemon.log\"));\n  }\n\n  async main(): Promise<void> {\n    if (this.noopMode) {\n      actionsCore.warning(TEXT_NOOP);\n      return;\n    }\n\n    if (this.nixStoreTrust === \"untrusted\") {\n      actionsCore.warning(TEXT_TRUST_UNTRUSTED);\n      return;\n    } else if (this.nixStoreTrust === \"unknown\") {\n      actionsCore.info(TEXT_TRUST_UNKNOWN);\n    }\n\n    await this.setUpAutoCache();\n    await this.notifyAutoCache();\n  }\n\n  async post(): Promise<void> {\n    if (this.noopMode) {\n      actionsCore.debug(TEXT_NOOP);\n      return;\n    }\n\n    if (this.nixStoreTrust === \"untrusted\") {\n      actionsCore.debug(TEXT_TRUST_UNTRUSTED);\n      return;\n    } else if (this.nixStoreTrust === \"unknown\") {\n      actionsCore.debug(TEXT_TRUST_UNKNOWN);\n    }\n\n    await this.tearDownAutoCache();\n  }\n\n  async setUpAutoCache(): Promise<void> {\n    const requiredEnv = [\n      \"ACTIONS_CACHE_URL\",\n      \"ACTIONS_RUNTIME_URL\",\n      \"ACTIONS_RUNTIME_TOKEN\",\n    ];\n\n    let anyMissing = false;\n    for (const n of requiredEnv) {\n      if (!process.env.hasOwnProperty(n)) {\n        anyMissing = true;\n        actionsCore.warning(\n          `Disabling automatic caching since required environment ${n} isn't available`,\n        );\n      }\n    }\n\n    this.addFact(FACT_ENV_VARS_PRESENT, !anyMissing);\n    if (anyMissing) {\n      return;\n    }\n\n    if (this.daemonStarted) {\n      actionsCore.debug(\"Already started.\");\n      return;\n    }\n\n    actionsCore.debug(\n      `GitHub Action Cache URL: ${process.env[\"ACTIONS_CACHE_URL\"]}`,\n    );\n\n    const daemonBin = await this.unpackClosure(\"magic-nix-cache\");\n\n    let runEnv;\n    if (actionsCore.isDebug()) {\n      runEnv = {\n        RUST_LOG: \"trace,magic_nix_cache=debug,gha_cache=debug\",\n        RUST_BACKTRACE: \"full\",\n        ...process.env,\n      };\n    } else {\n      runEnv = process.env;\n    }\n\n    const notifyPort = inputs.getString(\"startup-notification-port\");\n\n    const notifyPromise = new Promise<Promise<void>>((resolveListening) => {\n      const promise = new Promise<void>(async (resolveQuit) => {\n        const notifyServer = http.createServer((req, res) => {\n          if (req.method === \"POST\" && req.url === \"/\") {\n            actionsCore.debug(`Notify server shutting down.`);\n            res.writeHead(200, { \"Content-Type\": \"application/json\" });\n            res.end(\"{}\");\n            notifyServer.close(() => {\n              resolveQuit();\n            });\n          }\n        });\n\n        notifyServer.listen(notifyPort, () => {\n          actionsCore.debug(`Notify server running.`);\n          resolveListening(promise);\n        });\n      });\n    });\n\n    // Start tailing the daemon log.\n    const outputPath = `${this.daemonDir}/daemon.log`;\n    const output = openSync(outputPath, \"a\");\n    const log = tailLog(this.daemonDir);\n    const netrc = await netrcPath();\n    const nixConfPath = `${process.env[\"HOME\"]}/.config/nix/nix.conf`;\n    const upstreamCache = inputs.getString(\"upstream-cache\");\n    const diagnosticEndpoint = inputs.getString(\"diagnostic-endpoint\");\n    const useFlakeHub = inputs.getBool(\"use-flakehub\");\n    const flakeHubCacheServer = inputs.getString(\"flakehub-cache-server\");\n    const flakeHubApiServer = inputs.getString(\"flakehub-api-server\");\n    const flakeHubFlakeName = inputs.getString(\"flakehub-flake-name\");\n    const useGhaCache = inputs.getBool(\"use-gha-cache\");\n\n    const daemonCliFlags: string[] = [\n      \"--startup-notification-url\",\n      `http://127.0.0.1:${notifyPort}`,\n      \"--listen\",\n      this.hostAndPort,\n      \"--upstream\",\n      upstreamCache,\n      \"--diagnostic-endpoint\",\n      diagnosticEndpoint,\n      \"--nix-conf\",\n      nixConfPath,\n    ]\n      .concat(this.diffStore ? [\"--diff-store\"] : [])\n      .concat(\n        useFlakeHub\n          ? [\n              \"--use-flakehub\",\n              \"--flakehub-cache-server\",\n              flakeHubCacheServer,\n              \"--flakehub-api-server\",\n              flakeHubApiServer,\n              \"--flakehub-api-server-netrc\",\n              netrc,\n              \"--flakehub-flake-name\",\n              flakeHubFlakeName,\n            ]\n          : [],\n      )\n      .concat(useGhaCache ? [\"--use-gha-cache\"] : []);\n\n    const opts: SpawnOptions = {\n      stdio: [\"ignore\", output, output],\n      env: runEnv,\n      detached: true,\n    };\n\n    // Display the final command for debugging purposes\n    actionsCore.debug(\"Full daemon start command:\");\n    actionsCore.debug(`${daemonBin} ${daemonCliFlags.join(\" \")}`);\n\n    // Start the server. Once it is ready, it will notify us via the notification server.\n    const daemon = spawn(daemonBin, daemonCliFlags, opts);\n\n    this.daemonStarted = true;\n    actionsCore.saveState(STATE_STARTED, STARTED_HINT);\n\n    const pidFile = path.join(this.daemonDir, \"daemon.pid\");\n    await fs.writeFile(pidFile, `${daemon.pid}`);\n\n    actionsCore.info(\"Waiting for magic-nix-cache to start...\");\n\n    await new Promise<void>((resolve, reject) => {\n      notifyPromise\n        // eslint-disable-next-line github/no-then\n        .then((_value) => {\n          resolve();\n        })\n        // eslint-disable-next-line github/no-then\n        .catch((err) => {\n          const msg = `error in notifyPromise: ${err}`;\n          reject(new Error(msg));\n        });\n      daemon.on(\"exit\", async (code, signal) => {\n        let msg: string;\n        if (signal) {\n          msg = `Daemon was killed by signal ${signal}`;\n        } else if (code) {\n          msg = `Daemon exited with code ${code}`;\n        } else {\n          msg = \"Daemon unexpectedly exited\";\n        }\n        reject(new Error(msg));\n      });\n    });\n\n    daemon.unref();\n\n    actionsCore.info(\"Launched Magic Nix Cache\");\n\n    log.unwatch();\n  }\n\n  private async notifyAutoCache(): Promise<void> {\n    if (!this.daemonStarted) {\n      actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n      return;\n    }\n\n    try {\n      actionsCore.debug(`Indicating workflow start`);\n      const res: Response<string> = await this.httpClient.post(\n        `http://${this.hostAndPort}/api/workflow-start`,\n      );\n\n      actionsCore.debug(\n        `Response from POST to /api/workflow-start: (status: ${res.statusCode}, body: ${res.body})`,\n      );\n\n      if (res.statusCode !== 200) {\n        throw new Error(\n          `Failed to trigger workflow start hook; expected status 200 but got (status: ${res.statusCode}, body: ${res.body})`,\n        );\n      }\n\n      actionsCore.debug(`back from post: ${res.body}`);\n    } catch (e) {\n      actionsCore.info(`Error marking the workflow as started:`);\n      actionsCore.info(inspect(e));\n      actionsCore.info(`Magic Nix Cache may not be running for this workflow.`);\n      this.failOnError(`Magic Nix Cache failed to start: ${inspect(e)}`);\n    }\n  }\n\n  async tearDownAutoCache(): Promise<void> {\n    if (!this.daemonStarted) {\n      actionsCore.debug(\"magic-nix-cache not started - Skipping\");\n      return;\n    }\n\n    const pidFile = path.join(this.daemonDir, \"daemon.pid\");\n    const pid = parseInt(await fs.readFile(pidFile, { encoding: \"ascii\" }));\n    actionsCore.debug(`found daemon pid: ${pid}`);\n    if (!pid) {\n      throw new Error(\"magic-nix-cache did not start successfully\");\n    }\n\n    const log = tailLog(this.daemonDir);\n\n    try {\n      actionsCore.debug(`about to post to localhost`);\n      const res: Response<string> = await this.httpClient.post(\n        `http://${this.hostAndPort}/api/workflow-finish`,\n      );\n\n      actionsCore.debug(\n        `Response from POST to /api/workflow-finish: (status: ${res.statusCode}, body: ${res.body})`,\n      );\n\n      if (res.statusCode !== 200) {\n        throw new Error(\n          `Failed to trigger workflow finish hook; expected status 200 but got (status: ${res.statusCode}, body: ${res.body})`,\n        );\n      }\n    } finally {\n      actionsCore.debug(`unwatching the daemon log`);\n      log.unwatch();\n    }\n\n    actionsCore.debug(`killing`);\n    try {\n      process.kill(pid, \"SIGTERM\");\n    } catch (e) {\n      if (typeof e === \"object\" && e && \"code\" in e && e.code !== \"ESRCH\") {\n        throw e;\n      }\n    } finally {\n      if (actionsCore.isDebug()) {\n        actionsCore.info(\"Entire log:\");\n        const entireLog = readFileSync(path.join(this.daemonDir, \"daemon.log\"));\n        actionsCore.info(entireLog.toString());\n      }\n    }\n  }\n}\n\nfunction main(): void {\n  new MagicNixCacheAction().execute();\n}\n\nmain();\n"],"mappings":"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","names":["actionsCore","fs","path"]}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 58fc7ba..d38da6c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,7 +13,7 @@ dependencies:
     version: 1.1.1
   detsys-ts:
     specifier: github:DeterminateSystems/detsys-ts
-    version: github.com/DeterminateSystems/detsys-ts/5fcb0532d85556ebc2de286e483885976531339d
+    version: github.com/DeterminateSystems/detsys-ts/9d66d2c89c150f796165fdcc20b3be538807c0f4
   got:
     specifier: ^14.3.0
     version: 14.3.0
@@ -341,7 +341,7 @@ packages:
       '@babel/helper-split-export-declaration': 7.24.6
       '@babel/parser': 7.24.6
       '@babel/types': 7.24.6
-      debug: 4.3.4
+      debug: 4.3.5
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -591,7 +591,7 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4
+      debug: 4.3.5
       espree: 9.6.1
       globals: 13.24.0
       ignore: 5.3.1
@@ -622,7 +622,7 @@ packages:
     engines: {node: '>=10.10.0'}
     dependencies:
       '@humanwhocodes/object-schema': 2.0.3
-      debug: 4.3.4
+      debug: 4.3.5
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -960,7 +960,7 @@ packages:
       '@typescript-eslint/types': 7.11.0
       '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.4.5)
       '@typescript-eslint/visitor-keys': 7.11.0
-      debug: 4.3.4
+      debug: 4.3.5
       eslint: 8.57.0
       typescript: 5.4.5
     transitivePeerDependencies:
@@ -987,7 +987,7 @@ packages:
     dependencies:
       '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.4.5)
       '@typescript-eslint/utils': 7.11.0(eslint@8.57.0)(typescript@5.4.5)
-      debug: 4.3.4
+      debug: 4.3.5
       eslint: 8.57.0
       ts-api-utils: 1.3.0(typescript@5.4.5)
       typescript: 5.4.5
@@ -1011,7 +1011,7 @@ packages:
     dependencies:
       '@typescript-eslint/types': 7.11.0
       '@typescript-eslint/visitor-keys': 7.11.0
-      debug: 4.3.4
+      debug: 4.3.5
       globby: 11.1.0
       is-glob: 4.0.3
       minimatch: 9.0.4
@@ -1266,7 +1266,7 @@ packages:
     hasBin: true
     dependencies:
       caniuse-lite: 1.0.30001625
-      electron-to-chromium: 1.4.784
+      electron-to-chromium: 1.4.787
       node-releases: 2.0.14
       update-browserslist-db: 1.0.16(browserslist@4.23.0)
     dev: true
@@ -1443,8 +1443,8 @@ packages:
       ms: 2.1.3
     dev: true
 
-  /debug@4.3.4:
-    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+  /debug@4.3.5:
+    resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
     engines: {node: '>=6.0'}
     peerDependencies:
       supports-color: '*'
@@ -1524,8 +1524,8 @@ packages:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
     dev: true
 
-  /electron-to-chromium@1.4.784:
-    resolution: {integrity: sha512-9CZwh+sDrhDAeOEFh8s3PqwduzTyYIeYwZolc1b9ENAUt3ePu7R1sJSCWr/820ISssRxCJUyHI9Wb7j+0Uo1AA==}
+  /electron-to-chromium@1.4.787:
+    resolution: {integrity: sha512-d0EFmtLPjctczO3LogReyM2pbBiiZbnsKnGF+cdZhsYzHm/A0GV7W94kqzLD8SN4O3f3iHlgLUChqghgyznvCQ==}
     dev: true
 
   /emoji-regex@8.0.0:
@@ -1731,7 +1731,7 @@ packages:
       eslint: '*'
       eslint-plugin-import: '*'
     dependencies:
-      debug: 4.3.4
+      debug: 4.3.5
       enhanced-resolve: 5.16.1
       eslint: 8.57.0
       eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.11.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
@@ -1969,7 +1969,7 @@ packages:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4
+      debug: 4.3.5
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -3564,7 +3564,7 @@ packages:
       bundle-require: 4.1.0(esbuild@0.19.12)
       cac: 6.7.14
       chokidar: 3.6.0
-      debug: 4.3.4
+      debug: 4.3.5
       esbuild: 0.19.12
       execa: 5.1.1
       globby: 11.1.0
@@ -3826,8 +3826,8 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
-  github.com/DeterminateSystems/detsys-ts/5fcb0532d85556ebc2de286e483885976531339d:
-    resolution: {tarball: https://codeload.github.com/DeterminateSystems/detsys-ts/tar.gz/5fcb0532d85556ebc2de286e483885976531339d}
+  github.com/DeterminateSystems/detsys-ts/9d66d2c89c150f796165fdcc20b3be538807c0f4:
+    resolution: {tarball: https://codeload.github.com/DeterminateSystems/detsys-ts/tar.gz/9d66d2c89c150f796165fdcc20b3be538807c0f4}
     name: detsys-ts
     version: 1.0.0
     dependencies:
diff --git a/src/index.ts b/src/index.ts
index 1d78fde..44d6a1f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -43,6 +43,7 @@ class MagicNixCacheAction extends DetSysAction {
       fetchStyle: "gh-env-style",
       idsProjectName: "magic-nix-cache-closure",
       requireNix: "warn",
+      diagnosticsSuffix: "perf",
     });
 
     this.hostAndPort = inputs.getString("listen");