You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Studio uploads each file in a single signed PUT — an interruption restarts from byte 0, and very large objects are unsupported. Add an opt-in GCS resumable scheme: when a client passes resumable to upload_url, the server initiates a resumable session and returns a session URI for chunked uploads. Single-PUT stays unchanged for existing clients. This unblocks the ricecooker large-file client; the web frontend migration is deferred to a follow-up.
Complexity: Medium Target branch: hotfixes
Context
get_presigned_upload_url / _get_gcs_presigned_put_url sign a single PUT (content_md5, content_type); the web frontend POSTs /api/file/upload_url then PUTs the whole file. A GCS resumable upload is a POST initiation (x-goog-resumable: start) returning a session URI that is the credential for chunked PUTs.
Security model to preserve. Files are content-addressed (path = MD5 checksum); the server stays source of truth. A client must not:
store content not hashing to the checksum;
dedup-match an object whose md5Hash differs from the checksum;
bypass quota by under-declaring size.
Spike outcome (resolved).
A signedx-goog-hash / md5Hash at the initiation is not GCS-enforced for signed-URL resumable uploads.
Adopted: the server initiates the resumable session itself (bytes-free JSON-API POST pinning md5Hash to the expected checksum and recording the declared size as object metadata) and returns only the session URI; GCS rejects non-matching bytes at finalize (400).
Tradeoff: resumable sessions are pinned to the initiating region, so distant clients upload cross-region.
Constraint. Proxying uploads through the app server is not an option — it previously caused severe app-server performance problems. Integrity must hold within the direct-upload model.
The Change
Backend (upload_url): accept a resumable flag.
Absent / false: return the existing signed single-PUT, unchanged.
Absent / false with declared size > 500 MB: error — large files must use resumable.
True, object already stored (GCS md5Hash equals the checksum): return a definitive skip — no session, no client HEAD.
True, otherwise: initiate the resumable session server-side (JSON-API POST), pinning md5Hash to the checksum and recording the declared size as custom metadata (declared-size); return the session URI.
I used Claude (Opus 4.8) to verify the GCS resumable and checksum mechanics against the docs, run the integrity spike, and draft this issue. I drove the security analysis and the design decisions: server-initiated sessions over signed-URL initiation, and an opt-in resumable flag so existing single-PUT clients are untouched. I reviewed every claim against the cited GCS documentation.
Overview
Studio uploads each file in a single signed
PUT— an interruption restarts from byte 0, and very large objects are unsupported. Add an opt-in GCS resumable scheme: when a client passesresumabletoupload_url, the server initiates a resumable session and returns a session URI for chunked uploads. Single-PUTstays unchanged for existing clients. This unblocks the ricecooker large-file client; the web frontend migration is deferred to a follow-up.Complexity: Medium
Target branch: hotfixes
Context
get_presigned_upload_url/_get_gcs_presigned_put_urlsign a singlePUT(content_md5,content_type); the web frontendPOSTs/api/file/upload_urlthenPUTs the whole file. A GCS resumable upload is aPOSTinitiation (x-goog-resumable: start) returning a session URI that is the credential for chunkedPUTs.Security model to preserve. Files are content-addressed (path = MD5 checksum); the server stays source of truth. A client must not:
md5Hashdiffers from the checksum;Spike outcome (resolved).
x-goog-hash/md5Hashat the initiation is not GCS-enforced for signed-URL resumable uploads.POSTpinningmd5Hashto the expected checksum and recording the declared size as object metadata) and returns only the session URI; GCS rejects non-matching bytes at finalize (400).Constraint. Proxying uploads through the app server is not an option — it previously caused severe app-server performance problems. Integrity must hold within the direct-upload model.
The Change
upload_url): accept aresumableflag.PUT, unchanged.md5Hashequals the checksum): return a definitive skip — no session, no client HEAD.POST), pinningmd5Hashto the checksum and recording the declared size as custom metadata (declared-size); return the session URI.PUTfor now (S3 multipart added in Add S3 multipart upload support for the dev/minio backend #5990).md5Hashmakes GCS reject non-matching bytes at finalize.md5Hashvs path checksum, actual vs recorded size) and lifecycle cleanup.Out of Scope
PUT/ client cutover.File.file_sizechange.crc32c, notmd5Hash).Acceptance Criteria
upload_urlaccepts aresumableflag; absent or false returns the existing signed single-PUT, unchanged.upload_urlerrors on a non-resumable request whose declared size exceeds 500 MB.upload_urlreturns a definitive skip when GCS'smd5Hashequals the checksum, and a session URI otherwise — no client HEAD.md5Hashpinned to the checksum and declared size recorded as custom metadata (declared-size).PUTclients are unaffected.References
UNSIGNED-PAYLOADrequired for resumable initiation): https://docs.cloud.google.com/storage/docs/authentication/canonical-requestsAI usage
I used Claude (Opus 4.8) to verify the GCS resumable and checksum mechanics against the docs, run the integrity spike, and draft this issue. I drove the security analysis and the design decisions: server-initiated sessions over signed-URL initiation, and an opt-in
resumableflag so existing single-PUTclients are untouched. I reviewed every claim against the cited GCS documentation.