# BUD-01 ## Server requirements and blob retrieval `draft` `mandatory` _All pubkeys MUST be in hex format_ ## Cross origin headers 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. The header `Access-Control-Max-Age: 86400` MAY be set to cache the results of a preflight request for 24 hours. ## Error responses Every time a server sends an error response (HTTP status codes >=400), it may include a human-readable header `X-Reason` that can be displayed to the user. ## Authorization events Authorization events are used to identify the users to the server Authorization events must be generic and must NOT be scoped to specific servers. This allows pubkeys to sign a single event and interact the same way with multiple servers. Events MUST be kind `24242` and have a `t` tag with a verb of `get`, `upload`, `list`, or `delete` Events MUST have the `content` set to a human readable string explaining to the user what the events intended use is. For example `Upload Blob`, `Delete dog-picture.png`, `List Images`, etc All events MUST have a [NIP-40](https://github.com/nostr-protocol/nips/blob/master/40.md) `expiration` tag set to a unix timestamp at which the event should be considered expired. Authorization events MAY have multiple `x` tags for endpoints that require a sha256 hash. Example event: ```jsonc { "id": "bb653c815da18c089f3124b41c4b5ec072a40b87ca0f50bbbc6ecde9aca442eb", "pubkey": "b53185b9f27962ebdf76b8a9b0a84cd8b27f9f3d4abd59f715788a3bf9e7f75e", "kind": 24242, "content": "Upload bitcoin.pdf", "created_at": 1708773959, "tags": [ ["t", "upload"], // Authorization events MAY have multiple "x" tags. ["x", "b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553"], ["expiration", "1708858680"] ], "sig": "d0d58c92afb3f4f1925120b99c39cffe77d93e82f488c5f8f482e8f97df75c5357175b5098c338661c37d1074b0a18ab5e75a9df08967bfb200930ec6a76562f" } ``` Servers must perform the following checks in order to validate the event 1. The `kind` must be `24242` 2. `created_at` must be in the past 3. The `expiration` tag must be set to a Unix timestamp in the future 4. The `t` tag must have a verb matching the intended action of the endpoint 5. Additional checks for specific endpoints. `/upload`, `/delete`, etc Using the `Authorization` HTTP header, the kind `24242` event MUST be base64 encoded and use the Authorization scheme Nostr Example HTTP Authorization header: ``` Authorization: Nostr eyJpZCI6IjhlY2JkY2RkNTMyOTIwMDEwNTUyNGExNDI4NzkxMzg4MWIzOWQxNDA5ZDhiOTBjY2RiNGI0M2Y4ZjBmYzlkMGMiLCJwdWJrZXkiOiI5ZjBjYzE3MDIzYjJjZjUwOWUwZjFkMzA1NzkzZDIwZTdjNzIyNzY5MjhmZDliZjg1NTM2ODg3YWM1NzBhMjgwIiwiY3JlYXRlZF9hdCI6MTcwODc3MTIyNywia2luZCI6MjQyNDIsInRhZ3MiOltbInQiLCJnZXQiXSxbImV4cGlyYXRpb24iLCIxNzA4ODU3NTQwIl1dLCJjb250ZW50IjoiR2V0IEJsb2JzIiwic2lnIjoiMDJmMGQyYWIyM2IwNDQ0NjI4NGIwNzFhOTVjOThjNjE2YjVlOGM3NWFmMDY2N2Y5NmNlMmIzMWM1M2UwN2I0MjFmOGVmYWRhYzZkOTBiYTc1NTFlMzA4NWJhN2M0ZjU2NzRmZWJkMTVlYjQ4NTFjZTM5MGI4MzI4MjJiNDcwZDIifQ== ``` ## Endpoints All endpoints MUST be served from the root of the domain (eg. the `/upload` endpoint MUST be accessible from `https://cdn.example.com/upload`, etc). This allows clients to talk to servers interchangeably when uploading or retrieving blobs ## GET /sha256 - Get Blob The `GET /` endpoint MUST return the contents of the blob in the response body. the `Content-Type` header SHOULD beset to the appropriate MIME-type The endpoint MUST accept an optional file extension in the URL. ie. `.pdf`, `.png`, etc Regardless of the file extension, the server MUST return the MIME type of the blob in the `Content-Type` header. If the server does not know the MIME type of the blob, it MUST default to `application/octet-stream` ### Proxying and Redirection (Optional) If the endpoint returns a redirection 3xx status code such as 307 or 308 ([RFC 9110 section 15.4](https://datatracker.ietf.org/doc/html/rfc9110#name-redirection-3xx)), it MUST redirect to a URL containing the same sha256 hash as the requested blob. This ensures that if a user copies or reuses the redirect URL, it will contain the original sha256 hash. While the final blob may not be served from a Blossom server (e.g. CDN, IPFS, object storage, etc.), the destination server MUST set the `Access-Control-Allow-Origin: *` header on the response to allow cross-origin requests, as well as the `Content-Type` and `Content-Length` headers to ensure the blob can be correctly displayed by clients. Two ways to guarantee this are: 1. Proxying the blob through the Blossom server, allowing it to override headers such as `Content-Type`. 2. Manipulating the redirect URL to include a file extension that matches the blob type, such as `.pdf`, `.png`, etc. If the server is unable to determine the MIME type of the blob, it MUST default to `application/octet-stream` and MAY include a file extension in the URL that reflects the blob type (e.g. `.bin`, `.dat`, etc.). ### Get Authorization (optional) The server may optionally require authorization when retrieving blobs from the `GET /` endpoint In this case, the server MUST perform additional checks on the authorization event 1. A `t` tag MUST be present and set to `get` 2. The event MUST contain either a `server` tag containing the full URL to the server or MUST contain at least one `x` tag matching the sha256 hash of the blob being retrieved If the client did not send an `Authorization` header the server must respond with the appropriate HTTP status code `401` (Unauthorized) Example event for retrieving a single blob: ```json { "id": "06d4842b9d7f8bf72440471704de4efa9ef8f0348e366d097405573994f66294", "pubkey": "ec0d11351457798907a3900fe465bfdc3b081be6efeb3d68c4d67774c0bc1f9a", "kind": 24242, "content": "Get bitcoin.pdf", "created_at": 1708771927, "tags": [ ["t", "get"], ["expiration", "1708857340"], ["x", "b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553"] ], "sig": "22ecb5116ba143e4c3d6dc4b53d549aed6970ec455f6d25d145e0ad1fd7c0e26c465b2e92d5fdf699c7050fa43e6a41f087ef167208d4f06425f61548168fd7f" } ``` Example event for retrieving multiple blobs from single server: ```json { "id": "d9484f18533d5e36f000f902a45b15a7eecf5fbfcb046789756d57ea87115dc5", "pubkey": "b5f07faa8d3529f03bd898a23dfb3257bab8d8f5490777c46076ff9647e205dc", "kind": 24242, "content": "Get blobs from example.com", "created_at": 1708771927, "tags": [ ["t", "get"], ["expiration", "1708857340"], ["server", "https://cdn.example.com/"] ], "sig": "e402ade78e1714d40cd6bd3091bc5f4ada8e904e90301b5a2b9b5f0b6e95ce908d4f22b15e9fb86f8268a2131f8adbb3d1f0e7e7afd1ab0f4f08acb15822a999" } ``` ## HEAD /sha256 - Has Blob The `HEAD /` endpoint SHOULD be identical to the `GET /` endpoint except that it MUST NOT return the blob in the reponse body per [RFC 7231](https://www.rfc-editor.org/rfc/rfc7231#section-4.3.2) The endpoint MUST respond with the same `Content-Type` and `Content-Length` headers as the `GET /` endpoint. The endpoint MUST accept an optional file extension in the URL similar to the `GET /` endpoint. ie. `.pdf`, `.png`, etc ## Range requests To better support mobile devices, video files, or low bandwidth connections. servers should support range requests ([RFC 7233 section 3](https://www.rfc-editor.org/rfc/rfc7233#section-3)) on the `GET /` endpoint and signal support using the `accept-ranges: bytes` and `content-length` headers on the `HEAD /` endpoint See [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests) for more details