diff --git a/README.md b/README.md index 881b47c..e88be3c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ BUDs or **Blossom Upgrade Documents** are short documents that outline an additi - [BUD-11: Nostr Authorization](./buds/11.md) - [BUD-12: Blob management endpoints](./buds/12.md) - [BUD-13: Path-based blob upload](./buds/13.md) +- [BUD-14: Path-based multi-part upload](./buds/14.md) ## Endpoints @@ -38,6 +39,7 @@ Blossom Servers expose a few endpoints for managing blobs - `GET /` (optional file `.ext`) [BUD-01](./buds/01.md#get-sha256---get-blob) - `HEAD /` (optional file `.ext`) [BUD-01](./buds/01.md#head-sha256---has-blob) - `PUT /` [BUD-13](./buds/13.md#put-sha256---upload-blob) +- `PATCH /` (optional file `.ext`) [BUD-14](./buds/14.md#patch-sha256ext---upload-blob-in-parts) - `HEAD /upload` [BUD-06](./buds/06.md#head-upload---upload-requirements-optional) - `GET /list/` [BUD-12](./buds/12.md#get-listpubkey---list-blobs-unrecommended) _(unrecommended)_ - `DELETE /` [BUD-12](./buds/12.md#delete-sha256---delete-blob) diff --git a/buds/01.md b/buds/01.md index 76fbe76..cbe7982 100644 --- a/buds/01.md +++ b/buds/01.md @@ -11,7 +11,7 @@ _All pubkeys MUST be in hex format_ Servers MUST set the `Access-Control-Allow-Origin: *` header on all responses to ensure compatibility with applications hosted on other domains. For [preflight](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests) (`OPTIONS`) requests, -servers MUST also set, at minimum, the `Access-Control-Allow-Headers: Authorization, *` and `Access-Control-Allow-Methods: GET, HEAD, PUT, DELETE` headers. +servers MUST also set, at minimum, the `Access-Control-Allow-Headers: Authorization, *` and `Access-Control-Allow-Methods: GET, HEAD, PUT, DELETE` headers. Servers that implement [BUD-14](./14.md) MUST also include `PATCH`. The header `Access-Control-Max-Age: 86400` MAY be set to cache the results of a preflight request for 24 hours. diff --git a/buds/11.md b/buds/11.md index ce97cca..442d884 100644 --- a/buds/11.md +++ b/buds/11.md @@ -70,17 +70,18 @@ To validate an authorization token, a server MUST perform the following checks: The table below defines, for each endpoint, the required `t` tag action, the implied blob hash (if any), and whether at least one matching `x` tag is required. -| Endpoint | Required `t` | Implied Blob Hash | `x` Tag Requirement | -| -------------------- | ------------ | ---------------------------- | ------------------- | -| `GET /` | `get` | `` from the URL | optional | -| `HEAD /` | `get` | `` from the URL | optional | -| `PUT /` | `upload` | `` from the URL | required | -| `HEAD /upload` | `upload` | `X-SHA-256` request header | required | -| `DELETE /` | `delete` | `` from the URL | required | -| `GET /list/` | `list` | — | not applicable | -| `PUT /mirror` | `upload` | SHA-256 of the mirrored blob | required | -| `PUT /media` | `media` | `X-SHA-256` request header | required | -| `HEAD /media` | `media` | `X-SHA-256` request header | required | +| Endpoint | Required `t` | Implied Blob Hash | `x` Tag Requirement | +| ------------------------ | ------------ | ---------------------------- | ------------------- | +| `GET /` | `get` | `` from the URL | optional | +| `HEAD /` | `get` | `` from the URL | optional | +| `PUT /` | `upload` | `` from the URL | required | +| `PATCH /[.ext]` | `upload` | `` from the URL | required | +| `HEAD /upload` | `upload` | `X-SHA-256` request header | required | +| `DELETE /` | `delete` | `` from the URL | required | +| `GET /list/` | `list` | — | not applicable | +| `PUT /mirror` | `upload` | SHA-256 of the mirrored blob | required | +| `PUT /media` | `media` | `X-SHA-256` request header | required | +| `HEAD /media` | `media` | `X-SHA-256` request header | required | ## Security Considerations diff --git a/buds/13.md b/buds/13.md index fb636df..efdee43 100644 --- a/buds/13.md +++ b/buds/13.md @@ -12,6 +12,8 @@ Servers that implement this document MAY continue to support the `PUT /upload` e Defines the `PUT /` endpoint. +Multi-part uploads for the same path-based blob identity are defined in [BUD-14](./14.md). + The [Blob Descriptor](./02.md#blob-descriptor) type is defined in [BUD-02](./02.md). ## PUT /sha256 - Upload Blob diff --git a/buds/14.md b/buds/14.md new file mode 100644 index 0000000..89f9230 --- /dev/null +++ b/buds/14.md @@ -0,0 +1,115 @@ +# BUD-14 + +## Path-based multi-part upload + +`draft` `optional` + +_All pubkeys MUST be in hex format_ + +This document extends [BUD-13](./13.md) by defining the `PATCH /[.ext]` endpoint for multi-part uploads. + +Servers that implement this document MUST also implement [BUD-13](./13.md). + +The [Blob Descriptor](./02.md#blob-descriptor) type is defined in [BUD-02](./02.md). + +## PATCH /sha256[.ext] - Upload Blob In Parts + +The `PATCH /` endpoint MUST accept binary chunk data in the request body. + +The endpoint MUST also accept an optional file extension in the URL, similar to [BUD-01](./01.md#get-sha256---get-blob). The optional file extension MUST NOT affect blob identity or hash validation. `PATCH /` and `PATCH /.ext` refer to the same blob. + +The `` path segment MUST be a lowercase hex-encoded sha256 hash of the final blob. + +The server MUST NOT make the blob available at `/` until it has received enough data to reconstruct the full blob and has verified that the computed sha256 of the reconstructed bytes matches the `` path segment. + +### Signaling support + +A server that supports multi-part upload SHOULD respond to an `OPTIONS /[.ext]` request with `204 No Content` and include `PATCH` in the `Allow` header. + +### Upload requirements + +Clients MAY use [`HEAD /upload` from BUD-06](./06.md#head-upload---upload-requirements-optional) before sending any chunks. + +This pre-flight request remains only an optimization. Clients MAY skip it entirely, and servers MUST handle `PATCH /[.ext]` correctly regardless of whether the client performed a `HEAD /upload` request first. + +### Chunking strategy + +The client MAY split the blob into as many chunks as needed and MAY include overlap in the chunks if needed. + +Each chunk is positioned within the final blob using the `Upload-Offset` and `Content-Length` headers. + +The server MUST reconstruct the final blob using the received chunks according to their offsets so that overlapping chunks are handled correctly. + +The server MUST support chunks arriving out of order and SHOULD support concurrent chunk uploads for the same blob. + +### Request headers + +Clients MUST send the following headers in each `PATCH /[.ext]` request: + +- `Upload-Type`: The MIME type of the final blob. This header follows the same format as `Content-Type` in [RFC-9110](https://httpwg.org/specs/rfc9110.html#field.content-type). +- `Upload-Length`: The total length of the final blob in bytes. +- `Upload-Offset`: The byte offset of this chunk within the final blob. +- `Content-Length`: The length of the chunk in bytes. +- `Content-Type`: MUST be `application/octet-stream`. + +Servers MUST validate that each chunk fits within `Upload-Length`. Servers MUST reject chunks whose declared range is invalid. + +### Completion + +If the chunk was accepted but the upload is not yet complete, the server MUST respond with `204 No Content`. + +Once the server has received enough chunks to cover the full `Upload-Length`, it MUST verify the sha256 hash of the reconstructed blob against the `` path segment. + +If the computed sha256 does not match the `` path segment, the server MUST reject the upload with `409 Conflict` and MUST NOT make the blob available at `/`. + +If the reconstructed blob is valid, the server MUST respond as defined by [BUD-13](./13.md#put-sha256---upload-blob): + +- `201 Created` if the blob was newly stored +- `200 OK` if the blob already exists + +The response body MUST contain a [Blob Descriptor](./02.md#blob-descriptor). + +### Authorization + +The server MAY require authorization for `PATCH /[.ext]` requests as defined in [BUD-11](./11.md). + +The authorization event MUST use the `upload` verb. + +The blob hash is implied by the `` path segment, using the same authorization semantics as `PUT /` from [BUD-13](./13.md). + +Servers MUST NOT require one `x` tag per chunk hash. + +### Partial upload timeout + +Servers MAY impose a timeout on in-progress uploads. If no `PATCH` request is received for a partial upload within the timeout window, the server MAY discard the partial upload and free associated resources. + +The RECOMMENDED timeout is 60 seconds of inactivity between chunks. + +### Resuming uploads + +Clients SHOULD keep track of which chunks have been uploaded so they can resume an interrupted upload. + +This document does not define a standardized offset-discovery endpoint. Clients SHOULD assume they are responsible for local upload state. + +### Status codes + +Servers SHOULD use the following status codes for `PATCH /[.ext]` responses: + +| Status Code | Meaning | +| ---------------------------- | ------- | +| `200 OK` | The blob already exists and the server is returning the existing [Blob Descriptor](./02.md#blob-descriptor). | +| `201 Created` | The blob was fully reconstructed, stored successfully, and the server is returning its [Blob Descriptor](./02.md#blob-descriptor). | +| `204 No Content` | The chunk was accepted but the upload is not yet complete. | +| `400 Bad Request` | The sha256 path, optional file extension, request headers, or chunk body are malformed. | +| `401 Unauthorized` | Authorization is required and missing or invalid. See [BUD-11](./11.md#endpoint-authorization-requirements). | +| `402 Payment Required` | Payment is required before the upload can proceed. See [BUD-07](./07.md). | +| `403 Forbidden` | The request is understood but not allowed by server policy. | +| `409 Conflict` | The reconstructed blob does not match the sha256 from the URL. | +| `411 Length Required` | A required `Content-Length` header is missing. | +| `413 Content Too Large` | The blob exceeds server size limits. | +| `415 Unsupported Media Type` | The blob type is not supported. | +| `416 Range Not Satisfiable` | The chunk offset or length is outside the declared upload length. | +| `429 Too Many Requests` | The client has exceeded a rate limit or quota. | +| `503 Service Unavailable` | The upload service is temporarily unavailable. | + +If included, `X-Reason` MUST be treated as a human readable diagnostic message only and clients MUST NOT parse it for control flow.