Add BUD-14 path-based multipart upload

Define PATCH /<sha256>[.ext] as a BUD-13 extension so multipart uploads use the path hash as the upload identity and no longer require X-SHA-256.
This commit is contained in:
hzrd149
2026-04-11 17:36:24 -05:00
parent 2259b7399c
commit a0bdb98b28
5 changed files with 132 additions and 12 deletions
+2
View File
@@ -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 /<sha256>` (optional file `.ext`) [BUD-01](./buds/01.md#get-sha256---get-blob)
- `HEAD /<sha256>` (optional file `.ext`) [BUD-01](./buds/01.md#head-sha256---has-blob)
- `PUT /<sha256>` [BUD-13](./buds/13.md#put-sha256---upload-blob)
- `PATCH /<sha256>` (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/<pubkey>` [BUD-12](./buds/12.md#get-listpubkey---list-blobs-unrecommended) _(unrecommended)_
- `DELETE /<sha256>` [BUD-12](./buds/12.md#delete-sha256---delete-blob)
+1 -1
View File
@@ -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.
+12 -11
View File
@@ -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 /<sha256>` | `get` | `<sha256>` from the URL | optional |
| `HEAD /<sha256>` | `get` | `<sha256>` from the URL | optional |
| `PUT /<sha256>` | `upload` | `<sha256>` from the URL | required |
| `HEAD /upload` | `upload` | `X-SHA-256` request header | required |
| `DELETE /<sha256>` | `delete` | `<sha256>` from the URL | required |
| `GET /list/<pubkey>` | `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 /<sha256>` | `get` | `<sha256>` from the URL | optional |
| `HEAD /<sha256>` | `get` | `<sha256>` from the URL | optional |
| `PUT /<sha256>` | `upload` | `<sha256>` from the URL | required |
| `PATCH /<sha256>[.ext]` | `upload` | `<sha256>` from the URL | required |
| `HEAD /upload` | `upload` | `X-SHA-256` request header | required |
| `DELETE /<sha256>` | `delete` | `<sha256>` from the URL | required |
| `GET /list/<pubkey>` | `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
+2
View File
@@ -12,6 +12,8 @@ Servers that implement this document MAY continue to support the `PUT /upload` e
Defines the `PUT /<sha256>` 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
+115
View File
@@ -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 /<sha256>[.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 /<sha256>` 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 /<sha256>` and `PATCH /<sha256>.ext` refer to the same blob.
The `<sha256>` path segment MUST be a lowercase hex-encoded sha256 hash of the final blob.
The server MUST NOT make the blob available at `/<sha256>` until it has received enough data to reconstruct the full blob and has verified that the computed sha256 of the reconstructed bytes matches the `<sha256>` path segment.
### Signaling support
A server that supports multi-part upload SHOULD respond to an `OPTIONS /<sha256>[.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 /<sha256>[.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 /<sha256>[.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 `<sha256>` path segment.
If the computed sha256 does not match the `<sha256>` path segment, the server MUST reject the upload with `409 Conflict` and MUST NOT make the blob available at `/<sha256>`.
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 /<sha256>[.ext]` requests as defined in [BUD-11](./11.md).
The authorization event MUST use the `upload` verb.
The blob hash is implied by the `<sha256>` path segment, using the same authorization semantics as `PUT /<sha256>` 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 /<sha256>[.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.