max / makenotwork
1 file changed,
+13 insertions,
-0 deletions
| @@ -111,6 +111,19 @@ pub(super) async fn blob_upload_url( | |||
| 111 | 111 | /// | |
| 112 | 112 | /// Verifies the object exists in S3, then records it in the database. | |
| 113 | 113 | /// Idempotent: returns success without creating a duplicate. | |
| 114 | + | /// | |
| 115 | + | /// Content-addressing trust model (ultra-fuzz Run 4 Storage NOTE, decision | |
| 116 | + | /// 2026-06-23): the blob `hash` is treated as a content-address LABEL — confirm | |
| 117 | + | /// reads the authoritative `object_size` from S3 but does not re-hash the bytes | |
| 118 | + | /// to prove they match `hash`. The blast radius is per-user only: the key is | |
| 119 | + | /// `{app_id}/{user_id}/{hash}` and storage is `UNIQUE(app_id, user_id, hash)`, | |
| 120 | + | /// so a client that stores mismatched bytes can poison only its OWN dedup | |
| 121 | + | /// namespace — no cross-user effect, no data exposure. The correct A+ fix binds | |
| 122 | + | /// `x-amz-checksum-sha256` into the presigned PUT so S3 itself rejects a | |
| 123 | + | /// mismatched upload at write time (zero server-side download); that requires the | |
| 124 | + | /// `synckit-client` SDK to send the checksum header, so it is deferred to the | |
| 125 | + | /// SDK's own remediation rather than paying a full object download + re-hash on | |
| 126 | + | /// every confirm here. | |
| 114 | 127 | #[utoipa::path(post, path = "/api/v1/sync/blobs/confirm", tag = "SyncKit", | |
| 115 | 128 | request_body = BlobConfirmRequest, | |
| 116 | 129 | responses((status = 204, description = "Upload confirmed")), |