diff --git a/docs/contribute/release/sources.rst b/docs/contribute/release/sources.rst
index 3c68967e..ac2a8739 100644
--- a/docs/contribute/release/sources.rst
+++ b/docs/contribute/release/sources.rst
@@ -97,3 +97,19 @@ Eventually forward-port the new version
 :::::::::::::::::::::::::::::::::::::::
 
 If a newer version line is available, checkout and merge the new version into it.
+
+
+Create the GitHub release
+:::::::::::::::::::::::::
+
+.. code:: shell
+
+    open https://github.com/msqd/harp/releases/tag/$VERSION
+
+Create the release from tag (button on the right).
+
+To generate the markdown changes for github, use:
+
+.. code-block:: shell
+
+    pandoc -s -o docs/changelogs/$VERSION.rst changes.md
diff --git a/harp_apps/http_client/contrib/hishel/adapters.py b/harp_apps/http_client/contrib/hishel/adapters.py
index 3aa4bd2e..a1f3e156 100644
--- a/harp_apps/http_client/contrib/hishel/adapters.py
+++ b/harp_apps/http_client/contrib/hishel/adapters.py
@@ -106,7 +106,7 @@ async def _store_cache_meta(
         # with hishel's current design, it's the only decent way to make it work that we found. Maybe we want to change
         # the key-value store in the future to be able to contain content addressable and unadressable data, even maybe
         # namespaced/typed data (although we hack "content-type to do it, for now).
-        return await self.storage.put(
+        return await self.storage.force_put(
             Blob(
                 id=key,
                 data=yaml.safe_dump(
@@ -122,7 +122,7 @@ async def _store_cache_meta(
                     sort_keys=False,
                 ).encode(),
                 content_type="cache/meta",
-            )
+            ),
         )
 
     async def _serialize_request(self, request: Request) -> SerializedRequest:
diff --git a/harp_apps/storage/services/blob_storages/memory.py b/harp_apps/storage/services/blob_storages/memory.py
index 512514c3..7506e6f2 100644
--- a/harp_apps/storage/services/blob_storages/memory.py
+++ b/harp_apps/storage/services/blob_storages/memory.py
@@ -16,6 +16,8 @@ async def put(self, blob: Blob) -> Blob:
         self._blobs[blob.id] = blob
         return blob
 
+    force_put = put
+
     async def delete(self, blob_id: str):
         del self._blobs[blob_id]
 
diff --git a/harp_apps/storage/services/blob_storages/null.py b/harp_apps/storage/services/blob_storages/null.py
index 2579351b..9e8d2781 100644
--- a/harp_apps/storage/services/blob_storages/null.py
+++ b/harp_apps/storage/services/blob_storages/null.py
@@ -1,3 +1,5 @@
+from typing import override
+
 from harp.models import Blob
 from harp_apps.storage.types import IBlobStorage
 
@@ -8,9 +10,12 @@ class NullBlobStorage(IBlobStorage):
     async def get(self, blob_id: str):
         return None
 
+    @override
     async def put(self, blob: Blob) -> Blob:
         return blob
 
+    force_put = put
+
     async def delete(self, blob_id: str):
         pass
 
diff --git a/harp_apps/storage/services/blob_storages/redis.py b/harp_apps/storage/services/blob_storages/redis.py
index bdb2171a..cb67196e 100644
--- a/harp_apps/storage/services/blob_storages/redis.py
+++ b/harp_apps/storage/services/blob_storages/redis.py
@@ -28,6 +28,8 @@ async def put(self, blob: Blob) -> Blob:
         await self.client.set(blob.id, ((blob.content_type or "") + "\n").encode() + blob.data)
         return blob
 
+    force_put = put
+
     @override
     async def delete(self, blob_id):
         await self.client.delete(blob_id)
diff --git a/harp_apps/storage/services/blob_storages/sql.py b/harp_apps/storage/services/blob_storages/sql.py
index f5de3221..f0d8a421 100644
--- a/harp_apps/storage/services/blob_storages/sql.py
+++ b/harp_apps/storage/services/blob_storages/sql.py
@@ -1,7 +1,7 @@
 from collections import OrderedDict
 from typing import override
 
-from sqlalchemy import delete, insert, select
+from sqlalchemy import delete, insert, select, update
 from sqlalchemy.exc import IntegrityError
 from sqlalchemy.ext.asyncio import AsyncEngine
 
@@ -96,6 +96,21 @@ async def put(self, blob: Blob) -> Blob:
                     pass  # already there? that's fine!
         return blob
 
+    @override
+    async def force_put(self, blob: Blob) -> Blob:
+        async with self.engine.connect() as conn:
+            try:
+                await conn.execute(
+                    insert(SqlBlob).values(id=blob.id, data=blob.data, content_type=blob.content_type),
+                )
+                await conn.commit()
+            except IntegrityError:
+                await conn.execute(
+                    update(SqlBlob).where(SqlBlob.id == blob.id).values(data=blob.data, content_type=blob.content_type),
+                )
+                await conn.commit()
+        return blob
+
     @override
     async def delete(self, blob_id):
         """
diff --git a/harp_apps/storage/types/blob_storage.py b/harp_apps/storage/types/blob_storage.py
index 56731edf..a3c76709 100644
--- a/harp_apps/storage/types/blob_storage.py
+++ b/harp_apps/storage/types/blob_storage.py
@@ -10,6 +10,8 @@ async def get(self, blob_id: str): ...
 
     async def put(self, blob: Blob) -> Blob: ...
 
+    async def force_put(self, blob: Blob) -> Blob: ...
+
     async def delete(self, blob_id: str): ...
 
     async def exists(self, blob_id: str) -> bool: ...