API
Sending the bytes
POST /api/uploads gives you a sessionUri. The bytes go to Google Cloud Storage directly — not through KAIROS — so a broadcast-sized file is not passing through anybody’s API. Small files can go in one PUT. Anything large should be chunked, so a dropped connection costs one chunk rather than the whole upload.
PUT <sessionUri>
Content-Range: bytes 0-16777215/734003200 # 16 MiB, a multiple of 256 KiB
308 Resume Incomplete
Range: bytes=0-16777215 # what GCS ACTUALLY kept — resume from here
PUT <sessionUri>
Content-Range: bytes 16777216-33554431/734003200
...
200 OK # the last chunk answers 200 or 201On a 308, trust the Range header rather than your own byte count. It says how much was actually kept, which is not always how much you sent. Resuming from your own offset leaves a hole, and the upload then succeeds with a corrupt file — which is a far worse day than a failed upload.
Chunk sizes must be a multiple of 256 KiB, except the last one. 16 MiB is a good default.
Lost track after a crash? Ask, do not guess:
PUT <sessionUri>
Content-Range: bytes */734003200 # no body: "how much do you have?"
308 Resume Incomplete
Range: bytes=0-50331647 # continue from 50331648A session lasts a week. After that, ask for a new one — the old path is dead and nothing was created, so there is nothing to clean up.