diff --git a/docs/guide/architecture.md b/docs/guide/architecture.md index a810861..d4832a0 100644 --- a/docs/guide/architecture.md +++ b/docs/guide/architecture.md @@ -204,7 +204,7 @@ The key issuance endpoints require a valid `Authorization: Bearer ` header. | `POST` | `/fileupload/init` | Initialize a file upload. Returns a UUID and upload token. Optional `Authorization: Bearer PG-…` unlocks the higher upload-quota tier; Cryptify forwards the bearer to PKG's `/v2/api-key/validate` and uses the returned tenant id for rolling-window accounting. | | `PUT` | `/fileupload/{uuid}` | Upload a chunk. Uses `Content-Range` headers for offset tracking. Requires `cryptifytoken` header. Carries the same `Authorization` bearer on each chunk as `init`. | | `POST` | `/fileupload/finalize/{uuid}` | Finalize the upload after all chunks are sent. | -| `GET` | `/filedownload/{uuid}` | Download an encrypted file as a stream. | +| `GET` | `/filedownload/{uuid}` | Download an encrypted file as a stream. Sets `Accept-Ranges: bytes` and serves `206 Partial Content` for single-range `Range` requests so downloads can resume. See [Range support on `/filedownload`](/repos/cryptify#range-support-on-filedownload). | See [Upload limits](/repos/cryptify#upload-limits) and [Authentication for the higher tier](/repos/cryptify#authentication-for-the-higher-tier) on the Cryptify page for the tier breakdown and PKG-unreachable behaviour (503 vs. 413). diff --git a/docs/repos/cryptify.md b/docs/repos/cryptify.md index 025d917..0bc7cab 100644 --- a/docs/repos/cryptify.md +++ b/docs/repos/cryptify.md @@ -97,7 +97,7 @@ Cryptify exposes a file upload/download API. An OpenAPI 3.0 specification is ava - `PUT /fileupload/{uuid}`: Upload a file chunk (use `Content-Range` header for chunked uploads). - `POST /fileupload/finalize/{uuid}`: Finalize the upload (sends the recipient notification email if `notifyRecipients` was `true` on init). - `GET /fileupload/{uuid}/status`: Read rolling-token state to resume an in-flight upload across a page refresh or tab crash. Authenticated via `X-Recovery-Token`. -- `GET /filedownload/{uuid}`: Download a file. +- `GET /filedownload/{uuid}`: Download a file. Supports resumable downloads via the HTTP `Range` header (see [Range support on `/filedownload`](#range-support-on-filedownload) below). ### `POST /fileupload/init` request body @@ -173,6 +173,21 @@ The `prev_token` and `prev_offset` fields on `GET /fileupload/{uuid}/status` exi [Source: api-description.yaml#L109-L124](https://github.com/encryption4all/cryptify/blob/4c30e539a04be1dc08cc1704de35f1ad4320c5af/api-description.yaml#L109-L124) +### Range support on `/filedownload` + +`GET /filedownload/{uuid}` honours inbound `Range` headers so browsers and other clients can resume an interrupted download instead of restarting from byte zero. Every response sets `Accept-Ranges: bytes`. + +| Request | Response | +|---|---| +| No `Range` header | `200 OK` with `Content-Length` and the full body. | +| `Range: bytes=N-M`, `bytes=N-`, or `bytes=-N` | `206 Partial Content` with `Content-Range: bytes start-end/total` and the requested slice. Open-ended and suffix forms are clamped to the file size. | +| Unsatisfiable or malformed range | `416 Range Not Satisfiable` with `Content-Range: bytes */total`. | +| Multi-range (`bytes=0-9,20-29`) | Rejected as malformed; the server returns `416`. `multipart/byteranges` is intentionally unsupported. | + +The CORS preflight allowlist on the service includes `Range` alongside `Authorization`, `Content-Type`, `Content-Range`, `CryptifyToken`, and `X-Recovery-Token`, so browser `fetch` calls can send the header. + +[Source: src/main.rs#L959-L1083](https://github.com/encryption4all/cryptify/blob/b1eff690eae096d4cec55e7dbf323d8d0c3d74c0/src/main.rs#L959-L1083) + ## Development ### Docker (recommended)