Repository: kopia/kopia
Pull request #3115 (2023-07-03 22:08:48)

build(deps): bump the common-golang-dependencies group with 9 updates
Bumps the common-golang-dependencies group with 9 updates:

| Package | Update |
| --- | --- |
| [cloud.google.com/go/storage](https://github.com/googleapis/google-cloud-go) | 1.30.1 to 1.31.0 |
| [github.com/minio/minio-go/v7](https://github.com/minio/minio-go) | 7.0.56 to 7.0.59 |
| [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) | 1.15.1 to 1.16.0 |
| [golang.org/x/crypto](https://github.com/golang/crypto) | 0.9.0 to 0.10.0 |
| [golang.org/x/mod](https://github.com/golang/mod) | 0.10.0 to 0.11.0 |
| [golang.org/x/net](https://github.com/golang/net) | 0.10.0 to 0.11.0 |
| [golang.org/x/oauth2](https://github.com/golang/oauth2) | 0.8.0 to 0.9.0 |
| [golang.org/x/sync](https://github.com/golang/sync) | 0.2.0 to 0.3.0 |
| [google.golang.org/api](https://github.com/googleapis/google-api-go-client) | 0.127.0 to 0.129.0 |

Updates `cloud.google.com/go/storage` from 1.30.1 to 1.31.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/googleapis/google-cloud-go/releases">cloud.google.com/go/storage's releases</a>.</em></p>
<blockquote>
<h2>storage: v1.31.0</h2>
<h2><a href="https://github.com/googleapis/google-cloud-go/compare/storage/v1.30.1...storage/v1.31.0">1.31.0</a> (2023-06-27)</h2>
<h3>Features</h3>
<ul>
<li><strong>storage/internal:</strong> Add ctype=CORD for ChecksummedData.content (<a href="https://github.com/googleapis/google-cloud-go/commit/ca94e2724f9e2610b46aefd0a3b5ddc06102e91b">ca94e27</a>)</li>
<li><strong>storage:</strong> Add support for MatchGlob (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8097">#8097</a>) (<a href="https://github.com/googleapis/google-cloud-go/commit/9426a5a45d4c2fd07f84261f6d602680e79cdc48">9426a5a</a>), refs <a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7727">#7727</a> <a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7728">#7728</a></li>
<li><strong>storage:</strong> Respect WithEndpoint for SignedURLs and PostPolicy (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8113">#8113</a>) (<a href="https://github.com/googleapis/google-cloud-go/commit/f918f23a3cda4fbc8d709e32b914ead8b735d664">f918f23</a>)</li>
<li><strong>storage:</strong> Update all direct dependencies (<a href="https://github.com/googleapis/google-cloud-go/commit/b340d030f2b52a4ce48846ce63984b28583abde6">b340d03</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>storage:</strong> Fix CreateBucket logic for gRPC (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8165">#8165</a>) (<a href="https://github.com/googleapis/google-cloud-go/commit/8424e7e145a117c91006318fa924a8b2643c1c7e">8424e7e</a>), refs <a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8162">#8162</a></li>
<li><strong>storage:</strong> Fix reads with &quot;./&quot; in object names [XML] (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8017">#8017</a>) (<a href="https://github.com/googleapis/google-cloud-go/commit/6b7b21f8a334b6ad3a25e1f66ae1265b4d1f0995">6b7b21f</a>)</li>
<li><strong>storage:</strong> Fix routing header for writes (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8159">#8159</a>) (<a href="https://github.com/googleapis/google-cloud-go/commit/42a59f5a23ab9b4743ab032ad92304922c801d93">42a59f5</a>), refs <a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8142">#8142</a> <a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8143">#8143</a> <a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8144">#8144</a> <a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8145">#8145</a> <a href="https://redirect.github.com/googleapis/google-cloud-go/issues/8149">#8149</a></li>
<li><strong>storage:</strong> REST query UpdateMask bug (<a href="https://github.com/googleapis/google-cloud-go/commit/df52820b0e7721954809a8aa8700b93c5662dc9b">df52820</a>)</li>
<li><strong>storage:</strong> Update grpc to v1.55.0 (<a href="https://github.com/googleapis/google-cloud-go/commit/1147ce02a990276ca4f8ab7a1ab65c14da4450ef">1147ce0</a>)</li>
</ul>
<h3>Documentation</h3>
<ul>
<li><strong>storage/internal:</strong> Clarifications about behavior of DeleteObject RPC (<a href="https://github.com/googleapis/google-cloud-go/commit/3f1ed9c63fb115f47607a3ab478842fe5ba0df11">3f1ed9c</a>)</li>
<li><strong>storage/internal:</strong> Clarified the behavior of supplying bucket.name field in CreateBucket to reflect actual implementation (<a href="https://github.com/googleapis/google-cloud-go/commit/ebae64d53397ec5dfe851f098754eaa1f5df7cb1">ebae64d</a>)</li>
<li><strong>storage/internal:</strong> Revert ChecksummedData message definition not to specify ctype=CORD, because it would be a breaking change. (<a href="https://github.com/googleapis/google-cloud-go/commit/ef61e4799280a355b960da8ae240ceb2efbe71ac">ef61e47</a>)</li>
<li><strong>storage/internal:</strong> Update routing annotations for CancelResumableWriteRequest and QueryWriteStatusRequest (<a href="https://github.com/googleapis/google-cloud-go/commit/49008518e168fe6f7891b907d6fc14eecdef758c">4900851</a>)</li>
<li><strong>storage/internal:</strong> Updated ChecksummedData message definition to specify ctype=CORD, and removed incorrect earlier attempt that set that annotation in the ReadObjectResponse message definition (<a href="https://github.com/googleapis/google-cloud-go/commit/ef61e4799280a355b960da8ae240ceb2efbe71ac">ef61e47</a>)</li>
<li><strong>storage:</strong> WithXMLReads should mention XML instead of JSON API (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7881">#7881</a>) (<a href="https://github.com/googleapis/google-cloud-go/commit/36f56c80c456ca74ffc03df76844ce15980ced82">36f56c8</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/f3402bff3e7b529f4ae2d51e59e42b5c4f98bb2b"><code>f3402bf</code></a> chore(main): release pubsub 1.31.0 (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7892">#7892</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/46fc06099852412071be8c8a02d0d9416fd08817"><code>46fc060</code></a> fix(pubsub): allow clearing of topic schema (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7980">#7980</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/2296f00f664f049385eff8966a630aa038386701"><code>2296f00</code></a> chore(ci): ignore snippet go.mod in vet check (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7981">#7981</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/53d8d68cc703d59c5b8c9f75e2a73a2f2206aa1c"><code>53d8d68</code></a> chore(all): update deps (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7978">#7978</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/8d62d278a25effd0a0efb3977d94b6c4ee3636cf"><code>8d62d27</code></a> chore(all): update deps (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7970">#7970</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/76ec93f2a8efacccb5f07cbdc1d67e1990673344"><code>76ec93f</code></a> chore(all): update module cloud.google.com/go/iam to v1 (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7971">#7971</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/941fccb91534b58a50f8bdd9c4c4f0a5a907923f"><code>941fccb</code></a> test: fix tidy check to match all mod files (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7975">#7975</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/7a04ae98d2dbc0b9ffb96386b416cdb573867b44"><code>7a04ae9</code></a> chore(main): release firestore 1.10.0 (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7310">#7310</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/01f82a2975389ca1f02bb26fe290291e31a9485a"><code>01f82a2</code></a> chore: fixup imports after version bump (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7974">#7974</a>)</li>
<li><a href="https://github.com/googleapis/google-cloud-go/commit/2b3e7d9af7d2f500e736e3db77487127cb44ca23"><code>2b3e7d9</code></a> feat(webrisk): add SubmitUri endpoint (<a href="https://redirect.github.com/googleapis/google-cloud-go/issues/7953">#7953</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/googleapis/google-cloud-go/compare/pubsub/v1.30.1...pubsub/v1.31.0">compare view</a></li>
</ul>
</details>
<br />

Updates `github.com/minio/minio-go/v7` from 7.0.56 to 7.0.59
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/minio/minio-go/releases">github.com/minio/minio-go/v7's releases</a>.</em></p>
<blockquote>
<h2>Feature Release</h2>
<h2>What's Changed</h2>
<ul>
<li>replication: change metrics API by <a href="https://github.com/poornas"><code>@​poornas</code></a> in <a href="https://redirect.github.com/minio/minio-go/pull/1833">minio/minio-go#1833</a></li>
<li>replication: add validation API by <a href="https://github.com/poornas"><code>@​poornas</code></a> in <a href="https://redirect.github.com/minio/minio-go/pull/1844">minio/minio-go#1844</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/minio/minio-go/compare/v7.0.58...v7.0.59">https://github.com/minio/minio-go/compare/v7.0.58...v7.0.59</a></p>
<h2>Bugfix release</h2>
<h2>What's Changed</h2>
<ul>
<li>HealthCheck: Set initial state by <a href="https://github.com/klauspost"><code>@​klauspost</code></a> in <a href="https://redirect.github.com/minio/minio-go/pull/1841">minio/minio-go#1841</a></li>
<li>Added missing Buckets Events by <a href="https://github.com/bexsoft"><code>@​bexsoft</code></a> in <a href="https://redirect.github.com/minio/minio-go/pull/1843">minio/minio-go#1843</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/bexsoft"><code>@​bexsoft</code></a> made their first contribution in <a href="https://redirect.github.com/minio/minio-go/pull/1843">minio/minio-go#1843</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/minio/minio-go/compare/v7.0.57...v7.0.58">https://github.com/minio/minio-go/compare/v7.0.57...v7.0.58</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/minio/minio-go/commit/0be372116b0635ff5321f10dbee29482ccf956c6"><code>0be3721</code></a> replication: add validation API (<a href="https://redirect.github.com/minio/minio-go/issues/1844">#1844</a>)</li>
<li><a href="https://github.com/minio/minio-go/commit/ac95c83e6774dea83acb8316f067edfef643432a"><code>ac95c83</code></a> replication: change metrics API (<a href="https://redirect.github.com/minio/minio-go/issues/1833">#1833</a>)</li>
<li><a href="https://github.com/minio/minio-go/commit/de0473e938f3bf912ce7bb6f6186ca43c1f31405"><code>de0473e</code></a> Update version to next release</li>
<li><a href="https://github.com/minio/minio-go/commit/7048a16cfbca518da27395c33e6fb1cf9c415698"><code>7048a16</code></a> Added missing Buckets Events (<a href="https://redirect.github.com/minio/minio-go/issues/1843">#1843</a>)</li>
<li><a href="https://github.com/minio/minio-go/commit/691ae870325d3cd7c3fc426cbf8396342cd58907"><code>691ae87</code></a> HealthCheck: Set initial state (<a href="https://redirect.github.com/minio/minio-go/issues/1841">#1841</a>)</li>
<li><a href="https://github.com/minio/minio-go/commit/a73fe95ad965dddb77eaa026680554d0f1a82ef1"><code>a73fe95</code></a> look for extended error codes when error body is unavailable</li>
<li><a href="https://github.com/minio/minio-go/commit/83b12c3f4c35fe0533e346907c2da7381a8a09c1"><code>83b12c3</code></a> Update version to next release</li>
<li><a href="https://github.com/minio/minio-go/commit/1685fd850b3c6eb5a324b5e4ca209ffa54eef3d0"><code>1685fd8</code></a> Fix fanout response unmarshalable error type (<a href="https://redirect.github.com/minio/minio-go/issues/1839">#1839</a>)</li>
<li><a href="https://github.com/minio/minio-go/commit/1d018af3bfab9f3a1c761be9382403a4038a4478"><code>1d018af</code></a> Fix multipart upload with encryption context (<a href="https://redirect.github.com/minio/minio-go/issues/1838">#1838</a>)</li>
<li><a href="https://github.com/minio/minio-go/commit/9ddcd0e60a29f164c432c3752cdb82f37e379259"><code>9ddcd0e</code></a> fix wrong function desc in Chinese doc (<a href="https://redirect.github.com/minio/minio-go/issues/1807">#1807</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/minio/minio-go/compare/v7.0.56...v7.0.59">compare view</a></li>
</ul>
</details>
<br />

Updates `github.com/prometheus/client_golang` from 1.15.1 to 1.16.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/prometheus/client_golang/releases">github.com/prometheus/client_golang's releases</a>.</em></p>
<blockquote>
<h2>v1.16.0</h2>
<h2>What's Changed</h2>
<ul>
<li>[BUGFIX] api: Switch to POST for LabelNames, Series, and QueryExemplars. <a href="https://redirect.github.com/prometheus/client_golang/issues/1252">#1252</a></li>
<li>[BUGFIX] api: Fix undefined execution order in return statements. <a href="https://redirect.github.com/prometheus/client_golang/issues/1260">#1260</a></li>
<li>[BUGFIX] native histograms: Fix bug in bucket key calculation. <a href="https://redirect.github.com/prometheus/client_golang/issues/1279">#1279</a></li>
<li>[ENHANCEMENT] Reduce constrainLabels allocations for all metrics. <a href="https://redirect.github.com/prometheus/client_golang/issues/1272">#1272</a></li>
<li>[ENHANCEMENT] promhttp: Add process start time header for scrape efficiency. <a href="https://redirect.github.com/prometheus/client_golang/issues/1278">#1278</a></li>
<li>[ENHANCEMENT] promlint: Improve metricUnits runtime. <a href="https://redirect.github.com/prometheus/client_golang/issues/1286">#1286</a></li>
</ul>
<!-- raw HTML omitted -->
<ul>
<li>Merge v1.15 to main by <a href="https://github.com/bwplotka"><code>@​bwplotka</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1250">prometheus/client_golang#1250</a></li>
<li>Switch to POST for LabelNames, Series, and QueryExemplars to DoGetFallback by <a href="https://github.com/jacksontj"><code>@​jacksontj</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1252">prometheus/client_golang#1252</a></li>
<li>✏️ [collectors]: fix typo in test assertion by <a href="https://github.com/vegerot"><code>@​vegerot</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1153">prometheus/client_golang#1153</a></li>
<li>Added interactive tutorial [kubeCon] by <a href="https://github.com/bwplotka"><code>@​bwplotka</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1255">prometheus/client_golang#1255</a></li>
<li>Fixed tutorial. by <a href="https://github.com/bwplotka"><code>@​bwplotka</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1256">prometheus/client_golang#1256</a></li>
<li>Bump golang.org/x/sys from 0.6.0 to 0.7.0 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1265">prometheus/client_golang#1265</a></li>
<li>Cleanup proto use in tests by <a href="https://github.com/SuperQ"><code>@​SuperQ</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1264">prometheus/client_golang#1264</a></li>
<li>Fix tutorial on WSL-based systems by <a href="https://github.com/marevers"><code>@​marevers</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1257">prometheus/client_golang#1257</a></li>
<li>Fix undefined execution order in return statements by <a href="https://github.com/PiotrLewandowski323"><code>@​PiotrLewandowski323</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1260">prometheus/client_golang#1260</a></li>
<li>Merge release 1.15.1 to main by <a href="https://github.com/bwplotka"><code>@​bwplotka</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1267">prometheus/client_golang#1267</a></li>
<li>GitHub Workflows security hardening by <a href="https://github.com/sashashura"><code>@​sashashura</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1180">prometheus/client_golang#1180</a></li>
<li>add process start time header to client_golang prometheus by <a href="https://github.com/logicalhan"><code>@​logicalhan</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1278">prometheus/client_golang#1278</a></li>
<li>Fix bug in bucket key calculation by <a href="https://github.com/beorn7"><code>@​beorn7</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1279">prometheus/client_golang#1279</a></li>
<li>Bump github.com/prometheus/procfs from 0.9.0 to 0.10.1 by <a href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1283">prometheus/client_golang#1283</a></li>
<li>Reduce constrainLabels allocations by <a href="https://github.com/khasanovbi"><code>@​khasanovbi</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1272">prometheus/client_golang#1272</a></li>
<li>added circleci as gh action YAML by <a href="https://github.com/krishnaduttPanchagnula"><code>@​krishnaduttPanchagnula</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1281">prometheus/client_golang#1281</a></li>
<li>Improve metricUnits runtime by <a href="https://github.com/avlitman"><code>@​avlitman</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1286">prometheus/client_golang#1286</a></li>
<li>Moving fully to GH actions. by <a href="https://github.com/bwplotka"><code>@​bwplotka</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1288">prometheus/client_golang#1288</a></li>
<li>Fix docstring references to renamed native histogram fields / functions. by <a href="https://github.com/juliusv"><code>@​juliusv</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1290">prometheus/client_golang#1290</a></li>
<li>Fixed README &amp; CHANGELOG; Added fmt makefile command (+bingo) for easier contributions. by <a href="https://github.com/bwplotka"><code>@​bwplotka</code></a> in <a href="https://redirect.github.com/prometheus/client_golang/pull/1289">prometheus/client_golang#1289</a></li>
<li></li>
</ul>
<!-- raw HTML omitted -->
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/vegerot"><code>@​vegerot</code></a> made their first contribution in <a href="https://redirect.github.com/prometheus/client_golang/pull/1153">prometheus/client_golang#1153</a></li>
<li><a href="https://github.com/marevers"><code>@​marevers</code></a> made their first contribution in <a href="https://redirect.github.com/prometheus/client_golang/pull/1257">prometheus/client_golang#1257</a></li>
<li><a href="https://github.com/PiotrLewandowski323"><code>@​PiotrLewandowski323</code></a> made their first contribution in <a href="https://redirect.github.com/prometheus/client_golang/pull/1260">prometheus/client_golang#1260</a></li>
<li><a href="https://github.com/sashashura"><code>@​sashashura</code></a> made their first contribution in <a href="https://redirect.github.com/prometheus/client_golang/pull/1180">prometheus/client_golang#1180</a></li>
<li><a href="https://github.com/logicalhan"><code>@​logicalhan</code></a> made their first contribution in <a href="https://redirect.github.com/prometheus/client_golang/pull/1278">prometheus/client_golang#1278</a></li>
<li><a href="https://github.com/khasanovbi"><code>@​khasanovbi</code></a> made their first contribution in <a href="https://redirect.github.com/prometheus/client_golang/pull/1272">prometheus/client_golang#1272</a></li>
<li><a href="https://github.com/krishnaduttPanchagnula"><code>@​krishnaduttPanchagnula</code></a> made their first contribution in <a href="https://redirect.github.com/prometheus/client_golang/pull/1281">prometheus/client_golang#1281</a></li>
<li><a href="https://github.com/avlitman"><code>@​avlitman</code></a> made their first contribution in <a href="https://redirect.github.com/prometheus/client_golang/pull/1286">prometheus/client_golang#1286</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/prometheus/client_golang/compare/v1.15.1...v1.16.0">https://github.com/prometheus/client_golang/compare/v1.15.1...v1.16.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md">github.com/prometheus/client_golang's changelog</a>.</em></p>
<blockquote>
<h2>1.16.0 / 2023-06-15</h2>
<ul>
<li>[BUGFIX] api: Switch to POST for LabelNames, Series, and QueryExemplars. <a href="https://redirect.github.com/prometheus/client_golang/issues/1252">#1252</a></li>
<li>[BUGFIX] api: Fix undefined execution order in return statements. <a href="https://redirect.github.com/prometheus/client_golang/issues/1260">#1260</a></li>
<li>[BUGFIX] native histograms: Fix bug in bucket key calculation. <a href="https://redirect.github.com/prometheus/client_golang/issues/1279">#1279</a></li>
<li>[ENHANCEMENT] Reduce constrainLabels allocations for all metrics. <a href="https://redirect.github.com/prometheus/client_golang/issues/1272">#1272</a></li>
<li>[ENHANCEMENT] promhttp: Add process start time header for scrape efficiency. <a href="https://redirect.github.com/prometheus/client_golang/issues/1278">#1278</a></li>
<li>[ENHANCEMENT] promlint: Improve metricUnits runtime. <a href="https://redirect.github.com/prometheus/client_golang/issues/1286">#1286</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/prometheus/client_golang/commit/3583c1e1d085b75cab406c78b015562d45552b39"><code>3583c1e</code></a> Cut v1.16.0 (<a href="https://redirect.github.com/prometheus/client_golang/issues/1292">#1292</a>)</li>
<li><a href="https://github.com/prometheus/client_golang/commit/2feda42e447efac5bfe39bb226d7025af73c0947"><code>2feda42</code></a> Fixed README &amp; CHANGELOG; Added fmt makefile command (+bingo) for easier cont...</li>
<li><a href="https://github.com/prometheus/client_golang/commit/5b9cf9c6a891de0e7b5ec26d9f4326570a658d17"><code>5b9cf9c</code></a> Merge pull request <a href="https://redirect.github.com/prometheus/client_golang/issues/1290">#1290</a> from prometheus/fix-nh-docstring-refs</li>
<li><a href="https://github.com/prometheus/client_golang/commit/1b19d5f4589629067111815d0734a1ccbc245268"><code>1b19d5f</code></a> Fix docstring references to renamed native histogram fields / functions.</li>
<li><a href="https://github.com/prometheus/client_golang/commit/7352ab7f805ab3bf4d2144f3e8cac92d5caec263"><code>7352ab7</code></a> Moving fully to GH actions. (<a href="https://redirect.github.com/prometheus/client_golang/issues/1288">#1288</a>)</li>
<li><a href="https://github.com/prometheus/client_golang/commit/e4ff34d23eacb385c1a9d3d67c4d06aed042ebec"><code>e4ff34d</code></a> Improve metricUnits runtime (<a href="https://redirect.github.com/prometheus/client_golang/issues/1286">#1286</a>)</li>
<li><a href="https://github.com/prometheus/client_golang/commit/781ea2802473fd7aff2a92cb16244be57472a085"><code>781ea28</code></a> added circleci as gh action YAML (<a href="https://redirect.github.com/prometheus/client_golang/issues/1281">#1281</a>)</li>
<li><a href="https://github.com/prometheus/client_golang/commit/a09a1d34cbc74daa8ed70234b99467a30b020a40"><code>a09a1d3</code></a> Reduce constrainLabels allocations (<a href="https://redirect.github.com/prometheus/client_golang/issues/1272">#1272</a>)</li>
<li><a href="https://github.com/prometheus/client_golang/commit/8840afcfc2c3ff3d40357552dbc1d9d43c4bae67"><code>8840afc</code></a> Bump github.com/prometheus/procfs from 0.9.0 to 0.10.1 (<a href="https://redirect.github.com/prometheus/client_golang/issues/1283">#1283</a>)</li>
<li><a href="https://github.com/prometheus/client_golang/commit/5e78d5f66b851fef874b783814b2e884df2798d0"><code>5e78d5f</code></a> Merge pull request <a href="https://redirect.github.com/prometheus/client_golang/issues/1279">#1279</a> from prometheus/beorn7/histogram</li>
<li>Additional commits viewable in <a href="https://github.com/prometheus/client_golang/compare/v1.15.1...v1.16.0">compare view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/crypto` from 0.9.0 to 0.10.0
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/crypto/commit/8e447d8cc585b0089d1938b8747264783295e65f"><code>8e447d8</code></a> go.mod: update golang.org/x dependencies</li>
<li><a href="https://github.com/golang/crypto/commit/22c99979527e39b0c665cb2ed3c91e30739b69e6"><code>22c9997</code></a> go.mod: tell x repo tagging to ignore dep on net</li>
<li><a href="https://github.com/golang/crypto/commit/d0b316056be80e452fdae998581d0a807b8df0a8"><code>d0b3160</code></a> x509roots: add new module</li>
<li><a href="https://github.com/golang/crypto/commit/16222386f4de802a3c27c1714b0bcc28c0fd5397"><code>1622238</code></a> Add support for &quot;<a href="mailto:hmac-sha2-512-etm@openssh.com">hmac-sha2-512-etm@openssh.com</a>&quot;</li>
<li>See full diff in <a href="https://github.com/golang/crypto/compare/v0.9.0...v0.10.0">compare view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/mod` from 0.10.0 to 0.11.0
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/mod/commit/62c7e578f1a7275d934c99dd48715525bd52b17e"><code>62c7e57</code></a> sumdb: handle panic from c.ReadRemote during ReadTiles</li>
<li><a href="https://github.com/golang/mod/commit/2a1c06a60797e734ab13afef967c60867445e1e5"><code>2a1c06a</code></a> modfile: update toolchain syntax</li>
<li><a href="https://github.com/golang/mod/commit/e81ad1007af3dfbbc0761df9e1a7ace49b7fd7d2"><code>e81ad10</code></a> sumdb: correct spelling mistakes</li>
<li><a href="https://github.com/golang/mod/commit/e343115d42363d625a8424d854065a547fe86f5c"><code>e343115</code></a> sumdb: add missing return after http.Error</li>
<li><a href="https://github.com/golang/mod/commit/fc83a8faf9931f09514a196d1b7ff229c8efedc6"><code>fc83a8f</code></a> modfile: add support for dropping go and toolchain stmts</li>
<li><a href="https://github.com/golang/mod/commit/1846133a84c76607dd1d28fd099344fe91e79b56"><code>1846133</code></a> modfile: add parsing support for toolchain</li>
<li><a href="https://github.com/golang/mod/commit/a73672d4be603f5c3bb0120c69ac4cdde265d008"><code>a73672d</code></a> modfile: add support for go and toolchain lines</li>
<li><a href="https://github.com/golang/mod/commit/e7bea8f1d64fb6752bf603c5c6f39f12f99fc3a3"><code>e7bea8f</code></a> modfile: use semantic sort for exclude blocks</li>
<li>See full diff in <a href="https://github.com/golang/mod/compare/v0.10.0...v0.11.0">compare view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/net` from 0.10.0 to 0.11.0
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/net/commit/6c96ca5daff89298060438c3b5d24e1bd0900a52"><code>6c96ca5</code></a> go.mod: update golang.org/x dependencies</li>
<li><a href="https://github.com/golang/net/commit/5541298b83d714971b9a1e63219fbd68d60aa98d"><code>5541298</code></a> quic: add packet pacer</li>
<li><a href="https://github.com/golang/net/commit/88a50b64844d3c278f93e8007efd20742c386ded"><code>88a50b6</code></a> all: update x/sys to HEAD</li>
<li><a href="https://github.com/golang/net/commit/7e6923f9c41329cf4f8ffe24e4503e030dab2ff2"><code>7e6923f</code></a> quic: add RTT estimator</li>
<li><a href="https://github.com/golang/net/commit/2796e09d6af2852bffb1ae1eefb63a21c823e22f"><code>2796e09</code></a> bpf: check for little endian CPU for OS VM comparison</li>
<li><a href="https://github.com/golang/net/commit/10cf388024ca83e11a073bb365ccda2b2806d103"><code>10cf388</code></a> quic: add a data structure for tracking lists of sent packets</li>
<li><a href="https://github.com/golang/net/commit/ccc217c97ec0528131ef5dc058e57a2080cff953"><code>ccc217c</code></a> quic: parameterize rangeset</li>
<li><a href="https://github.com/golang/net/commit/f16447cf6cc0d406a38d3fd21ce0c5bab9fdc9c7"><code>f16447c</code></a> quic: add go1.21 build constraint</li>
<li><a href="https://github.com/golang/net/commit/f7250ea19d213699fb52bf6b0c0d2cb9cda49d7c"><code>f7250ea</code></a> quic: add a type tracking sent values</li>
<li><a href="https://github.com/golang/net/commit/1b5a2d8538e23b99860872499f7a5c65b8b8c4b7"><code>1b5a2d8</code></a> quic: packet encoding/decoding</li>
<li>Additional commits viewable in <a href="https://github.com/golang/net/compare/v0.10.0...v0.11.0">compare view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/oauth2` from 0.8.0 to 0.9.0
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/oauth2/commit/2323c81c8dba82e8650ed3a24a1a5667e293af38"><code>2323c81</code></a> go.mod: update golang.org/x dependencies</li>
<li>See full diff in <a href="https://github.com/golang/oauth2/compare/v0.8.0...v0.9.0">compare view</a></li>
</ul>
</details>
<br />

Updates `golang.org/x/sync` from 0.2.0 to 0.3.0
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/golang/sync/commit/93782cc822b6b554cb7df40332fd010f0473cbc8"><code>93782cc</code></a> errgroup: use WithCancelCause to cancel context</li>
<li><a href="https://github.com/golang/sync/commit/4966af63bb0cd2fe6d8cf9198904d30401b05fbd"><code>4966af6</code></a> singleflight: example for using Group</li>
<li><a href="https://github.com/golang/sync/commit/a6666c150eb9762365996ad2f3a8c35b0818d7bb"><code>a6666c1</code></a> go.mod: upgrade to go 1.17</li>
<li>See full diff in <a href="https://github.com/golang/sync/compare/v0.2.0...v0.3.0">compare view</a></li>
</ul>
</details>
<br />

Updates `google.golang.org/api` from 0.127.0 to 0.129.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/googleapis/google-api-go-client/releases">google.golang.org/api's releases</a>.</em></p>
<blockquote>
<h2>v0.129.0</h2>
<h2><a href="https://github.com/googleapis/google-api-go-client/compare/v0.128.0...v0.129.0">0.129.0</a> (2023-06-27)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2028">#2028</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/922fc6a38962d441b49d8592d62b5b4c70354b7a">922fc6a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2030">#2030</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/3eb845f12832f5a8943983d54b4a23da38e6f0ba">3eb845f</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2032">#2032</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/247ad09c5eaa9c06208a1acd99ccbf3b41b5e8f4">247ad09</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2033">#2033</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/cc73573e605b04cdaabb4bdfbc355d79c6fba8ba">cc73573</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2035">#2035</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/177408ea127ce2e5a171fa54008838560203e587">177408e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2040">#2040</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/5e1c531ef949b53a8997c25e3f4ea7943b3335b7">5e1c531</a>)</li>
</ul>
<h2>v0.128.0</h2>
<h2><a href="https://github.com/googleapis/google-api-go-client/compare/v0.127.0...v0.128.0">0.128.0</a> (2023-06-15)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2022">#2022</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/d846ea5dd79c01bbf7f2c116fa40b0bdcdc0dc1a">d846ea5</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2026">#2026</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/58512c3c782e9b42d0f215b1f46545686a07033f">58512c3</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2027">#2027</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/229def0dc32b86ab6524a4619189aece0b1ebbea">229def0</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md">google.golang.org/api's changelog</a>.</em></p>
<blockquote>
<h2><a href="https://github.com/googleapis/google-api-go-client/compare/v0.128.0...v0.129.0">0.129.0</a> (2023-06-27)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2028">#2028</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/922fc6a38962d441b49d8592d62b5b4c70354b7a">922fc6a</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2030">#2030</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/3eb845f12832f5a8943983d54b4a23da38e6f0ba">3eb845f</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2032">#2032</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/247ad09c5eaa9c06208a1acd99ccbf3b41b5e8f4">247ad09</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2033">#2033</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/cc73573e605b04cdaabb4bdfbc355d79c6fba8ba">cc73573</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2035">#2035</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/177408ea127ce2e5a171fa54008838560203e587">177408e</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2040">#2040</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/5e1c531ef949b53a8997c25e3f4ea7943b3335b7">5e1c531</a>)</li>
</ul>
<h2><a href="https://github.com/googleapis/google-api-go-client/compare/v0.127.0...v0.128.0">0.128.0</a> (2023-06-15)</h2>
<h3>Features</h3>
<ul>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2022">#2022</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/d846ea5dd79c01bbf7f2c116fa40b0bdcdc0dc1a">d846ea5</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2026">#2026</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/58512c3c782e9b42d0f215b1f46545686a07033f">58512c3</a>)</li>
<li><strong>all:</strong> Auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2027">#2027</a>) (<a href="https://github.com/googleapis/google-api-go-client/commit/229def0dc32b86ab6524a4619189aece0b1ebbea">229def0</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/78ebbdb0ea67c7936888eab6c93db50c734cfc93"><code>78ebbdb</code></a> chore(main): release 0.129.0 (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2029">#2029</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/5e1c531ef949b53a8997c25e3f4ea7943b3335b7"><code>5e1c531</code></a> feat(all): auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2040">#2040</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/888df6f9da49e64b83815eeba7e0d82ed46e1eec"><code>888df6f</code></a> chore: skip generating integrations:v1alpha (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2039">#2039</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/bbfccf87fc59ebaaa1c406b6ee078d43edd07987"><code>bbfccf8</code></a> chore(all): update all (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2038">#2038</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/efba8fab11b608ddcba8eceed9dc535852016e0e"><code>efba8fa</code></a> chore: restore generating integrations:v1alpha (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2037">#2037</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/177408ea127ce2e5a171fa54008838560203e587"><code>177408e</code></a> feat(all): auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2035">#2035</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/807827a10560bd302626f6852abbe38034424a61"><code>807827a</code></a> chore(all): update all (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2031">#2031</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/cc73573e605b04cdaabb4bdfbc355d79c6fba8ba"><code>cc73573</code></a> feat(all): auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2033">#2033</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/247ad09c5eaa9c06208a1acd99ccbf3b41b5e8f4"><code>247ad09</code></a> feat(all): auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2032">#2032</a>)</li>
<li><a href="https://github.com/googleapis/google-api-go-client/commit/3eb845f12832f5a8943983d54b4a23da38e6f0ba"><code>3eb845f</code></a> feat(all): auto-regenerate discovery clients (<a href="https://redirect.github.com/googleapis/google-api-go-client/issues/2030">#2030</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/googleapis/google-api-go-client/compare/v0.127.0...v0.129.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 will merge this PR once CI passes on it, as requested by @jkowalski.

[//]: # (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


</details>

jkowalski: @dependabot merge

Patch:
diff --git a/cli/command_maintenance_info.go b/cli/command_maintenance_info.go
index f5d157c2..60908e2f 100644
--- a/cli/command_maintenance_info.go
+++ b/cli/command_maintenance_info.go
@@ -66,6 +66,12 @@ func (c *commandMaintenanceInfo) run(ctx context.Context, rep repo.DirectReposit
 	c.out.printStdout("  max age of logs: %v\n", cl.MaxAge)
 	c.out.printStdout("  max total size:  %v\n", units.BytesString(cl.MaxTotalSize))
 
+	if p.ExtendObjectLocks {
+		c.out.printStdout("Object Lock Extension: enabled\n")
+	} else {
+		c.out.printStdout("Object Lock Extension: disabled\n")
+	}
+
 	c.out.printStdout("Recent Maintenance Runs:\n")
 
 	for run, timings := range s.Runs {
diff --git a/cli/command_maintenance_set.go b/cli/command_maintenance_set.go
index c08905c1..d2f5bd9d 100644
--- a/cli/command_maintenance_set.go
+++ b/cli/command_maintenance_set.go
@@ -23,6 +23,8 @@ type commandMaintenanceSet struct {
 	maxRetainedLogCount       int
 	maxRetainedLogAge         time.Duration
 	maxTotalRetainedLogSizeMB int64
+
+	extendObjectLocks []bool // optional boolean
 }
 
 func (c *commandMaintenanceSet) setup(svc appServices, parent commandParent) {
@@ -51,6 +53,7 @@ func (c *commandMaintenanceSet) setup(svc appServices, parent commandParent) {
 	cmd.Flag("max-retained-log-count", "Set maximum number of log sessions to retain").IntVar(&c.maxRetainedLogCount)
 	cmd.Flag("max-retained-log-age", "Set maximum age of log sessions to retain").DurationVar(&c.maxRetainedLogAge)
 	cmd.Flag("max-retained-log-size-mb", "Set maximum total size of log sessions").Int64Var(&c.maxTotalRetainedLogSizeMB)
+	cmd.Flag("extend-object-locks", "Extend retention period of locked objects as part of full maintenance.").BoolListVar(&c.extendObjectLocks)
 
 	cmd.Action(svc.directRepositoryWriteAction(c.run))
 }
@@ -121,6 +124,22 @@ func (c *commandMaintenanceSet) setMaintenanceEnabledAndIntervalFromFlags(ctx co
 	}
 }
 
+func (c *commandMaintenanceSet) setMaintenanceObjectLockExtendFromFlags(ctx context.Context, p *maintenance.Params, changed *bool) {
+	// we use lists to distinguish between flag not set
+	// Zero elements == not set, more than zero - flag set, in which case we pick the last value
+	if len(c.extendObjectLocks) > 0 {
+		lastVal := c.extendObjectLocks[len(c.extendObjectLocks)-1]
+		p.ExtendObjectLocks = lastVal
+		*changed = true
+
+		if lastVal {
+			log(ctx).Info("Object Lock extension maintenance enabled.")
+		} else {
+			log(ctx).Info("Object Lock extension maintenance disabled.")
+		}
+	}
+}
+
 func (c *commandMaintenanceSet) run(ctx context.Context, rep repo.DirectRepositoryWriter) error {
 	p, err := maintenance.GetParams(ctx, rep)
 	if err != nil {
@@ -138,6 +157,7 @@ func (c *commandMaintenanceSet) run(ctx context.Context, rep repo.DirectReposito
 	c.setMaintenanceEnabledAndIntervalFromFlags(ctx, &p.QuickCycle, "quick", c.maintenanceSetEnableQuick, c.maintenanceSetQuickFrequency, &changedParams)
 	c.setMaintenanceEnabledAndIntervalFromFlags(ctx, &p.FullCycle, "full", c.maintenanceSetEnableFull, c.maintenanceSetFullFrequency, &changedParams)
 	c.setLogCleanupParametersFromFlags(ctx, p, &changedParams)
+	c.setMaintenanceObjectLockExtendFromFlags(ctx, p, &changedParams)
 
 	if pauseDuration := c.maintenanceSetPauseQuick; pauseDuration != -1 {
 		s.NextQuickMaintenanceTime = rep.Time().Add(pauseDuration)
@@ -157,6 +177,15 @@ func (c *commandMaintenanceSet) run(ctx context.Context, rep repo.DirectReposito
 		return errors.Errorf("no changes specified")
 	}
 
+	blobCfg, err := rep.FormatManager().BlobCfgBlob()
+	if err != nil {
+		return errors.Wrap(err, "blob configuration")
+	}
+
+	if err = maintenance.CheckExtendRetention(ctx, blobCfg, p); err != nil {
+		return errors.Wrap(err, "unable to apply maintenance changes")
+	}
+
 	if changedSchedule {
 		if err := maintenance.SetSchedule(ctx, rep, s); err != nil {
 			return errors.Wrap(err, "unable to set schedule")
diff --git a/cli/command_repository_set_parameters.go b/cli/command_repository_set_parameters.go
index d898a892..d9b76ea2 100644
--- a/cli/command_repository_set_parameters.go
+++ b/cli/command_repository_set_parameters.go
@@ -12,6 +12,7 @@
 	"github.com/kopia/kopia/repo"
 	"github.com/kopia/kopia/repo/blob"
 	"github.com/kopia/kopia/repo/format"
+	"github.com/kopia/kopia/repo/maintenance"
 )
 
 type commandRepositorySetParameters struct {
@@ -230,6 +231,17 @@ func (c *commandRepositorySetParameters) run(ctx context.Context, rep repo.Direc
 		return errors.Errorf("no changes")
 	}
 
+	if blobcfg.IsRetentionEnabled() {
+		p, err := maintenance.GetParams(ctx, rep)
+		if err != nil {
+			return errors.Wrap(err, "unable to get current maintenance parameters")
+		}
+
+		if err := maintenance.CheckExtendRetention(ctx, blobcfg, p); err != nil {
+			return errors.Wrap(err, "unable to apply maintenance changes")
+		}
+	}
+
 	if err := updateRepositoryParameters(ctx, upgradeToEpochManager, mp, rep, blobcfg, requiredFeatures); err != nil {
 		return errors.Wrap(err, "error updating repository parameters")
 	}
diff --git a/go.mod b/go.mod
index 7d51d40b..4dc171ad 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/kopia/kopia
 go 1.20
 
 require (
-	cloud.google.com/go/storage v1.30.1
+	cloud.google.com/go/storage v1.31.0
 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1
 	github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0
 	github.com/Azure/azure-storage-blob-go v0.15.0
@@ -27,23 +27,23 @@ require (
 	github.com/hanwen/go-fuse/v2 v2.3.0
 	github.com/klauspost/compress v1.16.5
 	github.com/klauspost/pgzip v1.2.6
-	github.com/klauspost/reedsolomon v1.11.7
+	github.com/klauspost/reedsolomon v1.11.8
 	github.com/kopia/htmluibuild v0.0.0-20230605144737-e386b860759d
 	github.com/kylelemons/godebug v1.1.0
 	github.com/mattn/go-colorable v0.1.13
-	github.com/minio/minio-go/v7 v7.0.56
+	github.com/minio/minio-go/v7 v7.0.59
 	github.com/natefinch/atomic v1.0.1
 	github.com/pierrec/lz4 v2.6.1+incompatible
 	github.com/pkg/errors v0.9.1
 	github.com/pkg/profile v1.7.0
 	github.com/pkg/sftp v1.13.5
-	github.com/prometheus/client_golang v1.15.1
+	github.com/prometheus/client_golang v1.16.0
 	github.com/prometheus/client_model v0.4.0
 	github.com/prometheus/common v0.44.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.3
-	github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2
+	github.com/stretchr/testify v1.8.4
+	github.com/studio-b12/gowebdav v0.9.0
 	github.com/tg123/go-htpasswd v1.2.1
 	github.com/zalando/go-keyring v0.2.3
 	github.com/zeebo/blake3 v0.2.3
@@ -53,18 +53,18 @@ require (
 	go.opentelemetry.io/otel/trace v1.16.0
 	go.uber.org/multierr v1.11.0
 	go.uber.org/zap v1.24.0
-	golang.org/x/crypto v0.9.0
+	golang.org/x/crypto v0.10.0
 	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
-	golang.org/x/mod v0.10.0
-	golang.org/x/net v0.10.0
-	golang.org/x/oauth2 v0.8.0
-	golang.org/x/sync v0.2.0
+	golang.org/x/mod v0.11.0
+	golang.org/x/net v0.11.0
+	golang.org/x/oauth2 v0.9.0
+	golang.org/x/sync v0.3.0
 	golang.org/x/sys v0.9.0
 	golang.org/x/term v0.9.0
 	golang.org/x/text v0.10.0
-	google.golang.org/api v0.127.0
-	google.golang.org/grpc v1.55.0
-	google.golang.org/protobuf v1.30.0
+	google.golang.org/api v0.129.0
+	google.golang.org/grpc v1.56.1
+	google.golang.org/protobuf v1.31.0
 	gopkg.in/kothar/go-backblaze.v0 v0.0.0-20210124194846-35409b867216
 )
 
@@ -97,8 +97,8 @@ require (
 	github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
 	github.com/google/readahead v0.0.0-20161222183148-eaceba169032 // indirect
 	github.com/google/s2a-go v0.1.4 // indirect
-	github.com/googleapis/enterprise-certificate-proxy v0.2.4 // indirect
-	github.com/googleapis/gax-go/v2 v2.10.0 // indirect
+	github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
+	github.com/googleapis/gax-go/v2 v2.11.0 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
diff --git a/go.sum b/go.sum
index 6c9fb256..5ec107f0 100644
--- a/go.sum
+++ b/go.sum
@@ -8,8 +8,8 @@ cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGB
 cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
 cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94=
 cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk=
-cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
-cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E=
+cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI=
+cloud.google.com/go/storage v1.31.0/go.mod h1:81ams1PrhW16L4kF7qg+4mTq7SRs5HsbDTM0bWvrwJ0=
 github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
 github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1 h1:SEy2xmstIphdPwNBUi7uhvjyjhVKISfwjfOJmuy7kg4=
@@ -170,10 +170,10 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.2.4 h1:uGy6JWR/uMIILU8wbf+OkstIrNiMjGpEIyhx8f6W7s4=
-github.com/googleapis/enterprise-certificate-proxy v0.2.4/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
-github.com/googleapis/gax-go/v2 v2.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg=
-github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
+github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
+github.com/googleapis/enterprise-certificate-proxy v0.2.5/go.mod h1:RxW0N9901Cko1VOCW3SXCpWP+mlIEkk2tP7jnHy9a3w=
+github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
+github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
@@ -196,8 +196,8 @@ github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/q
 github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
 github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
-github.com/klauspost/reedsolomon v1.11.7 h1:9uaHU0slncktTEEg4+7Vl7q7XUNMBUOK4R9gnKhMjAU=
-github.com/klauspost/reedsolomon v1.11.7/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
+github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY=
+github.com/klauspost/reedsolomon v1.11.8/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
 github.com/kopia/htmluibuild v0.0.0-20230605144737-e386b860759d h1:qvV3TN5X/RsgmckkxsKh9P7Vtf9GYy6vOPzQY1SY4qM=
 github.com/kopia/htmluibuild v0.0.0-20230605144737-e386b860759d/go.mod h1:eWer4rx9P8lJo2eKc+Q7AZ1dE1x1hJNdkbDFPzMu1Hw=
 github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
@@ -228,8 +228,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 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.56 h1:pkZplIEHu8vinjkmhsexcXpWth2tjVLphrTZx6fBVZY=
-github.com/minio/minio-go/v7 v7.0.56/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE=
+github.com/minio/minio-go/v7 v7.0.59 h1:lxIXwsTIcQkYoEG25rUJbzpmSB/oWeVDmxFo/uWUUsw=
+github.com/minio/minio-go/v7 v7.0.59/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE=
 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
 github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
 github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
@@ -257,8 +257,8 @@ 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.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
-github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
+github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
+github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
 github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
@@ -289,10 +289,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
-github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 h1:VsBj3UD2xyAOu7kJw6O/2jjG2UXLFoBzihqDU9Ofg9M=
-github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU=
+github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
 github.com/tg123/go-htpasswd v1.2.1 h1:i4wfsX1KvvkyoMiHZzjS0VzbAPWfxzI8INcZAKtutoU=
 github.com/tg123/go-htpasswd v1.2.1/go.mod h1:erHp1B86KXdwQf1X5ZrLb7erXZnWueEQezb2dql4q58=
 github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
@@ -334,8 +334,8 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
-golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
+golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
 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=
@@ -343,8 +343,8 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
-golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
+golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -361,20 +361,20 @@ golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
+golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
-golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
+golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs=
+golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw=
 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-20181221193216-37e7f081c4d4/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.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
-golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
+golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
 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=
@@ -422,8 +422,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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.127.0 h1:v7rj0vA0imM3Ou81k1eyFxQNScLzn71EyGnJDr+V/XI=
-google.golang.org/api v0.127.0/go.mod h1:Y611qgqaE92On/7g65MQgxYul3c0rEB894kniWLY750=
+google.golang.org/api v0.129.0 h1:2XbdjjNfFPXQyufzQVwPf1RRnHH8Den2pfNE2jw7L8w=
+google.golang.org/api v0.129.0/go.mod h1:dFjiXlanKwWE3612X97llhsoI36FAoIiRj3aTl5b/zE=
 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.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
@@ -446,8 +446,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
-google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
+google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ=
+google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -459,8 +459,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/repo/blob/azure/azure_storage.go b/repo/blob/azure/azure_storage.go
index 14a907ee..2f55ed60 100644
--- a/repo/blob/azure/azure_storage.go
+++ b/repo/blob/azure/azure_storage.go
@@ -27,6 +27,7 @@
 
 type azStorage struct {
 	Options
+	blob.UnsupportedBlobRetention
 
 	service   *azblob.Client
 	container string
diff --git a/repo/blob/b2/b2_storage.go b/repo/blob/b2/b2_storage.go
index e2fb6494..fb45e08c 100644
--- a/repo/blob/b2/b2_storage.go
+++ b/repo/blob/b2/b2_storage.go
@@ -26,6 +26,7 @@
 
 type b2Storage struct {
 	Options
+	blob.UnsupportedBlobRetention
 
 	cli    *backblaze.B2
 	bucket *backblaze.Bucket
diff --git a/repo/blob/filesystem/filesystem_storage.go b/repo/blob/filesystem/filesystem_storage.go
index 8030b58d..1a9f5e20 100644
--- a/repo/blob/filesystem/filesystem_storage.go
+++ b/repo/blob/filesystem/filesystem_storage.go
@@ -34,6 +34,7 @@
 
 type fsStorage struct {
 	sharded.Storage
+	blob.UnsupportedBlobRetention
 }
 
 type fsImpl struct {
@@ -365,7 +366,7 @@ func New(ctx context.Context, opts *Options, isCreate bool) (blob.Storage, error
 	}
 
 	return &fsStorage{
-		sharded.New(&fsImpl{*opts, osi}, opts.Path, opts.Options, isCreate),
+		Storage: sharded.New(&fsImpl{*opts, osi}, opts.Path, opts.Options, isCreate),
 	}, nil
 }
 
diff --git a/repo/blob/gcs/gcs_storage.go b/repo/blob/gcs/gcs_storage.go
index d058a4bc..bad430c9 100644
--- a/repo/blob/gcs/gcs_storage.go
+++ b/repo/blob/gcs/gcs_storage.go
@@ -32,6 +32,7 @@
 
 type gcsStorage struct {
 	Options
+	blob.UnsupportedBlobRetention
 
 	storageClient *gcsclient.Client
 	bucket        *gcsclient.BucketHandle
diff --git a/repo/blob/gdrive/gdrive_storage.go b/repo/blob/gdrive/gdrive_storage.go
index 2762e6d1..ef53176c 100644
--- a/repo/blob/gdrive/gdrive_storage.go
+++ b/repo/blob/gdrive/gdrive_storage.go
@@ -45,6 +45,7 @@
 
 type gdriveStorage struct {
 	Options
+	blob.UnsupportedBlobRetention
 
 	client      *drive.FilesService
 	about       *drive.AboutService
diff --git a/repo/blob/logging/logging_storage.go b/repo/blob/logging/logging_storage.go
index 2f8817e4..e4180e2f 100644
--- a/repo/blob/logging/logging_storage.go
+++ b/repo/blob/logging/logging_storage.go
@@ -211,6 +211,26 @@ func (s *loggingStorage) FlushCaches(ctx context.Context) error {
 	return err
 }
 
+func (s *loggingStorage) ExtendBlobRetention(ctx context.Context, b blob.ID, opts blob.ExtendOptions) error {
+	ctx, span := tracer.Start(ctx, "ExtendBlobRetention")
+	defer span.End()
+
+	s.beginConcurrency()
+	defer s.endConcurrency()
+
+	timer := timetrack.StartTimer()
+	err := s.base.ExtendBlobRetention(ctx, b, opts)
+	dt := timer.Elapsed()
+
+	s.logger.Debugw(s.prefix+"ExtendBlobRetention",
+		"blobID", b,
+		"error", err,
+		"duration", dt,
+	)
+	//nolint:wrapcheck
+	return err
+}
+
 func (s *loggingStorage) translateError(err error) interface{} {
 	if err == nil {
 		return nil
diff --git a/repo/blob/readonly/readonly_storage.go b/repo/blob/readonly/readonly_storage.go
index bbd8eee2..6171011d 100644
--- a/repo/blob/readonly/readonly_storage.go
+++ b/repo/blob/readonly/readonly_storage.go
@@ -15,6 +15,7 @@
 // readonlyStorage prevents all mutations on the underlying storage.
 type readonlyStorage struct {
 	base blob.Storage
+	blob.UnsupportedBlobRetention
 }
 
 func (s readonlyStorage) GetCapacity(ctx context.Context) (blob.Capacity, error) {
diff --git a/repo/blob/s3/s3_storage.go b/repo/blob/s3/s3_storage.go
index 6cd5b677..af2a5822 100644
--- a/repo/blob/s3/s3_storage.go
+++ b/repo/blob/s3/s3_storage.go
@@ -231,6 +231,25 @@ func (s *s3Storage) DeleteBlob(ctx context.Context, b blob.ID) error {
 	return err
 }
 
+func (s *s3Storage) ExtendBlobRetention(ctx context.Context, b blob.ID, opts blob.ExtendOptions) error {
+	retentionMode := minio.RetentionMode(opts.RetentionMode)
+	if !retentionMode.IsValid() {
+		return errors.Errorf("invalid retention mode: %q", opts.RetentionMode)
+	}
+
+	retainUntilDate := clock.Now().Add(opts.RetentionPeriod).UTC()
+
+	err := s.cli.PutObjectRetention(ctx, s.BucketName, s.getObjectNameString(b), minio.PutObjectRetentionOptions{
+		RetainUntilDate: &retainUntilDate,
+		Mode:            &retentionMode,
+	})
+	if err != nil {
+		return errors.Wrap(err, "unable to extend retention period")
+	}
+
+	return nil
+}
+
 func (s *s3Storage) getObjectNameString(b blob.ID) string {
 	return s.Prefix + string(b)
 }
diff --git a/repo/blob/sftp/sftp_storage.go b/repo/blob/sftp/sftp_storage.go
index 14c39ab7..bb310f2c 100644
--- a/repo/blob/sftp/sftp_storage.go
+++ b/repo/blob/sftp/sftp_storage.go
@@ -39,6 +39,7 @@
 // sftpStorage implements blob.Storage on top of sftp.
 type sftpStorage struct {
 	sharded.Storage
+	blob.UnsupportedBlobRetention
 }
 
 type sftpImpl struct {
@@ -540,7 +541,7 @@ func New(ctx context.Context, opts *Options, isCreate bool) (blob.Storage, error
 	}
 
 	r := &sftpStorage{
-		sharded.New(impl, opts.Path, opts.Options, isCreate),
+		Storage: sharded.New(impl, opts.Path, opts.Options, isCreate),
 	}
 
 	impl.rec = connection.NewReconnector(impl)
diff --git a/repo/blob/storage.go b/repo/blob/storage.go
index dd9a0d51..2a19a6ad 100644
--- a/repo/blob/storage.go
+++ b/repo/blob/storage.go
@@ -40,6 +40,10 @@
 // implementation that does not support the intended functionality.
 var ErrNotAVolume = errors.New("unsupported method, storage is not a volume")
 
+// ErrUnsupportedObjectLock is returned when attempting to use an Object Lock specific
+// function on a storage implementation that does not have the intended functionality.
+var ErrUnsupportedObjectLock = errors.New("object locking unsupported")
+
 // Bytes encapsulates a sequence of bytes, possibly stored in a non-contiguous buffers,
 // which can be written sequentially or treated as a io.Reader.
 type Bytes interface {
@@ -128,6 +132,20 @@ type PutOptions struct {
 	GetModTime *time.Time // if != nil, populate the value pointed at with the actual modification time
 }
 
+// ExtendOptions represents retention options for extending object locks.
+type ExtendOptions struct {
+	RetentionMode   RetentionMode
+	RetentionPeriod time.Duration
+}
+
+// UnsupportedBlobRetention provides a default implementation for ExtendBlobRetention.
+type UnsupportedBlobRetention struct{}
+
+// ExtendBlobRetention provides a common implementation for unsupported blob retention storage.
+func (s *UnsupportedBlobRetention) ExtendBlobRetention(context.Context, ID, ExtendOptions) error {
+	return ErrUnsupportedObjectLock
+}
+
 // HasRetentionOptions returns true when blob-retention settings have been
 // specified, otherwise returns false.
 func (o PutOptions) HasRetentionOptions() bool {
@@ -161,6 +179,9 @@ type Storage interface {
 
 	// FlushCaches flushes any local caches associated with storage.
 	FlushCaches(ctx context.Context) error
+
+	// ExtendBlobRetention extends the retention time of a blob (when blob retention is enabled)
+	ExtendBlobRetention(ctx context.Context, blobID ID, opts ExtendOptions) error
 }
 
 // ID is a string that represents blob identifier.
diff --git a/repo/blob/storagemetrics/storage_metrics.go b/repo/blob/storagemetrics/storage_metrics.go
index 6fe1de01..68b3d656 100644
--- a/repo/blob/storagemetrics/storage_metrics.go
+++ b/repo/blob/storagemetrics/storage_metrics.go
@@ -18,24 +18,26 @@ type blobMetrics struct {
 	uploadedBytes          *metrics.Counter
 	listBlobItems          *metrics.Counter
 
-	getBlobPartialDuration *metrics.Distribution[time.Duration]
-	getBlobFullDuration    *metrics.Distribution[time.Duration]
-	putBlobDuration        *metrics.Distribution[time.Duration]
-	getCapacityDuration    *metrics.Distribution[time.Duration]
-	getMetadataDuration    *metrics.Distribution[time.Duration]
-	deleteBlobDuration     *metrics.Distribution[time.Duration]
-	listBlobsDuration      *metrics.Distribution[time.Duration]
-	closeDuration          *metrics.Distribution[time.Duration]
-	flushCachesDuration    *metrics.Distribution[time.Duration]
-
-	getBlobErrors     *metrics.Counter
-	getCapacityErrors *metrics.Counter
-	getMetadataErrors *metrics.Counter
-	putBlobErrors     *metrics.Counter
-	deleteBlobErrors  *metrics.Counter
-	listBlobsErrors   *metrics.Counter
-	closeErrors       *metrics.Counter
-	flushCachesErrors *metrics.Counter
+	getBlobPartialDuration      *metrics.Distribution[time.Duration]
+	getBlobFullDuration         *metrics.Distribution[time.Duration]
+	putBlobDuration             *metrics.Distribution[time.Duration]
+	getCapacityDuration         *metrics.Distribution[time.Duration]
+	getMetadataDuration         *metrics.Distribution[time.Duration]
+	deleteBlobDuration          *metrics.Distribution[time.Duration]
+	extendBlobRetentionDuration *metrics.Distribution[time.Duration]
+	listBlobsDuration           *metrics.Distribution[time.Duration]
+	closeDuration               *metrics.Distribution[time.Duration]
+	flushCachesDuration         *metrics.Distribution[time.Duration]
+
+	getBlobErrors             *metrics.Counter
+	getCapacityErrors         *metrics.Counter
+	getMetadataErrors         *metrics.Counter
+	putBlobErrors             *metrics.Counter
+	deleteBlobErrors          *metrics.Counter
+	extendBlobRetentionErrors *metrics.Counter
+	listBlobsErrors           *metrics.Counter
+	closeErrors               *metrics.Counter
+	flushCachesErrors         *metrics.Counter
 }
 
 func (s *blobMetrics) GetBlob(ctx context.Context, id blob.ID, offset, length int64, output blob.OutputBuffer) error {
@@ -121,6 +123,21 @@ func (s *blobMetrics) DeleteBlob(ctx context.Context, id blob.ID) error {
 	return err
 }
 
+func (s *blobMetrics) ExtendBlobRetention(ctx context.Context, id blob.ID, opts blob.ExtendOptions) error {
+	timer := timetrack.StartTimer()
+	err := s.base.ExtendBlobRetention(ctx, id, opts)
+	dt := timer.Elapsed()
+
+	s.extendBlobRetentionDuration.Observe(dt)
+
+	if err != nil {
+		s.extendBlobRetentionErrors.Add(1)
+	}
+
+	//nolint:wrapcheck
+	return err
+}
+
 func (s *blobMetrics) ListBlobs(ctx context.Context, prefix blob.ID, callback func(blob.Metadata) error) error {
 	timer := timetrack.StartTimer()
 	cnt := int64(0)
diff --git a/repo/blob/throttling/throttling_storage.go b/repo/blob/throttling/throttling_storage.go
index 3662bbaa..5c5f3272 100644
--- a/repo/blob/throttling/throttling_storage.go
+++ b/repo/blob/throttling/throttling_storage.go
@@ -14,11 +14,12 @@
 
 // operations supported.
 const (
-	operationGetBlob     = "GetBlob"
-	operationGetMetadata = "GetMetadata"
-	operationListBlobs   = "ListBlobs"
-	operationPutBlob     = "PutBlob"
-	operationDeleteBlob  = "DeleteBlob"
+	operationGetBlob             = "GetBlob"
+	operationGetMetadata         = "GetMetadata"
+	operationListBlobs           = "ListBlobs"
+	operationPutBlob             = "PutBlob"
+	operationDeleteBlob          = "DeleteBlob"
+	operationExtendBlobRetention = "ExtendBlobRetention"
 )
 
 // Throttler implements throttling policy by blocking before certain operations are
@@ -104,6 +105,13 @@ func (s *throttlingStorage) DeleteBlob(ctx context.Context, id blob.ID) error {
 	return s.Storage.DeleteBlob(ctx, id) //nolint:wrapcheck
 }
 
+func (s *throttlingStorage) ExtendBlobRetention(ctx context.Context, id blob.ID, opts blob.ExtendOptions) error {
+	s.throttler.BeforeOperation(ctx, operationExtendBlobRetention)
+	defer s.throttler.AfterOperation(ctx, operationExtendBlobRetention)
+
+	return s.Storage.ExtendBlobRetention(ctx, id, opts) //nolint:wrapcheck
+}
+
 // NewWrapper returns a Storage wrapper that adds retry loop around all operations of the underlying storage.
 func NewWrapper(wrapped blob.Storage, throttler Throttler) blob.Storage {
 	return &throttlingStorage{wrapped, throttler}
diff --git a/repo/blob/webdav/webdav_storage.go b/repo/blob/webdav/webdav_storage.go
index d5af61bd..a8b503be 100644
--- a/repo/blob/webdav/webdav_storage.go
+++ b/repo/blob/webdav/webdav_storage.go
@@ -36,6 +36,7 @@
 // may be accessed using WebDAV or File interchangeably.
 type davStorage struct {
 	sharded.Storage
+	blob.UnsupportedBlobRetention
 }
 
 type davStorageImpl struct {
@@ -277,7 +278,7 @@ func New(ctx context.Context, opts *Options, isCreate bool) (blob.Storage, error
 	}
 
 	s := retrying.NewWrapper(&davStorage{
-		sharded.New(&davStorageImpl{
+		Storage: sharded.New(&davStorageImpl{
 			Options: *opts,
 			cli:     cli,
 		}, "", opts.Options, isCreate),
diff --git a/repo/locking_storage.go b/repo/locking_storage.go
new file mode 100644
index 00000000..3500a71a
--- /dev/null
+++ b/repo/locking_storage.go
@@ -0,0 +1,22 @@
+package repo
+
+import (
+	"github.com/kopia/kopia/internal/epoch"
+	"github.com/kopia/kopia/repo/content"
+	"github.com/kopia/kopia/repo/content/indexblob"
+	"github.com/kopia/kopia/repo/format"
+)
+
+// GetLockingStoragePrefixes Return all prefixes that may be maintained by Object Locking.
+func GetLockingStoragePrefixes() []string {
+	var prefixes []string
+	// collect prefixes that need to be locked on put
+	for _, prefix := range content.PackBlobIDPrefixes {
+		prefixes = append(prefixes, string(prefix))
+	}
+
+	prefixes = append(prefixes, indexblob.V0IndexBlobPrefix, epoch.EpochManagerIndexUberPrefix, format.KopiaRepositoryBlobID,
+		format.KopiaBlobCfgBlobID)
+
+	return prefixes
+}
diff --git a/repo/maintenance/blob_retain.go b/repo/maintenance/blob_retain.go
new file mode 100644
index 00000000..e195f63c
--- /dev/null
+++ b/repo/maintenance/blob_retain.go
@@ -0,0 +1,140 @@
+package maintenance
+
+import (
+	"context"
+	"runtime"
+	"sync"
+	"sync/atomic"
+	"time"
+
+	"github.com/pkg/errors"
+
+	"github.com/kopia/kopia/repo"
+	"github.com/kopia/kopia/repo/blob"
+	"github.com/kopia/kopia/repo/format"
+)
+
+const parallelBlobRetainCPUMultiplier = 2
+
+const minRetentionMaintenanceDiff = time.Duration(24) * time.Hour
+
+// ExtendBlobRetentionTimeOptions provides options for extending blob retention algorithm.
+type ExtendBlobRetentionTimeOptions struct {
+	Parallel int
+	DryRun   bool
+}
+
+// ExtendBlobRetentionTime extends the retention time of all relevant blobs managed by storage engine with Object Locking enabled.
+func ExtendBlobRetentionTime(ctx context.Context, rep repo.DirectRepositoryWriter, opt ExtendBlobRetentionTimeOptions) (int, error) {
+	const extendQueueSize = 100
+
+	var (
+		wg        sync.WaitGroup
+		prefixes  []blob.ID
+		cnt       = new(uint32)
+		toExtend  = new(uint32)
+		failedCnt = new(uint32)
+	)
+
+	if opt.Parallel == 0 {
+		opt.Parallel = runtime.NumCPU() * parallelBlobRetainCPUMultiplier
+	}
+
+	blobCfg, err := rep.FormatManager().BlobCfgBlob()
+	if err != nil {
+		return 0, errors.Wrap(err, "blob configuration")
+	}
+
+	if !blobCfg.IsRetentionEnabled() {
+		// Blob retention is disabled
+		log(ctx).Info("Object lock retention is disabled.")
+		return 0, nil
+	}
+
+	extend := make(chan blob.Metadata, extendQueueSize)
+	extendOpts := blob.ExtendOptions{
+		RetentionMode:   blobCfg.RetentionMode,
+		RetentionPeriod: blobCfg.RetentionPeriod,
+	}
+
+	if !opt.DryRun {
+		// start goroutines to extend blob retention as they come.
+		for i := 0; i < opt.Parallel; i++ {
+			wg.Add(1)
+
+			go func() {
+				defer wg.Done()
+
+				for bm := range extend {
+					if err1 := rep.BlobStorage().ExtendBlobRetention(ctx, bm.BlobID, extendOpts); err1 != nil {
+						log(ctx).Errorf("Failed to extend blob %v: %v", bm.BlobID, err1)
+						atomic.AddUint32(failedCnt, 1)
+
+						continue
+					}
+
+					curCnt := atomic.AddUint32(cnt, 1)
+					if curCnt%100 == 0 {
+						log(ctx).Infof("  extended %v blobs", curCnt)
+					}
+				}
+			}()
+		}
+	}
+
+	// Convert prefixes from string to BlobID.
+	for _, pfx := range repo.GetLockingStoragePrefixes() {
+		prefixes = append(prefixes, blob.ID(pfx))
+	}
+
+	// iterate all relevant (active, extendable) blobs and count them + optionally send to the channel to be extended
+	log(ctx).Infof("Extending retention time for blobs...")
+
+	err = blob.IterateAllPrefixesInParallel(ctx, opt.Parallel, rep.BlobStorage(), prefixes, func(bm blob.Metadata) error {
+		if !opt.DryRun {
+			extend <- bm
+		}
+
+		atomic.AddUint32(toExtend, 1)
+		return nil
+	})
+
+	close(extend)
+	log(ctx).Infof("Found %v blobs to extend", *toExtend)
+
+	// wait for all extend workers to finish.
+	wg.Wait()
+
+	if *failedCnt > 0 {
+		return 0, errors.Errorf("Failed to extend %v blobs", *failedCnt)
+	}
+
+	if err != nil {
+		return 0, errors.Wrap(err, "error iterating packs")
+	}
+
+	if opt.DryRun {
+		return int(*toExtend), nil
+	}
+
+	log(ctx).Infof("Extended total %v blobs", *cnt)
+
+	return int(*cnt), nil
+}
+
+// CheckExtendRetention verifies if extension can be enabled due to maintenance and blob parameters.
+func CheckExtendRetention(ctx context.Context, blobCfg format.BlobStorageConfiguration, p *Params) error {
+	if !p.ExtendObjectLocks {
+		return nil
+	}
+
+	if !p.FullCycle.Enabled {
+		log(ctx).Warn("Object Lock extension will not function because Full-Maintenance is disabled")
+	}
+
+	if blobCfg.RetentionPeriod > 0 && blobCfg.RetentionPeriod-p.FullCycle.Interval < minRetentionMaintenanceDiff {
+		return errors.Errorf("The repo RetentionPeriod must be %v greater than the Full Maintenance interval %v %v", minRetentionMaintenanceDiff, blobCfg.RetentionPeriod, p.FullCycle.Interval)
+	}
+
+	return nil
+}
diff --git a/repo/maintenance/maintenance_params.go b/repo/maintenance/maintenance_params.go
index f70e7f64..3cbbb208 100644
--- a/repo/maintenance/maintenance_params.go
+++ b/repo/maintenance/maintenance_params.go
@@ -23,6 +23,8 @@ type Params struct {
 	FullCycle  CycleParams `json:"full"`
 
 	LogRetention LogRetentionOptions `json:"logRetention"`
+
+	ExtendObjectLocks bool `json:"extendObjectLocks"`
 }
 
 func (p *Params) isOwnedByByThisUser(rep repo.Repository) bool {
@@ -41,6 +43,11 @@ func DefaultParams() Params {
 			Interval: 1 * time.Hour,
 		},
 		LogRetention: defaultLogRetention(),
+		// Don't attempt to extend object locks by default. This option may not be
+		// supported by all storage providers or blob implementations (currently
+		// supported by S3 backend) and may cause data to be kept longer than
+		// desired if the retention period is relatively long.
+		ExtendObjectLocks: false,
 	}
 }
 
diff --git a/repo/maintenance/maintenance_run.go b/repo/maintenance/maintenance_run.go
index 1e08cb1c..455e0da0 100644
--- a/repo/maintenance/maintenance_run.go
+++ b/repo/maintenance/maintenance_run.go
@@ -36,15 +36,16 @@
 
 // Task IDs.
 const (
-	TaskSnapshotGarbageCollection = "snapshot-gc"
-	TaskDeleteOrphanedBlobsQuick  = "quick-delete-blobs"
-	TaskDeleteOrphanedBlobsFull   = "full-delete-blobs"
-	TaskRewriteContentsQuick      = "quick-rewrite-contents"
-	TaskRewriteContentsFull       = "full-rewrite-contents"
-	TaskDropDeletedContentsFull   = "full-drop-deleted-content"
-	TaskIndexCompaction           = "index-compaction"
-	TaskCleanupLogs               = "cleanup-logs"
-	TaskCleanupEpochManager       = "cleanup-epoch-manager"
+	TaskSnapshotGarbageCollection   = "snapshot-gc"
+	TaskDeleteOrphanedBlobsQuick    = "quick-delete-blobs"
+	TaskDeleteOrphanedBlobsFull     = "full-delete-blobs"
+	TaskRewriteContentsQuick        = "quick-rewrite-contents"
+	TaskRewriteContentsFull         = "full-rewrite-contents"
+	TaskDropDeletedContentsFull     = "full-drop-deleted-content"
+	TaskIndexCompaction             = "index-compaction"
+	TaskExtendBlobRetentionTimeFull = "extend-blob-retention-time"
+	TaskCleanupLogs                 = "cleanup-logs"
+	TaskCleanupEpochManager         = "cleanup-epoch-manager"
 )
 
 // shouldRun returns Mode if repository is due for periodic maintenance.
@@ -400,6 +401,13 @@ func runTaskDeleteOrphanedBlobsQuick(ctx context.Context, runParams RunParameter
 	})
 }
 
+func runTaskExtendBlobRetentionTimeFull(ctx context.Context, runParams RunParameters, s *Schedule) error {
+	return ReportRun(ctx, runParams.rep, TaskExtendBlobRetentionTimeFull, s, func() error {
+		_, err := ExtendBlobRetentionTime(ctx, runParams.rep, ExtendBlobRetentionTimeOptions{})
+		return err
+	})
+}
+
 func runFullMaintenance(ctx context.Context, runParams RunParameters, safety SafetyParameters) error {
 	s, err := GetSchedule(ctx, runParams.rep)
 	if err != nil {
@@ -431,6 +439,15 @@ func runFullMaintenance(ctx context.Context, runParams RunParameters, safety Saf
 		notDeletingOrphanedBlobs(ctx, s, safety)
 	}
 
+	// extend retention-time on supported storage.
+	if runParams.Params.ExtendObjectLocks {
+		if err := runTaskExtendBlobRetentionTimeFull(ctx, runParams, s); err != nil {
+			return errors.Wrap(err, "error extending object lock retention time")
+		}
+	} else {
+		log(ctx).Debug("Extending object lock retention-period is disabled.")
+	}
+
 	if err := runTaskCleanupLogs(ctx, runParams, s); err != nil {
 		return errors.Wrap(err, "error cleaning up logs")
 	}
diff --git a/repo/open.go b/repo/open.go
index dd7c5a20..9aa44c1e 100644
--- a/repo/open.go
+++ b/repo/open.go
@@ -14,7 +14,6 @@
 
 	"github.com/kopia/kopia/internal/cache"
 	"github.com/kopia/kopia/internal/cacheprot"
-	"github.com/kopia/kopia/internal/epoch"
 	"github.com/kopia/kopia/internal/feature"
 	"github.com/kopia/kopia/internal/metrics"
 	"github.com/kopia/kopia/internal/retry"
@@ -25,7 +24,6 @@
 	"github.com/kopia/kopia/repo/blob/storagemetrics"
 	"github.com/kopia/kopia/repo/blob/throttling"
 	"github.com/kopia/kopia/repo/content"
-	"github.com/kopia/kopia/repo/content/indexblob"
 	"github.com/kopia/kopia/repo/format"
 	"github.com/kopia/kopia/repo/logging"
 	"github.com/kopia/kopia/repo/manifest"
@@ -394,13 +392,7 @@ func handleMissingRequiredFeatures(ctx context.Context, fmgr *format.Manager, ig
 
 func wrapLockingStorage(st blob.Storage, r format.BlobStorageConfiguration) blob.Storage {
 	// collect prefixes that need to be locked on put
-	var prefixes []string
-	for _, prefix := range content.PackBlobIDPrefixes {
-		prefixes = append(prefixes, string(prefix))
-	}
-
-	prefixes = append(prefixes, indexblob.V0IndexBlobPrefix, epoch.EpochManagerIndexUberPrefix, format.KopiaRepositoryBlobID,
-		format.KopiaBlobCfgBlobID)
+	prefixes := GetLockingStoragePrefixes()
 
 	return beforeop.NewWrapper(st, nil, nil, nil, func(ctx context.Context, id blob.ID, opts *blob.PutOptions) error {
 		for _, prefix := range prefixes {


Test patch:
diff --git a/cli/command_maintenance_set_test.go b/cli/command_maintenance_set_test.go
new file mode 100644
index 00000000..41dd22a6
--- /dev/null
+++ b/cli/command_maintenance_set_test.go
@@ -0,0 +1,83 @@
+package cli_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/kopia/kopia/cli"
+	"github.com/kopia/kopia/internal/testutil"
+	"github.com/kopia/kopia/repo/blob"
+	"github.com/kopia/kopia/tests/testenv"
+)
+
+func TestMaintenanceSetExtendObjectLocks(t *testing.T) {
+	t.Parallel()
+
+	e := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, testenv.NewInProcRunner(t))
+	defer e.RunAndExpectSuccess(t, "repo", "disconnect")
+
+	var mi cli.MaintenanceInfo
+
+	e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir)
+
+	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "maintenance", "info", "--json"), &mi)
+
+	require.False(t, mi.ExtendObjectLocks, "ExtendOjectLocks should not default to enabled.")
+
+	e.RunAndExpectSuccess(t, "maintenance", "set", "--extend-object-locks", "true")
+
+	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "maintenance", "info", "--json"), &mi)
+
+	require.True(t, mi.ExtendObjectLocks, "ExtendOjectLocks should be enabled.")
+
+	e.RunAndExpectSuccess(t, "maintenance", "set", "--extend-object-locks", "false")
+
+	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "maintenance", "info", "--json"), &mi)
+
+	require.False(t, mi.ExtendObjectLocks, "ExtendOjectLocks should be disabled.")
+}
+
+func (s *formatSpecificTestSuite) TestInvalidExtendRetainOptions(t *testing.T) {
+	var mi cli.MaintenanceInfo
+
+	var rs cli.RepositoryStatus
+
+	e := s.setupInMemoryRepo(t)
+
+	// set retention
+	e.RunAndExpectSuccess(t, "repository", "set-parameters", "--retention-mode", blob.Compliance.String(),
+		"--retention-period", "48h")
+
+	e.RunAndExpectSuccess(t, "maintenance", "set", "--full-interval", "24h01m")
+
+	// Cannot enable extend object locks when retention_period-full_maintenance_interval < 24h
+	e.RunAndExpectFailure(t, "maintenance", "set", "--extend-object-locks", "true")
+
+	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "maintenance", "info", "--json"), &mi)
+
+	require.False(t, mi.ExtendObjectLocks, "ExtendOjectLocks should be disabled.")
+
+	// Enable extend object locks when retention_period-full_maintenance_interval > 24h
+	e.RunAndExpectSuccess(t, "maintenance", "set", "--full-interval", "23h59m")
+
+	e.RunAndExpectSuccess(t, "maintenance", "set", "--extend-object-locks", "true")
+
+	// Cannot change full_maintenance_interval when retention_period-full_maintenance_interval < 24h
+	e.RunAndExpectFailure(t, "maintenance", "set", "--full-interval", "24h01m")
+
+	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "maintenance", "info", "--json"), &mi)
+
+	require.True(t, mi.ExtendObjectLocks, "ExtendOjectLocks should be enabled.")
+	require.True(t, mi.FullCycle.Interval == 86340000000000, "maintenance-interval should be unchanged.")
+
+	// Cannot change retention_period when retention_period-full_maintenance_interval < 24h
+	e.RunAndExpectFailure(t, "repository", "set-parameters", "--retention-period", "47h")
+	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "repo", "status", "--json"), &rs)
+	require.True(t, rs.BlobRetention.RetentionPeriod == 172800000000000, "retention-interval should be unchanged.")
+
+	// Can change retention_period when retention_period-full_maintenance_interval > 24h
+	e.RunAndExpectSuccess(t, "repository", "set-parameters", "--retention-period", "49h")
+	testutil.MustParseJSONLines(t, e.RunAndExpectSuccess(t, "repo", "status", "--json"), &rs)
+	require.True(t, rs.BlobRetention.RetentionPeriod == 176400000000000, "retention-interval should be unchanged.")
+}
diff --git a/internal/blobtesting/eventually_consistent.go b/internal/blobtesting/eventually_consistent.go
index db6553c3..82e82cde 100644
--- a/internal/blobtesting/eventually_consistent.go
+++ b/internal/blobtesting/eventually_consistent.go
@@ -285,6 +285,10 @@ func (s *eventuallyConsistentStorage) FlushCaches(ctx context.Context) error {
 	return s.realStorage.FlushCaches(ctx)
 }
 
+func (s *eventuallyConsistentStorage) ExtendBlobRetention(ctx context.Context, b blob.ID, opts blob.ExtendOptions) error {
+	return s.realStorage.ExtendBlobRetention(ctx, b, opts)
+}
+
 // NewEventuallyConsistentStorage returns an eventually-consistent storage wrapper on top
 // of provided storage.
 func NewEventuallyConsistentStorage(st blob.Storage, listSettleTime time.Duration, timeNow func() time.Time) blob.Storage {
diff --git a/internal/blobtesting/faulty.go b/internal/blobtesting/faulty.go
index 620724e4..f5cc99c0 100644
--- a/internal/blobtesting/faulty.go
+++ b/internal/blobtesting/faulty.go
@@ -123,4 +123,9 @@ func (s *FaultyStorage) FlushCaches(ctx context.Context) error {
 	return s.base.FlushCaches(ctx)
 }
 
+// ExtendBlobRetention implements blob.Storage.
+func (s *FaultyStorage) ExtendBlobRetention(ctx context.Context, b blob.ID, opts blob.ExtendOptions) error {
+	return s.base.ExtendBlobRetention(ctx, b, opts)
+}
+
 var _ blob.Storage = (*FaultyStorage)(nil)
diff --git a/internal/blobtesting/map.go b/internal/blobtesting/map.go
index 78a4820e..810e95a3 100644
--- a/internal/blobtesting/map.go
+++ b/internal/blobtesting/map.go
@@ -18,6 +18,7 @@
 type DataMap map[blob.ID][]byte
 
 type mapStorage struct {
+	blob.UnsupportedBlobRetention
 	// +checklocks:mutex
 	data DataMap
 	// +checklocks:mutex
diff --git a/internal/blobtesting/object_locking_map.go b/internal/blobtesting/object_locking_map.go
index 5954f6c7..9276d2fb 100644
--- a/internal/blobtesting/object_locking_map.go
+++ b/internal/blobtesting/object_locking_map.go
@@ -187,6 +187,23 @@ func (s *objectLockingMap) DeleteBlob(ctx context.Context, id blob.ID) error {
 	return nil
 }
 
+// ExtendBlobRetention will alter the retention time on a blob if it exists.
+func (s *objectLockingMap) ExtendBlobRetention(ctx context.Context, id blob.ID, opts blob.ExtendOptions) error {
+	s.mutex.Lock()
+	defer s.mutex.Unlock()
+
+	e, err := s.getLatestByID(id)
+	if err != nil {
+		return blob.ErrBlobNotFound
+	}
+
+	if !e.retentionTime.IsZero() {
+		e.retentionTime = e.mtime.Add(opts.RetentionPeriod)
+	}
+
+	return nil
+}
+
 // ListBlobs will return the list of all the objects except the ones which have
 // a delete-marker as their latest version.
 func (s *objectLockingMap) ListBlobs(ctx context.Context, prefix blob.ID, callback func(blob.Metadata) error) error {
diff --git a/internal/blobtesting/verify.go b/internal/blobtesting/verify.go
index fba682de..87d44b56 100644
--- a/internal/blobtesting/verify.go
+++ b/internal/blobtesting/verify.go
@@ -128,6 +128,18 @@ func VerifyStorage(ctx context.Context, t *testing.T, r blob.Storage, opts blob.
 		}
 	})
 
+	t.Run("ExtendBlobRetention", func(t *testing.T) {
+		err := r.ExtendBlobRetention(ctx, blocks[0].blk, blob.ExtendOptions{
+			RetentionMode:   opts.RetentionMode,
+			RetentionPeriod: opts.RetentionPeriod,
+		})
+		if opts.RetentionMode != "" && err != nil {
+			t.Fatalf("No error expected during extend retention: %v", err)
+		} else if opts.RetentionMode == "" && err == nil {
+			t.Fatal("No error found when expected during extend retention")
+		}
+	})
+
 	t.Run("DeleteBlobsAndList", func(t *testing.T) {
 		require.NoError(t, r.DeleteBlob(ctx, blocks[0].blk))
 		require.NoError(t, r.DeleteBlob(ctx, blocks[0].blk))
diff --git a/repo/blob/storage_extend_test.go b/repo/blob/storage_extend_test.go
new file mode 100644
index 00000000..1022c18a
--- /dev/null
+++ b/repo/blob/storage_extend_test.go
@@ -0,0 +1,138 @@
+package blob_test
+
+import (
+	"io"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/kopia/kopia/internal/cache"
+	"github.com/kopia/kopia/internal/faketime"
+	"github.com/kopia/kopia/internal/repotesting"
+	"github.com/kopia/kopia/repo"
+	"github.com/kopia/kopia/repo/blob"
+	"github.com/kopia/kopia/repo/encryption"
+	"github.com/kopia/kopia/repo/object"
+)
+
+var testHMACSecret = []byte{1, 2, 3}
+
+var testMasterKey = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
+
+func (s *formatSpecificTestSuite) TestExtendBlobRetention(t *testing.T) {
+	// set up fake clock which is initially synchronized to wall clock time
+	// and moved at the same speed but which can be moved forward.
+	ta := faketime.NewClockTimeWithOffset(0)
+
+	ctx, env := repotesting.NewEnvironment(t, s.formatVersion, repotesting.Options{
+		OpenOptions: func(o *repo.Options) {
+			o.TimeNowFunc = ta.NowFunc()
+		},
+		NewRepositoryOptions: func(nro *repo.NewRepositoryOptions) {
+			nro.BlockFormat.Encryption = encryption.DefaultAlgorithm
+			nro.BlockFormat.MasterKey = testMasterKey
+			nro.BlockFormat.Hash = "HMAC-SHA256"
+			nro.BlockFormat.HMACSecret = testHMACSecret
+			nro.RetentionMode = blob.Governance
+			nro.RetentionPeriod = time.Hour * 24
+		},
+	})
+	w := env.RepositoryWriter.NewObjectWriter(ctx, object.WriterOptions{})
+	io.WriteString(w, "hello world!")
+	w.Result()
+	w.Close()
+
+	env.RepositoryWriter.Flush(ctx)
+
+	blobsBefore, err := blob.ListAllBlobs(ctx, env.RepositoryWriter.BlobStorage(), "")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if got, want := len(blobsBefore), 4; got != want {
+		t.Fatalf("unexpected number of blobs after writing: %v", blobsBefore)
+	}
+
+	lastBlobIdx := len(blobsBefore) - 1
+	st := env.RootStorage().(cache.Storage)
+
+	// Verify that file is locked
+	_, err = st.TouchBlob(ctx, blobsBefore[lastBlobIdx].BlobID, time.Hour)
+	assert.EqualErrorf(t, err, "cannot alter object before retention period expires", "Altering locked object should fail")
+
+	ta.Advance(7 * 24 * time.Hour)
+
+	// Verify that file is unlocked
+	_, err = st.TouchBlob(ctx, blobsBefore[lastBlobIdx].BlobID, time.Hour)
+	if err != nil {
+		t.Fatalf("Altering expired object failed")
+	}
+
+	// Relock blob
+	err = env.RepositoryWriter.BlobStorage().ExtendBlobRetention(ctx, blobsBefore[lastBlobIdx].BlobID, blob.ExtendOptions{
+		RetentionMode:   blob.Governance,
+		RetentionPeriod: 2 * time.Hour,
+	})
+	if err != nil {
+		t.Fatalf("Extending Retention time failed, got err: %v", err)
+	}
+
+	// Verify Lock period
+	ta.Advance(1 * time.Hour)
+
+	_, err = st.TouchBlob(ctx, blobsBefore[lastBlobIdx].BlobID, time.Hour)
+	assert.EqualErrorf(t, err, "cannot alter object before retention period expires", "Altering locked object should fail")
+
+	ta.Advance(2 * time.Hour)
+
+	_, err = st.TouchBlob(ctx, blobsBefore[lastBlobIdx].BlobID, time.Hour)
+	if err != nil {
+		t.Fatalf("Altering expired object failed")
+	}
+}
+
+func (s *formatSpecificTestSuite) TestExtendBlobRetentionUnsupported(t *testing.T) {
+	// set up fake clock which is initially synchronized to wall clock time
+	// and moved at the same speed but which can be moved forward.
+	ta := faketime.NewClockTimeWithOffset(0)
+
+	ctx, env := repotesting.NewEnvironment(t, s.formatVersion, repotesting.Options{
+		OpenOptions: func(o *repo.Options) {
+			o.TimeNowFunc = ta.NowFunc()
+		},
+		NewRepositoryOptions: func(nro *repo.NewRepositoryOptions) {
+			nro.BlockFormat.Encryption = encryption.DefaultAlgorithm
+			nro.BlockFormat.MasterKey = testMasterKey
+			nro.BlockFormat.Hash = "HMAC-SHA256"
+			nro.BlockFormat.HMACSecret = testHMACSecret
+			// Ensure retention is disabled to trigger ExtendBlobRetention unsupported
+			nro.RetentionPeriod = 0
+			nro.RetentionMode = ""
+		},
+	})
+	w := env.RepositoryWriter.NewObjectWriter(ctx, object.WriterOptions{})
+	io.WriteString(w, "hello world!")
+	w.Result()
+	w.Close()
+
+	env.RepositoryWriter.Flush(ctx)
+
+	blobsBefore, err := blob.ListAllBlobs(ctx, env.RepositoryWriter.BlobStorage(), "")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if got, want := len(blobsBefore), 4; got != want {
+		t.Fatalf("unexpected number of blobs after writing: %v", blobsBefore)
+	}
+
+	lastBlobIdx := len(blobsBefore) - 1
+
+	// Extend retention time
+	err = env.RepositoryWriter.BlobStorage().ExtendBlobRetention(ctx, blobsBefore[lastBlobIdx].BlobID, blob.ExtendOptions{
+		RetentionMode:   blob.Governance,
+		RetentionPeriod: 2 * time.Hour,
+	})
+	assert.EqualErrorf(t, err, "object locking unsupported", "Storage should not support ExtendBlobRetention")
+}
diff --git a/repo/blob/suite_test.go b/repo/blob/suite_test.go
new file mode 100644
index 00000000..78b158a9
--- /dev/null
+++ b/repo/blob/suite_test.go
@@ -0,0 +1,24 @@
+package blob_test
+
+import (
+	"testing"
+
+	"github.com/kopia/kopia/internal/testutil"
+	"github.com/kopia/kopia/repo/format"
+)
+
+type formatSpecificTestSuite struct {
+	formatVersion format.Version
+}
+
+func TestFormatV1(t *testing.T) {
+	testutil.RunAllTestsWithParam(t, &formatSpecificTestSuite{format.FormatVersion1})
+}
+
+func TestFormatV2(t *testing.T) {
+	testutil.RunAllTestsWithParam(t, &formatSpecificTestSuite{format.FormatVersion2})
+}
+
+func TestFormatV3(t *testing.T) {
+	testutil.RunAllTestsWithParam(t, &formatSpecificTestSuite{format.FormatVersion3})
+}
diff --git a/repo/maintenance/blob_retain_test.go b/repo/maintenance/blob_retain_test.go
new file mode 100644
index 00000000..9dcc4653
--- /dev/null
+++ b/repo/maintenance/blob_retain_test.go
@@ -0,0 +1,123 @@
+package maintenance_test
+
+import (
+	"io"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/kopia/kopia/internal/cache"
+	"github.com/kopia/kopia/internal/faketime"
+	"github.com/kopia/kopia/internal/repotesting"
+	"github.com/kopia/kopia/repo"
+	"github.com/kopia/kopia/repo/blob"
+	"github.com/kopia/kopia/repo/encryption"
+	"github.com/kopia/kopia/repo/maintenance"
+	"github.com/kopia/kopia/repo/object"
+)
+
+const blockFormatHash = "HMAC-SHA256"
+
+func (s *formatSpecificTestSuite) TestExtendBlobRetentionTime(t *testing.T) {
+	// set up fake clock which is initially synchronized to wall clock time
+	// and moved at the same speed but which can be moved forward.
+	ta := faketime.NewClockTimeWithOffset(0)
+
+	ctx, env := repotesting.NewEnvironment(t, s.formatVersion, repotesting.Options{
+		OpenOptions: func(o *repo.Options) {
+			o.TimeNowFunc = ta.NowFunc()
+		},
+		NewRepositoryOptions: func(nro *repo.NewRepositoryOptions) {
+			nro.BlockFormat.Encryption = encryption.DefaultAlgorithm
+			nro.BlockFormat.MasterKey = testMasterKey
+			nro.BlockFormat.Hash = blockFormatHash
+			nro.BlockFormat.HMACSecret = testHMACSecret
+			nro.RetentionMode = blob.Governance
+			nro.RetentionPeriod = time.Hour * 24
+		},
+	})
+	w := env.RepositoryWriter.NewObjectWriter(ctx, object.WriterOptions{})
+	io.WriteString(w, "hello world!")
+	w.Result()
+	w.Close()
+
+	env.RepositoryWriter.Flush(ctx)
+
+	blobsBefore, err := blob.ListAllBlobs(ctx, env.RepositoryWriter.BlobStorage(), "")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if got, want := len(blobsBefore), 4; got != want {
+		t.Fatalf("unexpected number of blobs after writing: %v", blobsBefore)
+	}
+
+	lastBlobIdx := len(blobsBefore) - 1
+	st := env.RootStorage().(cache.Storage)
+
+	ta.Advance(7 * 24 * time.Hour)
+
+	if _, err = st.TouchBlob(ctx, blobsBefore[lastBlobIdx].BlobID, time.Hour); err != nil {
+		t.Fatalf("Altering expired object failed")
+	}
+
+	// extend retention time of all blobs
+	if _, err = maintenance.ExtendBlobRetentionTime(ctx, env.RepositoryWriter, maintenance.ExtendBlobRetentionTimeOptions{}); err != nil {
+		t.Fatal(err)
+	}
+
+	_, err = st.TouchBlob(ctx, blobsBefore[lastBlobIdx].BlobID, time.Hour)
+	assert.EqualErrorf(t, err, "cannot alter object before retention period expires", "Altering locked object should fail")
+}
+
+func (s *formatSpecificTestSuite) TestExtendBlobRetentionTimeDisabled(t *testing.T) {
+	// set up fake clock which is initially synchronized to wall clock time
+	// and moved at the same speed but which can be moved forward.
+	ta := faketime.NewClockTimeWithOffset(0)
+
+	ctx, env := repotesting.NewEnvironment(t, s.formatVersion, repotesting.Options{
+		OpenOptions: func(o *repo.Options) {
+			o.TimeNowFunc = ta.NowFunc()
+		},
+		NewRepositoryOptions: func(nro *repo.NewRepositoryOptions) {
+			nro.BlockFormat.Encryption = encryption.DefaultAlgorithm
+			nro.BlockFormat.MasterKey = testMasterKey
+			nro.BlockFormat.Hash = blockFormatHash
+			nro.BlockFormat.HMACSecret = testHMACSecret
+		},
+	})
+	w := env.RepositoryWriter.NewObjectWriter(ctx, object.WriterOptions{})
+	io.WriteString(w, "hello world!")
+	w.Result()
+	w.Close()
+
+	env.RepositoryWriter.Flush(ctx)
+
+	blobsBefore, err := blob.ListAllBlobs(ctx, env.RepositoryWriter.BlobStorage(), "")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if got, want := len(blobsBefore), 4; got != want {
+		t.Fatalf("unexpected number of blobs after writing: %v", blobsBefore)
+	}
+
+	lastBlobIdx := len(blobsBefore) - 1
+	st := env.RootStorage().(cache.Storage)
+
+	ta.Advance(7 * 24 * time.Hour)
+
+	if _, err = st.TouchBlob(ctx, blobsBefore[lastBlobIdx].BlobID, time.Hour); err != nil {
+		t.Fatalf("Altering expired object failed")
+	}
+
+	// extend retention time of all blobs
+	if _, err = maintenance.ExtendBlobRetentionTime(ctx, env.RepositoryWriter, maintenance.ExtendBlobRetentionTimeOptions{}); err != nil {
+		t.Fatal(err)
+	}
+
+	if _, err = st.TouchBlob(ctx, blobsBefore[lastBlobIdx].BlobID, time.Hour); err != nil {
+		t.Fatalf("Altering expired object failed")
+	}
+}