This NIP defines a REST API for HTTP file storage servers intended to be used in conjunction with the nostr network.
The API will enable nostr users to upload files and later reference them by url on nostr notes.
The spec DOES NOT use regular nostr events through websockets for
storing, requesting nor retrieving data because, for simplicity, the server
will not have to learn anything about nostr relays.
## Server Adaptation
File storage servers wishing to be accessible by nostr users should opt-in by making available an https route at `/.well-known/nostr/nip96.json` with `api_url`:
```js
{
// Required
// File upload and deletion are served from this url
// Also downloads if "download_url" field is absent or empty string
When indicated, `clients` must add an [NIP-98](98.md) `Authorization` header (**optionally** with the encoded `payload` tag set to the base64-encoded 256-bit SHA-256 hash of the file - not the hash of the whole request body).
-`expiration`: UNIX timestamp in seconds. Empty string if file should be stored forever. The server isn't required to honor this.
-`size`: File byte size. This is just a value the server can use to reject early if the file size exceeds the server limits.
-`alt`: **RECOMMENDED** strict description text for visibility-impaired users.
-`media_type`: "avatar" or "banner". Informs the server if the file will be used as an avatar or banner. If absent, the server will interpret it as a normal upload, without special treatment.
Others custom form data fields may be used depending on specific `server` support.
The `server` isn't required to store any metadata sent by `clients`.
The `filename` embedded in the file may not be honored by the `server`, which could internally store just the SHA-256 hash value as the file name, ignoring extra metadata.
`no_transform` can be used to replicate a file to multiple servers for redundancy, clients can use the [server list](#selecting-a-server) to find alternative servers which might contain the same file. When uploading a file and requesting `no_transform` clients should check that the hash matches in the response in order to detect if the file was modified.
// Optional. Recommended for helping clients to easily know file type before downloading it.
["m", "image/png"]
// Optional. Recommended for helping clients to reserve an adequate UI space to show the file before downloading it.
["dim", "800x600"]
// ... other optional NIP-94 tags
],
content: ""
},
// ... other custom fields (please consider adding them to this NIP or to NIP-94 tags)
}
```
Note that if the server didn't apply any transformation to the received file, both `nip94_event.tags.*.ox` and `nip94_event.tags.*.x` fields will have the same value. The server MUST link the saved file to the SHA-256 hash of the **original** file before any server transformations (the `nip94_event.tags.*.ox` tag value). The **original** file's SHA-256 hash will be used to identify the saved file when downloading or deleting it.
After successful upload, the `client` may optionally generate and send to any set of nostr `relays` a [NIP-94](94.md) event by including the missing fields.
Sometimes the server may want to place the uploaded file in a processing queue for deferred file processing.
In that case, the server MUST serve the original file while the processing isn't done, then swap the original file for the processed one when the processing is over. The upload response is the same as usual but some optional metadata like `nip94_event.tags.*.x` and `nip94_event.tags.*.size` won't be available.
The expected resulting metadata that is known in advance should be returned on the response.
For example, if the file processing would change a file from "jpg" to "webp",
use ".webp" extension on the `nip94_event.tags.*.url` field value and set "image/webp" to the `nip94_event.tags.*.m` field.
If some metadata are unknown before processing ends, omit them from the response.
The upload response MAY include a `processing_url` field informing a temporary url that may be used by clients to check if
the file processing is done.
If the processing isn't done, the server should reply at the `processing_url` url with **200 OK** and the following JSON:
```
{
// It should be "processing". If "error" it would mean the processing failed.
status: "processing",
message: "Processing. Please check again later for updated status.",
percentage: 15 // Processing percentage. An integer between 0 and 100.
}
```
When the processing is over, the server replies at the `processing_url` url with **201 Created** status and a regular successful JSON response already mentioned before (now **without** a `processing_url` field), possibly including optional metadata at `nip94_event.tags.*` fields
that weren't available before processing.
### File compression
File compression and other transformations like metadata stripping can be applied by the server.
However, for all file actions, such as download and deletion, the **original** file SHA-256 hash is what identifies the file in the url string.
Note that the "\<sha256-hash\>" part is from the **original** file, **not** from the **transformed** file if the uploaded file went through any server transformation.
Note that the `/<sha256-hash>` part is from the **original** file, **not** from the **transformed** file if the uploaded file went through any server transformation.
It should be noted that more than one user may have uploaded the same file (with the same hash). In this case, a delete must not really delete the file but just remove the user's `pubkey` from the file owners list (considering the server keeps just one copy of the same file, because multiple uploads of the same file results
`servers` may require payment for file storage. In that case, the server SHOULD serve the normal response with a **402 Payment Required** status code and a the `payment_request` field filled with a LN invoice.
Until the payment is done, the server SHOULD display a QR code in the url tag of the NIP-94 event response. When the payment is done, the server MUST swap the QR code for the uploaded file.