Repository: kopia/kopia
Pull request #3548 (2024-01-01 23:37:25)

build(deps): bump the kopia-ui-npm-dependencies group in /app with 2 updates
Bumps the kopia-ui-npm-dependencies group in /app with 2 updates: [electron-log](https://github.com/megahertz/electron-log) and [electron](https://github.com/electron/electron).

Updates `electron-log` from 5.0.1 to 5.0.3
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/megahertz/electron-log/commit/4e33575098738ddb4336f976c844d0988034b83a"><code>4e33575</code></a> 5.0.3</li>
<li><a href="https://github.com/megahertz/electron-log/commit/2fb3d78cf9e3bfae6e15d5428626c09cb78c740b"><code>2fb3d78</code></a> fix(renderer): Incorrect depth set in ipc, fixes <a href="https://redirect.github.com/megahertz/electron-log/issues/389">#389</a></li>
<li><a href="https://github.com/megahertz/electron-log/commit/8419bba0e6e18f2831936df33417629cd609d1e4"><code>8419bba</code></a> 5.0.2</li>
<li><a href="https://github.com/megahertz/electron-log/commit/0ec6d044945efbd5f3fc65000e08c83927c86261"><code>0ec6d04</code></a> fix(initialize): Allow custom sessions, fixes <a href="https://redirect.github.com/megahertz/electron-log/issues/379">#379</a>, closes <a href="https://redirect.github.com/megahertz/electron-log/issues/380">#380</a> (<a href="https://redirect.github.com/megahertz/electron-log/issues/388">#388</a>)</li>
<li><a href="https://github.com/megahertz/electron-log/commit/00cd210eb318a4ff1f406f197d76422cd9b7c80c"><code>00cd210</code></a> fix: Pass the correct params to <code>writeFn</code> (<a href="https://redirect.github.com/megahertz/electron-log/issues/387">#387</a>)</li>
<li><a href="https://github.com/megahertz/electron-log/commit/c0610ecb4345787666c0b6de3cd35394a4670e1d"><code>c0610ec</code></a> fix(type): Set the exact Logger.create return type, closes <a href="https://redirect.github.com/megahertz/electron-log/issues/382">#382</a></li>
<li><a href="https://github.com/megahertz/electron-log/commit/aa6161dc2022d7d4fc3d09f86fcc24fadf41e935"><code>aa6161d</code></a> chore: Update README.md, follwing 5.0.1 change (<a href="https://redirect.github.com/megahertz/electron-log/issues/381">#381</a>)</li>
<li>See full diff in <a href="https://github.com/megahertz/electron-log/compare/v5.0.1...v5.0.3">compare view</a></li>
</ul>
</details>
<br />

Updates `electron` from 27.1.3 to 28.1.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/electron/electron/releases">electron's releases</a>.</em></p>
<blockquote>
<h2>electron v28.1.0</h2>
<h1>Release Notes for v28.1.0</h1>
<h2>Features</h2>
<ul>
<li>Added an option in <code>protocol.registerSchemesAsPrivileged</code> to allow V8 code cache in custom schemes. <a href="https://redirect.github.com/electron/electron/pull/40709">#40709</a> <!-- raw HTML omitted -->(Also in <a href="https://redirect.github.com/electron/electron/pull/40708">27</a>)<!-- raw HTML omitted --></li>
</ul>
<h2>Fixes</h2>
<ul>
<li>Fixed documentation of the default <code>--inspect</code> port. <a href="https://redirect.github.com/electron/electron/pull/40743">#40743</a> <!-- raw HTML omitted -->(Also in <a href="https://redirect.github.com/electron/electron/pull/40742">27</a>)<!-- raw HTML omitted --></li>
<li>Prevent node mode to be used as script runner by other apps on macOS. <a href="https://redirect.github.com/electron/electron/pull/40710">#40710</a> <!-- raw HTML omitted -->(Also in <a href="https://redirect.github.com/electron/electron/pull/40738">26</a>, <a href="https://redirect.github.com/electron/electron/pull/40737">27</a>)<!-- raw HTML omitted --></li>
</ul>
<h2>Other Changes</h2>
<ul>
<li>Security: backported fix for CVE-2023-7024. <a href="https://redirect.github.com/electron/electron/pull/40806">#40806</a></li>
<li>Updated Chromium to 120.0.6099.71. <a href="https://redirect.github.com/electron/electron/pull/40722">#40722</a></li>
</ul>
<h2>electron v28.0.0</h2>
<h1>Release Notes for 28.0.0</h1>
<h2>Stack Upgrades</h2>
<ul>
<li>Chromium <code>120.0.6099.56</code>
<ul>
<li><a href="https://developer.chrome.com/blog/new-in-chrome-119/">New in 119</a></li>
<li><a href="https://developer.chrome.com/blog/new-in-chrome-120/">New in 120</a></li>
</ul>
</li>
<li>Node <code>18.18.2</code>
<ul>
<li><a href="https://nodejs.org/en/blog/release/v18.18.0/">Node 18.18.0 release notes</a></li>
<li><a href="https://nodejs.org/en/blog/release/v18.18.1/">Node 18.18.1 release notes</a></li>
<li><a href="https://nodejs.org/en/blog/release/v18.18.2/">Node 18.18.2 release notes</a></li>
</ul>
</li>
<li>V8 <code>12.0</code></li>
</ul>
<h2>Breaking Changes</h2>
<ul>
<li>The <code>BrowserWindow.getTrafficLightPosition()</code> and <code>BrowserWindow.setTrafficLightPosition()</code> methods have been removed. <a href="https://redirect.github.com/electron/electron/pull/39479">#39479</a></li>
<li>The <code>app.runningUnderRosettaTranslation()</code> method has been removed. <a href="https://redirect.github.com/electron/electron/pull/39956">#39956</a></li>
<li>The <code>ipcRenderer.sendTo()</code> method has been removed. <a href="https://redirect.github.com/electron/electron/pull/39087">#39087</a></li>
<li>The <code>scroll-touch-{begin,end,edge}</code> events have been removed. <a href="https://redirect.github.com/electron/electron/pull/39814">#39814</a></li>
<li>Setting <code>backgroundThrottling</code> to false will disable frames throttling in the <code>BrowserWindow</code> for all <code>WebContents</code> displayed by it. <a href="https://redirect.github.com/electron/electron/pull/38924">#38924</a></li>
</ul>
<h2>Features</h2>
<h3>Additions</h3>
<ul>
<li>Enabled ESM support. <a href="https://redirect.github.com/electron/electron/pull/37535">#37535</a>
<ul>
<li>For more details, see the <a href="https://github.com/electron/electron/blob/main/docs/tutorial/esm.md">ESM documentation</a>.</li>
</ul>
</li>
<li>The <code>UtilityProcess</code> API now supports ESM entrypoints. <a href="https://redirect.github.com/electron/electron/pull/40047">#40047</a></li>
<li>Added several properties to the <code>display</code> object including <code>detected</code>, <code>maximumCursorSize</code>, and <code>nativeOrigin</code>. <a href="https://redirect.github.com/electron/electron/pull/40554">#40554</a></li>
<li>Added support for <code>ELECTRON_OZONE_PLATFORM_HINT</code> environment variable on Linux. <a href="https://redirect.github.com/electron/electron/pull/39792">#39792</a></li>
</ul>
<p>In addition to enabling ESM support in Electron itself, Electron Forge also supports using ESM to package, build and develop Electron applications. You can find this support in Forge v7.0.0 or higher: <a href="https://github.com/electron/forge/releases/tag/v7.0.0">https://github.com/electron/forge/releases/tag/v7.0.0</a></p>
<ul>
<li>Added API to help apps know when to avoid semitransparent backgrounds. <a href="https://redirect.github.com/electron/electron/pull/39631">#39631</a> <!-- raw HTML omitted -->(Also in <a href="https://redirect.github.com/electron/electron/pull/40073">26</a>, <a href="https://redirect.github.com/electron/electron/pull/40074">27</a>)<!-- raw HTML omitted --></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/electron/electron/commit/957d8748b24a523f4275c4acf97916aa5b72c59f"><code>957d874</code></a> chore: cherry-pick 1 changes from Release-2-M120 (<a href="https://redirect.github.com/electron/electron/issues/40806">#40806</a>)</li>
<li><a href="https://github.com/electron/electron/commit/14c6651e358b9d75b1933d8a105da8c57410f6cf"><code>14c6651</code></a> feat: enable code cache for custom protocols (<a href="https://redirect.github.com/electron/electron/issues/40709">#40709</a>)</li>
<li><a href="https://github.com/electron/electron/commit/c9f886c03da55ae5434bd397b9cfd9f048fc1134"><code>c9f886c</code></a> chore: bump chromium to 120.0.6099.109 (28-x-y) (<a href="https://redirect.github.com/electron/electron/issues/40756">#40756</a>)</li>
<li><a href="https://github.com/electron/electron/commit/0aa33cc3df14353ac6b87c90920466f0deb11fdf"><code>0aa33cc</code></a> fix: wrong default port in docs (<a href="https://redirect.github.com/electron/electron/issues/40743">#40743</a>)</li>
<li><a href="https://github.com/electron/electron/commit/5d5ebc9da2f12a59e84cf76c211e68d6d0b6ce06"><code>5d5ebc9</code></a> chore: bump chromium to 120.0.6099.71 (28-x-y) (<a href="https://redirect.github.com/electron/electron/issues/40722">#40722</a>)</li>
<li><a href="https://github.com/electron/electron/commit/66e4c3089becbdfc12d9970890d2dd99e6432642"><code>66e4c30</code></a> fix: prevent node mode to be used as script runner by other apps (<a href="https://redirect.github.com/electron/electron/issues/40710">#40710</a>)</li>
<li><a href="https://github.com/electron/electron/commit/4a617aa666788fe6fdb3da5b06da55dcd5871379"><code>4a617aa</code></a> docs: fix year typos in electron-timelines.md (<a href="https://redirect.github.com/electron/electron/issues/40729">#40729</a>)</li>
<li><a href="https://github.com/electron/electron/commit/271f2b576a567a39038aefc65def2edb94696d19"><code>271f2b5</code></a> chore: bump chromium to 120.0.6099.62 (28-x-y) (<a href="https://redirect.github.com/electron/electron/issues/40700">#40700</a>)</li>
<li><a href="https://github.com/electron/electron/commit/515803e601e0958fbf5c402f2e0922a707419294"><code>515803e</code></a> docs: add E28 support for ESM in tutorial.md (<a href="https://redirect.github.com/electron/electron/issues/40691">#40691</a>)</li>
<li><a href="https://github.com/electron/electron/commit/34d115a4302fce8a0ec19fafe945a652c0d79683"><code>34d115a</code></a> fix: cherry pick 9009d76968b1ec2ed825bc95e47d086ceea07520 from chromium (<a href="https://redirect.github.com/electron/electron/issues/40688">#40688</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/electron/electron/compare/v27.1.3...v28.1.0">compare view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions


</details>

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/kopia/kopia/pull/3548?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`a934629`)](https://app.codecov.io/gh/kopia/kopia/commit/a934629c55f5a04a1496b58708bf44df1f7b6690?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) 75.82% compared to head [(`ba143a5`)](https://app.codecov.io/gh/kopia/kopia/pull/3548?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia) 75.82%.


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #3548   +/-   ##
=======================================
  Coverage   75.82%   75.82%           
=======================================
  Files         465      465           
  Lines       37171    37171           
=======================================
  Hits        28185    28185           
  Misses       7057     7057           
  Partials     1929     1929           
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/kopia/kopia/pull/3548?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kopia).


Patch:
diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml
index d11dda79..a90d8be5 100644
--- a/.github/workflows/code-coverage.yml
+++ b/.github/workflows/code-coverage.yml
@@ -28,7 +28,7 @@ jobs:
       with:
         files: coverage.txt
     - name: Upload Logs
-      uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+      uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
       with:
         name: logs
         path: .logs/**/*.log
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
index d0c842c8..5e441537 100644
--- a/.github/workflows/dependency-review.yml
+++ b/.github/workflows/dependency-review.yml
@@ -17,4 +17,4 @@ jobs:
       - name: 'Checkout Repository'
         uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
       - name: 'Dependency Review'
-        uses: actions/dependency-review-action@v3
+        uses: actions/dependency-review-action@v4
diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml
index d0cda1d0..59f0386b 100644
--- a/.github/workflows/make.yml
+++ b/.github/workflows/make.yml
@@ -100,7 +100,7 @@ jobs:
         # macOS signing certificate (base64-encoded), used by Electron Builder
         MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
     - name: Upload Kopia Artifacts
-      uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+      uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
       with:
         name: kopia-${{ matrix.os }}
         path: |
@@ -122,7 +122,7 @@ jobs:
         if-no-files-found: ignore
       if: ${{ !contains(matrix.os, 'self-hosted') }}
     - name: Upload Kopia Binary
-      uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+      uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
       with:
         name: kopia_binaries-${{ matrix.os }}
         path: |
@@ -144,13 +144,13 @@ jobs:
     - name: Set up Docker Buildx
       uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
     - name: Download Artifacts
-      uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0
+      uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
       with:
         pattern: kopia-*
         merge-multiple: true
         path: dist
     - name: Download Kopia Binaries
-      uses: actions/download-artifact@f44cd7b40bfd40b6aa1cc1b9b5b7bf03d3c67110 # v4.1.0
+      uses: actions/download-artifact@6b208ae046db98c579e8a3aa621ab581ff575935 # v4.1.1
       with:
         pattern: kopia_binaries-*
         merge-multiple: true
diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml
index a6d89c84..4e42de79 100644
--- a/.github/workflows/ossf-scorecard.yml
+++ b/.github/workflows/ossf-scorecard.yml
@@ -39,12 +39,12 @@ jobs:
       -
         # Upload the results to GitHub's code scanning dashboard.
         name: "Upload to results to dashboard"
-        uses: github/codeql-action/upload-sarif@012739e5082ff0c22ca6d6ab32e07c36df03c4a4 # v3.22.12
+        uses: github/codeql-action/upload-sarif@0b21cf2492b6b02c465a3e5d7c473717ad7721ba # v3.23.1
         with:
           sarif_file: results.sarif
       -
         name: "Upload analysis results as 'Job Artifact'"
-        uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+        uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
         with:
           name: SARIF file
           path: results.sarif
diff --git a/app/package-lock.json b/app/package-lock.json
index ca110a18..d02ea5cd 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -11,7 +11,7 @@
       "dependencies": {
         "auto-launch": "^5.0.6",
         "electron-is-dev": "^2.0.0",
-        "electron-log": "^5.0.1",
+        "electron-log": "^5.0.3",
         "electron-store": "^8.1.0",
         "electron-updater": "^6.1.7",
         "minimist": "^1.2.8",
@@ -24,7 +24,7 @@
         "asar": "^3.2.0",
         "concurrently": "^8.2.2",
         "dotenv": "^16.3.1",
-        "electron": "^27.1.3",
+        "electron": "^28.1.0",
         "electron-builder": "^24.9.1",
         "electron-store": "^8.1.0",
         "playwright": "^1.37.1",
@@ -1612,9 +1612,9 @@
       }
     },
     "node_modules/electron": {
-      "version": "27.1.3",
-      "resolved": "https://registry.npmjs.org/electron/-/electron-27.1.3.tgz",
-      "integrity": "sha512-7eD8VMhhlL5J531OOawn00eMthUkX1e3qN5Nqd7eMK8bg5HxQBrn8bdPlvUEnCano9KhrVwaDnGeuzWoDOGpjQ==",
+      "version": "28.1.0",
+      "resolved": "https://registry.npmjs.org/electron/-/electron-28.1.0.tgz",
+      "integrity": "sha512-82Y7o4PSWPn1o/aVwYPsgmBw6Gyf2lVHpaBu3Ef8LrLWXxytg7ZRZr/RtDqEMOzQp3+mcuy3huH84MyjdmP50Q==",
       "dev": true,
       "hasInstallScript": true,
       "dependencies": {
@@ -1699,9 +1699,9 @@
       }
     },
     "node_modules/electron-log": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.0.1.tgz",
-      "integrity": "sha512-x4wnwHg00h/onWQgjmvcdLV7Mrd9TZjxNs8LmXVpqvANDf4FsSs5wLlzOykWLcaFzR3+5hdVEQ8ctmrUxgHlPA==",
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.0.3.tgz",
+      "integrity": "sha512-jUgAuRjfpCD9tmH1F6fb195YsFfM/DkqkZLhFeo0VAAstantn11bxmgx63uE6KG/JljHG7sIkgM2QEjDimJI0g==",
       "engines": {
         "node": ">= 14"
       }
@@ -4780,9 +4780,9 @@
       }
     },
     "electron": {
-      "version": "27.1.3",
-      "resolved": "https://registry.npmjs.org/electron/-/electron-27.1.3.tgz",
-      "integrity": "sha512-7eD8VMhhlL5J531OOawn00eMthUkX1e3qN5Nqd7eMK8bg5HxQBrn8bdPlvUEnCano9KhrVwaDnGeuzWoDOGpjQ==",
+      "version": "28.1.0",
+      "resolved": "https://registry.npmjs.org/electron/-/electron-28.1.0.tgz",
+      "integrity": "sha512-82Y7o4PSWPn1o/aVwYPsgmBw6Gyf2lVHpaBu3Ef8LrLWXxytg7ZRZr/RtDqEMOzQp3+mcuy3huH84MyjdmP50Q==",
       "dev": true,
       "requires": {
         "@electron/get": "^2.0.0",
@@ -4844,9 +4844,9 @@
       "integrity": "sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA=="
     },
     "electron-log": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.0.1.tgz",
-      "integrity": "sha512-x4wnwHg00h/onWQgjmvcdLV7Mrd9TZjxNs8LmXVpqvANDf4FsSs5wLlzOykWLcaFzR3+5hdVEQ8ctmrUxgHlPA=="
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.0.3.tgz",
+      "integrity": "sha512-jUgAuRjfpCD9tmH1F6fb195YsFfM/DkqkZLhFeo0VAAstantn11bxmgx63uE6KG/JljHG7sIkgM2QEjDimJI0g=="
     },
     "electron-publish": {
       "version": "24.8.1",
diff --git a/app/package.json b/app/package.json
index 8a3382ca..426ebc74 100644
--- a/app/package.json
+++ b/app/package.json
@@ -5,7 +5,7 @@
   "dependencies": {
     "auto-launch": "^5.0.6",
     "electron-is-dev": "^2.0.0",
-    "electron-log": "^5.0.1",
+    "electron-log": "^5.0.3",
     "electron-updater": "^6.1.7",
     "electron-store": "^8.1.0",
     "minimist": "^1.2.8",
@@ -116,7 +116,7 @@
     "asar": "^3.2.0",
     "concurrently": "^8.2.2",
     "dotenv": "^16.3.1",
-    "electron": "^27.1.3",
+    "electron": "^28.1.0",
     "electron-store": "^8.1.0",
     "electron-builder": "^24.9.1",
     "playwright": "^1.37.1",
diff --git a/cli/app.go b/cli/app.go
index 18c710cf..8ca4a1b8 100644
--- a/cli/app.go
+++ b/cli/app.go
@@ -89,7 +89,7 @@ type appServices interface {
 	stdout() io.Writer
 	Stderr() io.Writer
 	stdin() io.Reader
-	onCtrlC(callback func())
+	onTerminate(callback func())
 	onRepositoryFatalError(callback func(err error))
 	enableTestOnlyFlags() bool
 	EnvName(s string) string
diff --git a/cli/command_mount.go b/cli/command_mount.go
index d6e23662..bd1a82dc 100644
--- a/cli/command_mount.go
+++ b/cli/command_mount.go
@@ -103,7 +103,7 @@ func (c *commandMount) run(ctx context.Context, rep repo.Repository) error {
 	// Wait until ctrl-c pressed or until the directory is unmounted.
 	ctrlCPressed := make(chan bool)
 
-	c.svc.onCtrlC(func() {
+	c.svc.onTerminate(func() {
 		close(ctrlCPressed)
 	})
 
diff --git a/cli/command_repository_upgrade.go b/cli/command_repository_upgrade.go
index fe1a3830..546903dc 100644
--- a/cli/command_repository_upgrade.go
+++ b/cli/command_repository_upgrade.go
@@ -392,7 +392,7 @@ func (c *commandRepositoryUpgrade) sleepWithContext(ctx context.Context, dur tim
 
 	stop := make(chan struct{})
 
-	c.svc.onCtrlC(func() { close(stop) })
+	c.svc.onTerminate(func() { close(stop) })
 
 	select {
 	case <-ctx.Done():
diff --git a/cli/command_server_start.go b/cli/command_server_start.go
index 10587831..2e7f1db0 100644
--- a/cli/command_server_start.go
+++ b/cli/command_server_start.go
@@ -222,7 +222,7 @@ func (c *commandServerStart) run(ctx context.Context) error {
 		return nil
 	}
 
-	c.svc.onCtrlC(func() {
+	c.svc.onTerminate(func() {
 		log(ctx).Infof("Shutting down...")
 
 		if serr := httpServer.Shutdown(ctx); serr != nil {
diff --git a/cli/command_snapshot_create.go b/cli/command_snapshot_create.go
index 395a250a..c998ed87 100644
--- a/cli/command_snapshot_create.go
+++ b/cli/command_snapshot_create.go
@@ -233,7 +233,7 @@ func (c *commandSnapshotCreate) setupUploader(rep repo.RepositoryWriter) *snapsh
 		u.CheckpointInterval = interval
 	}
 
-	c.svc.onCtrlC(u.Cancel)
+	c.svc.onTerminate(u.Cancel)
 
 	u.ForceHashPercentage = c.snapshotCreateForceHash
 	u.ParallelUploads = c.snapshotCreateParallelUploads
diff --git a/cli/command_snapshot_migrate.go b/cli/command_snapshot_migrate.go
index bd4c9e4f..724ff7b3 100644
--- a/cli/command_snapshot_migrate.go
+++ b/cli/command_snapshot_migrate.go
@@ -68,7 +68,7 @@ func (c *commandSnapshotMigrate) run(ctx context.Context, destRepo repo.Reposito
 
 	c.svc.getProgress().StartShared()
 
-	c.svc.onCtrlC(func() {
+	c.svc.onTerminate(func() {
 		mu.Lock()
 		defer mu.Unlock()
 
diff --git a/cli/config.go b/cli/config.go
index 1278ef2a..3daac638 100644
--- a/cli/config.go
+++ b/cli/config.go
@@ -8,6 +8,7 @@
 	"os/signal"
 	"path/filepath"
 	"runtime"
+	"syscall"
 
 	"github.com/alecthomas/kingpin/v2"
 	"github.com/pkg/errors"
@@ -29,9 +30,9 @@ func (c *App) onRepositoryFatalError(f func(err error)) {
 	c.onFatalErrorCallbacks = append(c.onFatalErrorCallbacks, f)
 }
 
-func (c *App) onCtrlC(f func()) {
+func (c *App) onTerminate(f func()) {
 	s := make(chan os.Signal, 1)
-	signal.Notify(s, os.Interrupt)
+	signal.Notify(s, os.Interrupt, syscall.SIGTERM)
 
 	go func() {
 		// invoke the function when either real or simulated Ctrl-C signal is delivered
diff --git a/cli/inproc.go b/cli/inproc.go
index f625c47d..204a4e02 100644
--- a/cli/inproc.go
+++ b/cli/inproc.go
@@ -3,6 +3,7 @@
 import (
 	"context"
 	"io"
+	"os"
 
 	"github.com/alecthomas/kingpin/v2"
 
@@ -12,7 +13,7 @@
 
 // RunSubcommand executes the subcommand asynchronously in current process
 // with flags in an isolated CLI environment and returns standard output and standard error.
-func (c *App) RunSubcommand(ctx context.Context, kpapp *kingpin.Application, stdin io.Reader, argsAndFlags []string) (stdout, stderr io.Reader, wait func() error, kill func()) {
+func (c *App) RunSubcommand(ctx context.Context, kpapp *kingpin.Application, stdin io.Reader, argsAndFlags []string) (stdout, stderr io.Reader, wait func() error, interrupt func(os.Signal)) {
 	stdoutReader, stdoutWriter := io.Pipe()
 	stderrReader, stderrWriter := io.Pipe()
 
@@ -59,7 +60,7 @@ func (c *App) RunSubcommand(ctx context.Context, kpapp *kingpin.Application, std
 
 	return stdoutReader, stderrReader, func() error {
 			return <-resultErr
-		}, func() {
+		}, func(_ os.Signal) {
 			// deliver simulated Ctrl-C to the app.
 			c.simulatedCtrlC <- true
 		}
diff --git a/go.mod b/go.mod
index 05203a98..aa850284 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.21
 require (
 	cloud.google.com/go/storage v1.36.0
 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1
-	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0
+	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1
 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1
 	github.com/Azure/azure-storage-blob-go v0.15.0
 	github.com/alecthomas/kingpin/v2 v2.4.0
@@ -39,9 +39,9 @@ require (
 	github.com/pkg/errors v0.9.1
 	github.com/pkg/profile v1.7.0
 	github.com/pkg/sftp v1.13.6
-	github.com/prometheus/client_golang v1.17.0
+	github.com/prometheus/client_golang v1.18.0
 	github.com/prometheus/client_model v0.5.0
-	github.com/prometheus/common v0.45.0
+	github.com/prometheus/common v0.46.0
 	github.com/sanity-io/litter v1.5.5
 	github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
 	github.com/stretchr/testify v1.8.4
@@ -49,36 +49,36 @@ require (
 	github.com/tg123/go-htpasswd v1.2.2
 	github.com/zalando/go-keyring v0.2.3
 	github.com/zeebo/blake3 v0.2.3
-	go.opentelemetry.io/otel v1.21.0
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
-	go.opentelemetry.io/otel/sdk v1.21.0
-	go.opentelemetry.io/otel/trace v1.21.0
+	go.opentelemetry.io/otel v1.22.0
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0
+	go.opentelemetry.io/otel/sdk v1.22.0
+	go.opentelemetry.io/otel/trace v1.22.0
 	go.uber.org/multierr v1.11.0
 	go.uber.org/zap v1.26.0
-	golang.org/x/crypto v0.17.0
+	golang.org/x/crypto v0.18.0
 	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
 	golang.org/x/mod v0.14.0
-	golang.org/x/net v0.19.0
-	golang.org/x/oauth2 v0.15.0
-	golang.org/x/sync v0.5.0
-	golang.org/x/sys v0.15.0
-	golang.org/x/term v0.15.0
+	golang.org/x/net v0.20.0
+	golang.org/x/oauth2 v0.16.0
+	golang.org/x/sync v0.6.0
+	golang.org/x/sys v0.16.0
+	golang.org/x/term v0.16.0
 	golang.org/x/text v0.14.0
-	google.golang.org/api v0.154.0
+	google.golang.org/api v0.157.0
 	google.golang.org/grpc v1.60.1
 	google.golang.org/protobuf v1.32.0
 	gopkg.in/kothar/go-backblaze.v0 v0.0.0-20210124194846-35409b867216
 )
 
 require (
-	cloud.google.com/go v0.110.10 // indirect
+	cloud.google.com/go v0.111.0 // indirect
 	cloud.google.com/go/compute v1.23.3 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
 	cloud.google.com/go/iam v1.1.5 // indirect
 	github.com/Azure/azure-pipeline-go v0.2.3 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
 	github.com/Azure/go-autorest/autorest/adal v0.9.16 // indirect
-	github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect
+	github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect
 	github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 // indirect
 	github.com/alessio/shellescape v1.4.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
@@ -91,13 +91,13 @@ require (
 	github.com/felixge/fgprof v0.9.3 // indirect
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/frankban/quicktest v1.13.1 // indirect
-	github.com/go-logr/logr v1.3.0 // indirect
+	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/gobwas/httphead v0.1.0 // indirect
 	github.com/gobwas/pool v0.2.1 // indirect
 	github.com/gobwas/ws v1.3.0 // indirect
 	github.com/godbus/dbus/v5 v5.1.0 // indirect
-	github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
+	github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
 	github.com/golang/glog v1.1.2 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
@@ -113,30 +113,28 @@ require (
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-ieproxy v0.0.1 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
-	github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
 	github.com/minio/md5-simd v1.1.2 // indirect
 	github.com/minio/sha256-simd v1.0.1 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
-	github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
+	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 // indirect
-	github.com/prometheus/procfs v0.11.1 // indirect
+	github.com/prometheus/procfs v0.12.0 // indirect
 	github.com/rs/xid v1.5.0 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
 	go.opencensus.io v0.24.0 // indirect
 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
-	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
-	go.opentelemetry.io/otel/metric v1.21.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect
+	go.opentelemetry.io/otel/metric v1.22.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
-	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect
+	google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 6f5dcbb5..83304002 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,6 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y=
-cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic=
+cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM=
+cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU=
 cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
 cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
 cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
@@ -13,8 +13,8 @@ github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVt
 github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70=
@@ -35,8 +35,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
 github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
 github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
 github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI=
@@ -104,8 +104,8 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD
 github.com/frankban/quicktest v1.13.1 h1:xVm/f9seEhZFL9+n5kv5XLrGwy6elc4V9v/XFY2vmd8=
 github.com/frankban/quicktest v1.13.1/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
-github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
@@ -122,8 +122,8 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j
 github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
-github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
+github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
 github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
@@ -224,8 +224,6 @@ github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
-github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
 github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw=
@@ -245,8 +243,8 @@ github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhA
 github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
 github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
 github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
+github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
@@ -258,15 +256,15 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20=
 github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
-github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
-github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
+github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
+github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
 github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
-github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
-github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
-github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
-github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
+github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
+github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
@@ -312,18 +310,18 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.4
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
-go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
-go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
-go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
-go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
-go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
-go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
-go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
-go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
+go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
+go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ=
+go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
+go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
+go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
+go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
+go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
+go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
 go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
 go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -339,8 +337,8 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
@@ -362,18 +360,18 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
-golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
-golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
-golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
+golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
+golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
-golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -382,7 +380,6 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -391,13 +388,13 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
-golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
+golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
+golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -420,8 +417,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-google.golang.org/api v0.154.0 h1:X7QkVKZBskztmpPKWQXgjJRPA2dJYrL6r+sYPRLj050=
-google.golang.org/api v0.154.0/go.mod h1:qhSMkM85hgqiokIYsrRyKxrjfBeIhgl4Z2JmeRkYylc=
+google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20=
+google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
@@ -429,12 +426,12 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f h1:Vn+VyHU5guc9KjB5KrjI2q0wCOWEOIh0OEsleqakHJg=
-google.golang.org/genproto v0.0.0-20231120223509-83a465c0220f/go.mod h1:nWSwAFPb+qfNJXsoeO3Io7zf4tMSfN8EA8RlDA04GhY=
-google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
-google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
+google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
diff --git a/internal/mount/mount_net_use.go b/internal/mount/mount_net_use.go
index 2448cadc..f58ddd4d 100644
--- a/internal/mount/mount_net_use.go
+++ b/internal/mount/mount_net_use.go
@@ -17,7 +17,7 @@
 // Directory mounts a given directory under a provided drive letter.
 func Directory(ctx context.Context, entry fs.Directory, driveLetter string, _ Options) (Controller, error) {
 	if !isValidWindowsDriveOrAsterisk(driveLetter) {
-		return nil, errors.Errorf("must be a valid drive letter or asteris")
+		return nil, errors.Errorf("must be a valid drive letter or asterisk")
 	}
 
 	c, err := DirectoryWebDAV(ctx, entry)
diff --git a/internal/scrubber/scrub_sensitive.go b/internal/scrubber/scrub_sensitive.go
index 562bd5f0..f67c5e15 100644
--- a/internal/scrubber/scrub_sensitive.go
+++ b/internal/scrubber/scrub_sensitive.go
@@ -26,6 +26,23 @@ func ScrubSensitiveData(v reflect.Value) reflect.Value {
 					res.Field(i).SetString(strings.Repeat("*", fv.Len()))
 				}
 			} else if sf.IsExported() {
+				switch fv.Kind() {
+				case reflect.Pointer:
+					if !fv.IsNil() {
+						fv = ScrubSensitiveData(fv.Elem()).Addr()
+					}
+
+				case reflect.Struct:
+					fv = ScrubSensitiveData(fv)
+
+				case reflect.Interface:
+					if !fv.IsNil() {
+						fv = ScrubSensitiveData(fv.Elem())
+					}
+
+				default: // Set the field as-is.
+				}
+
 				res.Field(i).Set(fv)
 			}
 		}
diff --git a/internal/serverapi/serverapi.go b/internal/serverapi/serverapi.go
index 890acffb..b5a5d11b 100644
--- a/internal/serverapi/serverapi.go
+++ b/internal/serverapi/serverapi.go
@@ -291,6 +291,7 @@ type CLIInfo struct {
 type UIPreferences struct {
 	BytesStringBase2       bool   `json:"bytesStringBase2"`       // If `true`, display storage values in base-2 (default is base-10)
 	DefaultSnapshotViewAll bool   `json:"defaultSnapshotViewAll"` // If `true` default to showing all snapshots (default is local snapshots)
-	Theme                  string `json:"theme"`                  // 'dark', 'light' or ''
+	Theme                  string `json:"theme"`                  // Specifies the theme used by the UI
+	FontSize               string `json:"fontSize"`               // Specifies the font size used by the UI
 	PageSize               int    `json:"pageSize"`               // A page size; the actual possible values will only be provided by the frontend
 }


Test patch:
diff --git a/.github/workflows/compat-test.yml b/.github/workflows/compat-test.yml
index 9fd67cb5..b49d1cae 100644
--- a/.github/workflows/compat-test.yml
+++ b/.github/workflows/compat-test.yml
@@ -26,7 +26,7 @@ jobs:
     - name: Compat Test
       run: make compat-tests
     - name: Upload Logs
-      uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+      uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
       with:
         name: logs
         path: .logs/**/*.log
diff --git a/.github/workflows/endurance-test.yml b/.github/workflows/endurance-test.yml
index 2019daa7..ded66a35 100644
--- a/.github/workflows/endurance-test.yml
+++ b/.github/workflows/endurance-test.yml
@@ -31,7 +31,7 @@ jobs:
     - name: Endurance Tests
       run: make endurance-tests
     - name: Upload Logs
-      uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+      uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
       with:
         name: logs
         path: .logs/**/*.log
diff --git a/.github/workflows/htmlui-tests.yml b/.github/workflows/htmlui-tests.yml
index 2757ae74..401a738d 100644
--- a/.github/workflows/htmlui-tests.yml
+++ b/.github/workflows/htmlui-tests.yml
@@ -39,7 +39,7 @@ jobs:
     - name: Run Tests
       run: make htmlui-e2e-test
     - name: Upload Screenshots
-      uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+      uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
       with:
         path: .screenshots/**/*.png
         if-no-files-found: ignore
diff --git a/.github/workflows/stress-test.yml b/.github/workflows/stress-test.yml
index 11a59891..8d11bca8 100644
--- a/.github/workflows/stress-test.yml
+++ b/.github/workflows/stress-test.yml
@@ -30,7 +30,7 @@ jobs:
     - name: Stress Test
       run: make stress-test
     - name: Upload Logs
-      uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+      uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
       with:
         name: logs
         path: .logs/**/*.log
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 8173a74b..002adf88 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -64,7 +64,7 @@ jobs:
     - name: Integration Tests
       run: make -j2 ci-integration-tests
     - name: Upload Logs
-      uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+      uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
       with:
         name: logs-${{ matrix.os }}
         path: .logs/**/*.log
diff --git a/cli/terminate_signal_test.go b/cli/terminate_signal_test.go
new file mode 100644
index 00000000..4813a0ea
--- /dev/null
+++ b/cli/terminate_signal_test.go
@@ -0,0 +1,30 @@
+package cli_test
+
+import (
+	"strings"
+	"syscall"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/kopia/kopia/tests/testenv"
+)
+
+// Waits until the server advertises its address on the line.
+func serverStarted(line string) bool {
+	return !strings.HasPrefix(line, "SERVER ADDRESS: ")
+}
+
+func TestTerminate(t *testing.T) {
+	env := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, testenv.NewExeRunner(t))
+
+	env.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", env.RepoDir)
+
+	wait, interrupt := env.RunAndProcessStderrInt(t, serverStarted, "server", "start",
+		"--address=localhost:0",
+		"--insecure")
+
+	interrupt(syscall.SIGTERM)
+
+	require.NoError(t, wait())
+}
diff --git a/internal/scrubber/scrub_sensitive_test.go b/internal/scrubber/scrub_sensitive_test.go
index ac35b970..aa707d68 100644
--- a/internal/scrubber/scrub_sensitive_test.go
+++ b/internal/scrubber/scrub_sensitive_test.go
@@ -12,7 +12,11 @@
 type S struct {
 	SomePassword1 string `kopia:"sensitive"`
 	NonPassword   string
-	Inner         *Q
+	InnerPtr      *Q
+	InnerIf       interface{}
+	InnerStruct   Q
+	NilPtr        *Q
+	NilIf         interface{}
 }
 
 type Q struct {
@@ -24,19 +28,39 @@ func TestScrubber(t *testing.T) {
 	input := &S{
 		SomePassword1: "foo",
 		NonPassword:   "bar",
-		Inner: &Q{
+		InnerPtr: &Q{
 			SomePassword1: "foo",
 			NonPassword:   "bar",
 		},
+		InnerStruct: Q{
+			SomePassword1: "foo",
+			NonPassword:   "bar",
+		},
+		InnerIf: Q{
+			SomePassword1: "foo",
+			NonPassword:   "bar",
+		},
+		NilPtr: nil,
+		NilIf:  nil,
 	}
 
 	want := &S{
 		SomePassword1: "***",
 		NonPassword:   "bar",
-		Inner: &Q{
-			SomePassword1: "foo",
+		InnerPtr: &Q{
+			SomePassword1: "***",
+			NonPassword:   "bar",
+		},
+		InnerStruct: Q{
+			SomePassword1: "***",
+			NonPassword:   "bar",
+		},
+		InnerIf: Q{
+			SomePassword1: "***",
 			NonPassword:   "bar",
 		},
+		NilPtr: nil,
+		NilIf:  nil,
 	}
 
 	output := scrubber.ScrubSensitiveData(reflect.ValueOf(input)).Interface()
diff --git a/tests/testenv/cli_exe_runner.go b/tests/testenv/cli_exe_runner.go
index 91ec037e..39ee62e2 100644
--- a/tests/testenv/cli_exe_runner.go
+++ b/tests/testenv/cli_exe_runner.go
@@ -20,7 +20,7 @@ type CLIExeRunner struct {
 }
 
 // Start implements CLIRunner.
-func (e *CLIExeRunner) Start(t *testing.T, args []string, env map[string]string) (stdout, stderr io.Reader, wait func() error, kill func()) {
+func (e *CLIExeRunner) Start(t *testing.T, args []string, env map[string]string) (stdout, stderr io.Reader, wait func() error, interrupt func(os.Signal)) {
 	t.Helper()
 
 	c := exec.Command(e.Exe, append([]string{
@@ -51,8 +51,14 @@ func (e *CLIExeRunner) Start(t *testing.T, args []string, env map[string]string)
 		t.Fatalf("unable to start: %v", err)
 	}
 
-	return stdoutPipe, stderrPipe, c.Wait, func() {
-		c.Process.Kill()
+	return stdoutPipe, stderrPipe, c.Wait, func(sig os.Signal) {
+		if sig == os.Kill {
+			c.Process.Kill()
+
+			return
+		}
+
+		c.Process.Signal(sig)
 	}
 }
 
diff --git a/tests/testenv/cli_inproc_runner.go b/tests/testenv/cli_inproc_runner.go
index f5afae33..617a8914 100644
--- a/tests/testenv/cli_inproc_runner.go
+++ b/tests/testenv/cli_inproc_runner.go
@@ -27,7 +27,7 @@ type CLIInProcRunner struct {
 }
 
 // Start implements CLIRunner.
-func (e *CLIInProcRunner) Start(t *testing.T, args []string, env map[string]string) (stdout, stderr io.Reader, wait func() error, kill func()) {
+func (e *CLIInProcRunner) Start(t *testing.T, args []string, env map[string]string) (stdout, stderr io.Reader, wait func() error, interrupt func(os.Signal)) {
 	t.Helper()
 
 	ctx := testlogging.Context(t)
diff --git a/tests/testenv/cli_test_env.go b/tests/testenv/cli_test_env.go
index df001eee..79849218 100644
--- a/tests/testenv/cli_test_env.go
+++ b/tests/testenv/cli_test_env.go
@@ -30,7 +30,7 @@
 // CLIRunner encapsulates running kopia subcommands for testing purposes.
 // It supports implementations that use subprocesses or in-process invocations.
 type CLIRunner interface {
-	Start(t *testing.T, args []string, env map[string]string) (stdout, stderr io.Reader, wait func() error, kill func())
+	Start(t *testing.T, args []string, env map[string]string) (stdout, stderr io.Reader, wait func() error, interrupt func(os.Signal))
 }
 
 // CLITest encapsulates state for a CLI-based test.
@@ -165,7 +165,20 @@ func (e *CLITest) getLogOutputPrefix() (string, bool) {
 func (e *CLITest) RunAndProcessStderr(t *testing.T, callback func(line string) bool, args ...string) (wait func() error, kill func()) {
 	t.Helper()
 
-	stdout, stderr, wait, kill := e.Runner.Start(t, e.cmdArgs(args), e.Environment)
+	wait, interrupt := e.RunAndProcessStderrInt(t, callback, args...)
+	kill = func() {
+		interrupt(os.Kill)
+	}
+
+	return wait, kill
+}
+
+// RunAndProcessStderrInt runs the given command, and streams its output
+// line-by-line to outputCallback until it returns false.
+func (e *CLITest) RunAndProcessStderrInt(t *testing.T, outputCallback func(line string) bool, args ...string) (wait func() error, interrupt func(os.Signal)) {
+	t.Helper()
+
+	stdout, stderr, wait, interrupt := e.Runner.Start(t, e.cmdArgs(args), e.Environment)
 
 	go func() {
 		scanner := bufio.NewScanner(stdout)
@@ -182,7 +195,7 @@ func (e *CLITest) RunAndProcessStderr(t *testing.T, callback func(line string) b
 
 	scanner := bufio.NewScanner(stderr)
 	for scanner.Scan() {
-		if !callback(scanner.Text()) {
+		if !outputCallback(scanner.Text()) {
 			break
 		}
 	}
@@ -200,7 +213,7 @@ func (e *CLITest) RunAndProcessStderr(t *testing.T, callback func(line string) b
 		}
 	}()
 
-	return wait, kill
+	return wait, interrupt
 }
 
 // RunAndExpectSuccessWithErrOut runs the given command, expects it to succeed and returns its stdout and stderr lines.