Skip to content
GigadriveDocs

Uploading objects

Create a resumable upload session, send the bytes over tus, and let Gigadrive Network verify and finalize the object.

Every upload through the Gigadrive Network API is a session: you declare the key, the exact byte length and the SHA-256 up front, then send the bytes to a signed URL that speaks tus 1.0.0. The SDK does all of it in one call, and the protocol underneath is documented here for clients that drive it directly.

Uploading a file

The following example runs inside a deployed Function, where the client needs no configuration because the platform injects credentials. See OIDC federation for what it reads and how to get the same credentials outside a deployment.

import { GigadriveClient } from '@gigadrive/sdk';

const client = new GigadriveClient();

const { object, url } = await client.storage.upload({
  bucket: 'user-uploads',
  key: 'avatars/user-123.png',
  path: './avatar.png',
  waitForCompletion: true,
});

console.log(url, object?.contentLength);

Over REST it is two calls: create the session, then PATCH the bytes to the signed upload.url the response hands back. One PATCH covers a small file; a large one is sent as a series of them. Creating a session needs the network:storage_objects:write scope, and the object appears in the bucket as soon as the last chunk lands, without a second call from you.

What a session declares

FieldRequiredNotes
keyYes1 to 1024 characters
contentLengthYesExact size in bytes, and the hard cap for the upload
checksumSha256YesLowercase hex, 64 characters
contentTypeNoInferred from the key when omitted
checksumSha1, checksumMd5NoStored on the object, never verified

The SHA-256 is mandatory because storage is content addressed: it is both the integrity check and the identity the bytes are stored under. Send the wrong one and the upload is rejected at the end rather than silently stored. SHA-1 and MD5 are carried through to the object record for clients that expect them, and nothing compares them against the bytes.

Session lifetime and states

A session and the upload URL issued with it expire 15 minutes after creation. session.expiresAt is the expiry of both, because the signed token inside the URL carries the same instant.

StateMeaning
pendingTransient, while backend staging is set up. Create returns ready.
readyThe URL accepts bytes
completedVerified and finalized, the object exists
failedThe upload was terminated, or the backend rejected it
expiredThe window closed before the last chunk landed

Past expiresAt the token is refused with a 401 and the bytes already sent are lost. A session that was terminated or failed answers 410 instead. Neither can be revived: create a new session and upload again. GET /applications/{applicationId}/storage/buckets/{bucketRef}/uploads/{sessionId} reads the current state, which is what client.storage.upload({ waitForCompletion: true }) polls.

The upload endpoint

The URL points at /files/<sessionId> on the API host and carries its own credential as a query parameter:

https://api.gigadrive.network/files/0197b2f6-7082-712d-9f7e-777777777777?upload_token=eyJ2ZXJzaW9uIjox...

That token authorizes exactly one key, one length and one digest, which is what makes the URL safe to hand to a browser. It can also travel as Authorization: Bearer <token> if you would rather keep it out of the URL. The session id in the path has to match the one inside the token.

MethodWhat it does
HEADReturns Upload-Offset and Upload-Length. A GET is answered the same way.
PATCHAppends bytes at Upload-Offset, and answers 204 with the new offset.
DELETETerminates the upload and marks the session failed. A finished upload gets a 400.
OPTIONSAdvertises the protocol version, the extensions, and Tus-Max-Size.

Every request except OPTIONS carries Tus-Resumable: 1.0.0, and one that does not is answered with a 412. A PATCH also needs Upload-Offset and Content-Type: application/offset+octet-stream.

Supported tus extensions

Tus-Extension advertises creation, creation-with-upload, creation-defer-length and termination. The creation extensions are of no use to a client here: the upload resource exists before you receive its URL, and there is no collection endpoint to post to, since every request has to name a session that its token also names. To terminate the upload, delete its URL.

The checksum extension is not implemented. Upload-Checksum is accepted and ignored, and integrity comes from the SHA-256 declared on the session instead. The expiration extension is not implemented either, so no response carries Upload-Expires; read expiresAt off the session. Concatenation is not supported.

StatusCause
400A tus header failed validation, or the token names a different session
401The upload token is missing or past its expiry
403A PATCH without Upload-Offset, or without a content type
404No session with that id
409Upload-Offset disagrees with the offset the server holds
410The session was terminated or has failed
412Tus-Resumable is missing
413The bytes would exceed the declared contentLength

Chunking and resuming

The server offset advances only when a whole chunk has been stored, so an interrupted transfer costs you the chunk in flight and nothing before it. Ask for the offset, then continue from there:

curl -I "$UPLOAD_URL" -H 'Tus-Resumable: 1.0.0'
# HTTP/1.1 200 OK
# Upload-Offset: 5242880
# Upload-Length: 18874368

curl -X PATCH "$UPLOAD_URL" \
  -H 'Tus-Resumable: 1.0.0' \
  -H 'Upload-Offset: 5242880' \
  -H 'Content-Type: application/offset+octet-stream' \
  --data-binary @rest-of-file.bin

In-memory data goes up as a single chunk unless you set chunkSize, which means an interrupted transfer restarts from zero. The SDK retries a failed chunk after 0, 1, 3 and 5 seconds, and resume: true stores a fingerprint so a later call for the same bytes and the same URL picks up where it stopped.

Finalization

The last PATCH does not answer 204 until the object exists. Before creating it, Gigadrive Network checks what was stored: the length has to match what you declared, a declared content type that contradicts the stored one is rejected, and the SHA-256 is verified, by re-reading the whole object when the storage layer does not report a digest it trusts. A mismatch fails the session and no object appears.

Uploading to a key that already holds an object replaces it. There is no object versioning, so the previous bytes are gone once nothing else references them. Stored bytes accrue storage_bytes_hours from that point; sending the bytes costs nothing, because only delivery out of File Storage is metered as transfer.

Object keys

A key is the full path inside the bucket. Listing derives folders from the key with S3-style prefix and delimiter semantics, so avatars/user-123.png shows up under avatars/ without any directory ever being created.

RuleValue
LengthUp to 1024 characters
Leading slashesStripped before storage
Path traversalA .. segment is rejected
Control charactersRejected

Other ways in

A browser can send bytes to a bucket without them passing through your own server, which Client-side uploads walks through end to end. Classic S3 multipart is available through the S3-compatible API rather than the REST API: parts other than the last must be at least 5 MiB, the highest part number is 10,000, and an unfinished multipart upload stays resumable for 7 days.