mirror of https://github.com/hzrd149/blossom.git
commit
6d14d113f1
22
README.md
22
README.md
|
@ -1,5 +1,7 @@
|
|||
# 🌸 Blossom - Blobs stored simply on mediaservers
|
||||
|
||||
Blossom uses [nostr](https://github.com/nostr-protocol/nostr) public / private keys for identities. Users are expected to sign authorization events to prove their identity when interacting with servers
|
||||
|
||||
## What is it?
|
||||
|
||||
Blossom is a spec for a set of HTTP endpoints that allow users to store blobs of data on publicly accessible servers
|
||||
|
@ -12,22 +14,24 @@ Blobs are packs of binary data addressed by their sha256 hash
|
|||
|
||||
Blossom Servers expose four endpoints for managing blobs
|
||||
|
||||
- `GET /<sha256>` (optional file `.ext`)
|
||||
- `HEAD /<sha256>` (optional file `.ext`)
|
||||
- `PUT /upload`
|
||||
- `Authentication`: Signed [nostr event](./buds/bud-01.md#upload-authorization-required)
|
||||
- `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 /upload` [BUD-2](./buds/02.md#put-upload---upload-blob)
|
||||
- `Authentication`: Signed [nostr event](./buds/02.md#upload-authorization-required)
|
||||
- Return a blob descriptor
|
||||
- `GET /list/<pubkey>`
|
||||
- `GET /list/<pubkey>` [BUD-02](./buds/02.md#get-listpubkey---list-blobs)
|
||||
- Returns an array of blob descriptors
|
||||
- `Authentication` _(optional)_: Signed [nostr event](./buds/bud-01.md#list-authorization-optional)
|
||||
- `DELETE /<sha256>`
|
||||
- `Authentication`: Signed [nostr event](./buds/bud-01.md#delete-authorization-required)
|
||||
- `Authentication` _(optional)_: Signed [nostr event](./buds/02.md#list-authorization-optional)
|
||||
- `DELETE /<sha256>` [BUD-02](./buds/02.md#delete-sha256---delete-blob)
|
||||
- `Authentication`: Signed [nostr event](./buds/02.md#delete-authorization-required)
|
||||
- `PUT /mirror` [BUD-04](./buds/04.md#put-mirror---mirror-blob)
|
||||
- `Authentication`: Signed [nostr event](./buds/02.md#upload-authorization-required)
|
||||
|
||||
## Protocol specification (BUDs)
|
||||
|
||||
BUDs stand for **Blossom Upgrade Documents**.
|
||||
|
||||
See the [BUDs](./buds) folder and specifically [BUD-01](./buds/bud-01.md) for a detailed explanation of the endpoints
|
||||
See the [BUDs](./buds) folder and specifically [BUD-01](./buds/01.md) and [BUD-02](./buds/02.md) for a detailed explanation of the endpoints
|
||||
|
||||
## BUDs
|
||||
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
BUD-01
|
||||
======
|
||||
|
||||
Server requirements and blob reterival
|
||||
--------------------------------------
|
||||
|
||||
`draft` `mandatory`
|
||||
|
||||
_All pubkeys MUST be in hex format_
|
||||
|
||||
## Cross origin headers
|
||||
|
||||
Servers MUST set the `Access-Control-Allow-Origin: *`, `Access-Control-Allow-Headers: Authorization,*` and `Access-Control-Allow-Methods: GET, PUT, DELETE` headers on all endpoints to ensure compatibility with apps hosted on other domains
|
||||
|
||||
## 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 inteded 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.
|
||||
|
||||
Example event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "bb653c815da18c089f3124b41c4b5ec072a40b87ca0f50bbbc6ecde9aca442eb",
|
||||
"pubkey": "b53185b9f27962ebdf76b8a9b0a84cd8b27f9f3d4abd59f715788a3bf9e7f75e",
|
||||
"kind": 24242,
|
||||
"content": "Upload bitcoin.pdf",
|
||||
"created_at": 1708773959,
|
||||
"tags": [
|
||||
["t", "upload"],
|
||||
["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 path (eg. `https://cdn.example.com/upload`, etc). This allows clients to talk to servers interchangeably when uploading or reteriving blobs
|
||||
|
||||
## Error Responses
|
||||
|
||||
For HTTP `4xx` and `5xx` status codes servers MUST repond with `Content-Type: application/json` and a JSON object containing `message`
|
||||
|
||||
The `message` field MUST be human readable and should explain the reason for the error. Optionally servers may include other fields for the client with more information about the error
|
||||
|
||||
Example Error response:
|
||||
```
|
||||
HTTP/2 401
|
||||
content-type: application/json; charset=utf-8
|
||||
content-length: 32
|
||||
access-control-allow-origin: *
|
||||
access-control-expose-headers: *
|
||||
access-control-allow-headers: authorization,*
|
||||
access-control-allow-methods: get, put, delete
|
||||
|
||||
{"message":"Missing Auth event"}
|
||||
```
|
||||
|
||||
## GET /sha256 - Get Blob
|
||||
|
||||
The `GET /<sha256>` endpoint MUST return the contents of the blob with the `Content-Type` header set to the appropriate MIME type
|
||||
|
||||
The endpoint MUST accept an optional file extension in the URL. ie. `.pdf`, `.png`, etc
|
||||
|
||||
If the endpoints returns a 301 or 302 redirect it MUST redirect to a URL containing the same sha256 hash as requested blob.
|
||||
This ensures that if a user was to copy or reuse the redirect URL it would still contain the original sha256 hash
|
||||
|
||||
### Get Authorization (optional)
|
||||
|
||||
The server may optionally require authorization when reteriving blobs from the `GET /<sha256>` 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 an `x` tag with the sha256 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 retreiving 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 retreiving 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 /<sha256>` endpoint MUST respond with either a `200` or `404` status code
|
||||
|
||||
The endpoint MUST accept an optional file extension in the URL similar to the `GET /<sha256>` endpoint. ie. `.pdf`, `.png`, etc
|
122
buds/02.md
122
buds/02.md
|
@ -1,40 +1,124 @@
|
|||
BUD-02
|
||||
======
|
||||
|
||||
User Server List
|
||||
-------------------------
|
||||
Blob upload and management
|
||||
--------------------------
|
||||
|
||||
`draft` `optional`
|
||||
|
||||
Defines a replaceable event using `kind:10063` to advertise the blossom servers a user uses to host their blobs.
|
||||
_All pubkeys MUST be in hex format_
|
||||
|
||||
The event MUST include at least one `server` tag containing the full server URL including the `http://` or `https://`.
|
||||
Defines the `/upload`, `/list` and `DELETE /<sha256>` enpoints
|
||||
|
||||
The order of these tags is important and should be arranged with the users most "reliable" or "trusted" servers coming first.
|
||||
## Blob Descriptor
|
||||
|
||||
The `.content` is not used.
|
||||
A blob descriptor is a JSON object containing `url`, `sha256`, `size`, `type`, and `uploaded` fields
|
||||
|
||||
- `url` A public facing url this blob can retrieved from
|
||||
- `sha256` The sha256 hash of the blob
|
||||
- `size` The size of the blob in bytes
|
||||
- `type` (optional) The MIME type of the blob
|
||||
- `uploaded` The unix timestamp of when the blob was uploaded to the server
|
||||
|
||||
Servers may include additional fields in the descriptor like `magnet`, `infohash`, or `ipfs` depending on other protocols they support
|
||||
|
||||
## PUT /upload - Upload Blob
|
||||
|
||||
The `PUT /upload` endpoint MUST accept binary data in the body of the request and MAY use the `Content-Type` and `Content-Length` headers to get the MIME type and size of the data
|
||||
|
||||
The endpoint MUST NOT modify the blob in any way and should return the exact same sha256 that was uploaded. This is critical to allow users to re-upload their blobs to new servers
|
||||
|
||||
The endpoint MUST return a [Blob Descriptor](#blob-descriptor) if the upload was successful or an error object if it was not
|
||||
|
||||
Servers MAY reject an upload for any reason and should respond with the appropriate HTTP `4xx` status code and an error message explaining the reason for the rejection
|
||||
|
||||
### Upload Authorization (required)
|
||||
|
||||
Servers MUST accept an authorization event when uploading blobs and should perform additional checks
|
||||
|
||||
1. The `t` tag MUST be set to `upload`
|
||||
2. The `x` tag MUST be present and set to the sha256 hash of the blob
|
||||
|
||||
Example Authorization event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "e4bee088334cb5d38cff1616e964369c37b6081be997962ab289d6c671975d71",
|
||||
"pubkey": "781208004e09102d7da3b7345e64fd193cd1bc3fce8fdae6008d77f9cabcd036",
|
||||
"content": "",
|
||||
"kind": 10063,
|
||||
"created_at": 1708774162,
|
||||
"id": "bb653c815da18c089f3124b41c4b5ec072a40b87ca0f50bbbc6ecde9aca442eb",
|
||||
"pubkey": "b53185b9f27962ebdf76b8a9b0a84cd8b27f9f3d4abd59f715788a3bf9e7f75e",
|
||||
"kind": 24242,
|
||||
"content": "Upload bitcoin.pdf",
|
||||
"created_at": 1708773959,
|
||||
"tags": [
|
||||
["server", "https://cdn.self.hosted"],
|
||||
["server", "https://cdn.satellite.earth"]
|
||||
["t", "upload"],
|
||||
["x", "b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553"],
|
||||
["expiration", "1708858680"]
|
||||
],
|
||||
"sig": "cc5efa74f59e80622c77cacf4dd62076bcb7581b45e9acff471e7963a1f4d8b3406adab5ee1ac9673487480e57d20e523428e60ffcc7e7a904ac882cfccfc653"
|
||||
"sig": "d0d58c92afb3f4f1925120b99c39cffe77d93e82f488c5f8f482e8f97df75c5357175b5098c338661c37d1074b0a18ab5e75a9df08967bfb200930ec6a76562f"
|
||||
}
|
||||
```
|
||||
|
||||
## Client Integration
|
||||
## GET /list/pubkey - List Blobs
|
||||
|
||||
When uploading blobs clients should attempt to upload the blob to all or at least the top 3 server on the list event.
|
||||
The `/list/<pubkey>` endpoint MUST return a JSON array of [Blob Descriptor](#blob-descriptor) that where uploaded by the specified pubkey
|
||||
|
||||
This ensures that the blob is available in multiple locations in the case one of the servers goes offline.
|
||||
The endpoint MUST support a `since` and `until` query parameter to limit the returned blobs by thier `uploaded` date
|
||||
|
||||
## Server Integration
|
||||
Servers may reject a list for any reason and MUST respond with the appropriate HTTP `4xx` status code and an error message explaining the reason for the rejection
|
||||
|
||||
Servers may use this event kind to discover other servers that a user is uploading to in order to download specific blobs from.
|
||||
### List Authorization (optional)
|
||||
|
||||
The server may optionally require Authorization when listing blobs uploaded by the pubkey
|
||||
|
||||
In this case the server must perform additional checks on the authorization event
|
||||
|
||||
1. The `t` tag must be set to `list`
|
||||
|
||||
Example Authorization event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "cbb1cab9566355bfdf04e1f1fc1e655fe903ecc193e8a750092ee53beec2a0e8",
|
||||
"pubkey": "a5fc3654296e6de3cda6ba3e8eba7224fac8b150fd035d66b4c3c1dc2888b8fc",
|
||||
"kind": 24242,
|
||||
"content": "List Blobs",
|
||||
"created_at": 1708772350,
|
||||
"tags": [
|
||||
["t", "list"],
|
||||
["expiration", "1708858680"]
|
||||
],
|
||||
"sig": "ff9c716f8de0f633738036472be553ce4b58dc71d423a0ef403f95f64ef28582ef82129b41d4d0ef64d2338eb4aeeb66dbc03f8b3a3ed405054ea8ecb14fa36c"
|
||||
}
|
||||
```
|
||||
|
||||
## DELETE /sha256 - Delete Blob
|
||||
|
||||
Servers MUST accept `DELETE` requests to the `/<sha256>` endpoint
|
||||
|
||||
Servers may reject a delete request for any reason and should respond with the aproperate HTTP `4xx` status code and an error message explaining the reason for the rejection
|
||||
|
||||
### Delete Authorization (required)
|
||||
|
||||
Servers MUST accept an authorization event when deleting blobs
|
||||
|
||||
Servers should perform additional checks on the authorization event
|
||||
|
||||
1. The `t` tag must be set to `delete`
|
||||
2. A `x` tag must be present and set to the sha256 hash of the blob being deleted
|
||||
|
||||
Example Authorization event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "a92868bd8ea740706d931f5d205308eaa0e6698e5f8026a990e78ee34ce47fe8",
|
||||
"pubkey": "ae0063dd2c81ec469f2291ac029a19f39268bfc40aea7ab4136d7a858c3a06de",
|
||||
"kind": 24242,
|
||||
"content": "Delete bitcoin.pdf",
|
||||
"created_at": 1708774469,
|
||||
"tags": [
|
||||
["t", "delete"],
|
||||
["x", "b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553 "],
|
||||
["expiration", "1708858680"]
|
||||
],
|
||||
"sig": "2ba9af680505583e3eb289a1624a08661a2f6fa2e5566a5ee0036333d517f965e0ffba7f5f7a57c2de37e00a2e85fd7999076468e52bdbcfad8abb76b37a94b0"
|
||||
}
|
||||
```
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
BUD-02
|
||||
======
|
||||
|
||||
User Server List
|
||||
-------------------------
|
||||
|
||||
`draft` `optional`
|
||||
|
||||
Defines a replaceable event using `kind:10063` to advertise the blossom servers a user uses to host their blobs.
|
||||
|
||||
The event MUST include at least one `server` tag containing the full server URL including the `http://` or `https://`.
|
||||
|
||||
The order of these tags is important and should be arranged with the users most "reliable" or "trusted" servers coming first.
|
||||
|
||||
The `.content` is not used.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "e4bee088334cb5d38cff1616e964369c37b6081be997962ab289d6c671975d71",
|
||||
"pubkey": "781208004e09102d7da3b7345e64fd193cd1bc3fce8fdae6008d77f9cabcd036",
|
||||
"content": "",
|
||||
"kind": 10063,
|
||||
"created_at": 1708774162,
|
||||
"tags": [
|
||||
["server", "https://cdn.self.hosted"],
|
||||
["server", "https://cdn.satellite.earth"]
|
||||
],
|
||||
"sig": "cc5efa74f59e80622c77cacf4dd62076bcb7581b45e9acff471e7963a1f4d8b3406adab5ee1ac9673487480e57d20e523428e60ffcc7e7a904ac882cfccfc653"
|
||||
}
|
||||
```
|
||||
|
||||
## Client Integration
|
||||
|
||||
When uploading blobs clients should attempt to upload the blob to all or at least the top 3 server on the list event.
|
||||
|
||||
This ensures that the blob is available in multiple locations in the case one of the servers goes offline.
|
||||
|
||||
## Server Integration
|
||||
|
||||
Servers may use this event kind to discover other servers that a user is uploading to in order to download specific blobs from.
|
|
@ -0,0 +1,39 @@
|
|||
BUD-04
|
||||
======
|
||||
|
||||
Mirroring blobs
|
||||
---------------
|
||||
|
||||
`draft` `optional`
|
||||
|
||||
Defines the `/mirror` endpoint
|
||||
|
||||
## PUT /mirror - Mirror Blob
|
||||
|
||||
A server may expose a `PUT /mirror` endpoint to allow users to copy a blob from a URL instead of uploading it
|
||||
|
||||
Clients MUST pass the URL of the remote blob as a stringified JSON object in the request body
|
||||
|
||||
```json
|
||||
// request body
|
||||
{ "url": "https://cdn.satellite.earth/b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf" }
|
||||
```
|
||||
|
||||
Clients MUST set the `Authorization` header to an upload authorization event defined in [BUD-02](./02.md#upload-authorization-required)
|
||||
|
||||
The `/mirror` endpoint MUST download the blob from the specified URL and verify the sha256 hash matches the `x` tag in the upload authorization event
|
||||
|
||||
The endpoint MUST return a [Blob Descriptor](#blob-descriptor) if the mirroring was successful or an error object if it was not
|
||||
|
||||
Servers should re-use the `Content-Type` header returned from the URL to discover the mime type of the blob. if none is returned it may use the file extension in the URL
|
||||
|
||||
Servers MAY reject a mirror request for any reason and should respond with the appropriate HTTP `4xx` status code and an error message explaining the reason for the rejection
|
||||
|
||||
## Example Flow
|
||||
|
||||
1. Client signs authorization event and uploads blob to Server A
|
||||
1. Server A returns blob descriptor with `url`
|
||||
1. Client sends the `url` to Server B `/mirror` using the original authorization event
|
||||
1. Server B downloads blob from Server A using the url
|
||||
1. Server B verifies downloaded blob hash matches `x` tag in authorization event
|
||||
1. Server B returns [Blob Descriptor](./02.md#blob-descriptor)
|
236
buds/bud-01.md
236
buds/bud-01.md
|
@ -1,236 +0,0 @@
|
|||
# BUD-01
|
||||
|
||||
## Core endpoint outline
|
||||
|
||||
`draft` `mandatory`
|
||||
|
||||
Blossom uses [nostr](https://github.com/nostr-protocol/nostr) public / private keys for identities. Users are expected to sign authorization events to prove their identity when interacting with servers
|
||||
|
||||
_All pubkeys MUST be in hex format_
|
||||
|
||||
## Cross origin headers
|
||||
|
||||
Servers MUST set the `Access-Control-Allow-Origin: *`, `Access-Control-Allow-Headers: Authorization,*` and `Access-Control-Allow-Methods: GET, PUT, DELETE` headers on all endpoints to ensure compatibility with apps hosted on other domains
|
||||
|
||||
## 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 inteded 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.
|
||||
|
||||
Example event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "a2d97d0c8b19d6d91b8bd3c36feeb69f176861f9443ba575cbabf9941d4200bf",
|
||||
"pubkey": "2db760eae90b5764f3503e0c5660a1a74be9ded5eb8b493e81f65c28a088e9fe",
|
||||
"kind": 24242,
|
||||
"content": "Upload bitcoin.pdf",
|
||||
"created_at": 1708773959,
|
||||
"tags": [
|
||||
["t", "upload"],
|
||||
["size", "184292"],
|
||||
["expiration", "1708858680"]
|
||||
],
|
||||
"sig": "1442c68d5a661d821e9a4b91999b433a1d11557eeb6255496c6875c00d02497deb03dcb54597f210582cd62b621df21b080a0eadbd66ae703264b5929b160d05"
|
||||
}
|
||||
```
|
||||
|
||||
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 timespamp 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==
|
||||
```
|
||||
|
||||
## Blob Descriptor
|
||||
|
||||
A blob descriptor is a JSON object containing `url`, `sha256`, `size`, `type`, and `uploaded` fields
|
||||
|
||||
- `url` A public facing url this blob can retrieved from
|
||||
- `sha256` The sha256 hash of the blob
|
||||
- `size` The size of the blob in bytes
|
||||
- `type` (optional) The MIME type of the blob
|
||||
- `uploaded` The unix timestamp of when the blob was uploaded to the server
|
||||
|
||||
Servers may include additional fields in the descriptor like `magnet`, `infohash`, or `ipfs` depending on other protocols they support
|
||||
|
||||
## Endpoints
|
||||
|
||||
All endpoints MUST be served from the root path (eg. `https://cdn.example.com/upload`, etc). This allows clients to talk to servers interchangeably when uploading or fetching blobs
|
||||
|
||||
Servers MUST repond with `Content-Type: application/json` and a JSON object containing `message` for all error responses
|
||||
|
||||
The `message` field MUST be human readable and should explain the reason for the error. Optionally servers may include other fields for the client with more information about the error.
|
||||
|
||||
### Example Error response
|
||||
|
||||
```
|
||||
HTTP/2 401
|
||||
content-type: application/json; charset=utf-8
|
||||
content-length: 32
|
||||
access-control-allow-origin: *
|
||||
access-control-expose-headers: *
|
||||
access-control-allow-headers: authorization,*
|
||||
access-control-allow-methods: get, put, delete
|
||||
|
||||
{"message":"Missing Auth event"}
|
||||
```
|
||||
|
||||
### GET /sha256 - Get Blob
|
||||
|
||||
The `GET /<sha256>` endpoint MUST return the contents of the blob with the `Content-Type` header set to the appropriate MIME type
|
||||
|
||||
The endpoint MUST accept an optional file extension in the URL. ie. `.pdf`, `.png`, etc
|
||||
|
||||
If the endpoints returns a 301 or 302 redirect it MUST redirect to a URL containing the same sha256 hash as requested blob.
|
||||
This ensures that if a user was to copy or reuse the redirect URL it would still contain the original sha256 hash
|
||||
|
||||
#### Get Authorization (optional)
|
||||
|
||||
The server may optionally require authorization when fetching blobs from the `GET /<sha256>` endpoint
|
||||
|
||||
In this case the server MUST perform additional checks on the authorization event
|
||||
|
||||
1. The `t` tag must be set to `get`
|
||||
|
||||
If the client did not send an `Authorization` header the server must respond with the appropriate HTTP status code `401` (Unauthorized)
|
||||
|
||||
Example Authorization event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "3a2c0a58f88f86ab81ce7d111df57096e8cd9f41a75731a021e06e07c6df9d0e",
|
||||
"pubkey": "96ddb0e7c4a5786a842094fee014d4c6cbb1f1627a8d75ef6fb601baeb6c5054",
|
||||
"kind": 24242,
|
||||
"content": "Get Blobs",
|
||||
"created_at": 1708771927,
|
||||
"tags": [
|
||||
["t", "get"],
|
||||
["expiration", "1708857340"]
|
||||
],
|
||||
"sig": "2f279b2ac0a5d5f7551f5612b69a111e038ab6b31233a78bfc98f63bd5e38ae8cb5929cf7427f0b7b2dd5eff29e769df23d93926326b0d059dc475701a41d6d3"
|
||||
}
|
||||
```
|
||||
|
||||
### HEAD /sha256 - Has Blob
|
||||
|
||||
The `HEAD /<sha256>` endpoint MUST respond with either a `200` or `404` status code
|
||||
|
||||
The endpoint MUST accept an optional file extension in the URL similar to the `GET /<sha256>` endpoint. ie. `.pdf`, `.png`, etc
|
||||
|
||||
### PUT /upload - Upload Blob
|
||||
|
||||
The `PUT /upload` endpoint MUST accept binary data in the body of the request and MAY use the `Content-Type` header to get the MIME type of the data
|
||||
|
||||
The endpoint MUST NOT modify the blob in any way and should return the exact same sha256 that was uploaded. This is critical to allow users to re-upload their blobs to new servers
|
||||
|
||||
The endpoint MUST return a [Blob Descriptor](./README.md#blob-descriptor) if the upload was successful or an error object if not
|
||||
|
||||
Servers MAY reject an upload for any reason and should respond with the appropriate HTTP `4xx` status code and an error message explaining the reason for the rejection
|
||||
|
||||
#### Upload Authorization (required)
|
||||
|
||||
Servers MUST accept an authorization event when uploading blobs and should perform additional checks
|
||||
|
||||
1. The `t` tag MUST be set to `upload`
|
||||
2. A `size` tag MUST be present and set to the total size of the uploaded blob in bytes
|
||||
|
||||
Example Authorization event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "65c72db0c3b82ffcb395589d01f3e2849c28753e9e7156ceb88e5dd937ca845f",
|
||||
"pubkey": "6ea2ab6f206844b1fe48bd8a7eb22ed6e4114a5b2a5252700a729a88142b2bc3",
|
||||
"kind": 24242,
|
||||
"content": "Upload bitcoin.pdf",
|
||||
"created_at": 1708773959,
|
||||
"tags": [
|
||||
["t", "upload"],
|
||||
["size", "184292"],
|
||||
["expiration", "1708858680"]
|
||||
],
|
||||
"sig": "df099ecaeadb7ebcd7ec8247eb57eb6720d39f64a024be3ef1ed9b5d51087b0e866bd08fd317d5167f9bdb9cdae4e593539b86678c4d922db17d0463e0f9e0e3"
|
||||
}
|
||||
```
|
||||
|
||||
### GET /list/pubkey - List Blobs
|
||||
|
||||
The `/list/<pubkey>` endpoint MUST return a JSON array of [Blob Descriptor](#blob-descriptor) that where uploaded by the specified pubkey
|
||||
|
||||
The endpoint MUST support a `since` and `until` query parameter to limit the returned blobs by thier `uploaded` date
|
||||
|
||||
Servers may reject a list for any reason and MUST respond with the appropriate HTTP `4xx` status code and an error message explaining the reason for the rejection
|
||||
|
||||
#### List Authorization (optional)
|
||||
|
||||
The server may optionally require Authorization when listing blobs uploaded by the pubkey
|
||||
|
||||
In this case the server must perform additional checks on the authorization event
|
||||
|
||||
1. The `t` tag must be set to `list`
|
||||
|
||||
Example Authorization event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "cbb1cab9566355bfdf04e1f1fc1e655fe903ecc193e8a750092ee53beec2a0e8",
|
||||
"pubkey": "a5fc3654296e6de3cda6ba3e8eba7224fac8b150fd035d66b4c3c1dc2888b8fc",
|
||||
"kind": 24242,
|
||||
"content": "List Blobs",
|
||||
"created_at": 1708772350,
|
||||
"tags": [
|
||||
["t", "list"],
|
||||
["expiration", "1708858680"]
|
||||
],
|
||||
"sig": "ff9c716f8de0f633738036472be553ce4b58dc71d423a0ef403f95f64ef28582ef82129b41d4d0ef64d2338eb4aeeb66dbc03f8b3a3ed405054ea8ecb14fa36c"
|
||||
}
|
||||
```
|
||||
|
||||
### DELETE /sha256 - Delete Blob
|
||||
|
||||
Servers MUST accept `DELETE` requests to the `/<sha256>` endpoint
|
||||
|
||||
Servers may reject a delete request for any reason and should respond with the aproperate HTTP `4xx` status code and an error message explaining the reason for the rejection
|
||||
|
||||
#### Delete Authorization (required)
|
||||
|
||||
Servers MUST accept an authorization event when deleting blobs
|
||||
|
||||
Servers should perform additional checks on the authorization event
|
||||
|
||||
1. The `t` tag must be set to `delete`
|
||||
2. A `x` tag must be present and set to the sha256 hash of the blob being deleted
|
||||
|
||||
Example Authorization event:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "a92868bd8ea740706d931f5d205308eaa0e6698e5f8026a990e78ee34ce47fe8",
|
||||
"pubkey": "ae0063dd2c81ec469f2291ac029a19f39268bfc40aea7ab4136d7a858c3a06de",
|
||||
"kind": 24242,
|
||||
"content": "Delete bitcoin.pdf",
|
||||
"created_at": 1708774469,
|
||||
"tags": [
|
||||
["t", "delete"],
|
||||
["x", "b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553 "],
|
||||
["expiration", "1708858680"]
|
||||
],
|
||||
"sig": "2ba9af680505583e3eb289a1624a08661a2f6fa2e5566a5ee0036333d517f965e0ffba7f5f7a57c2de37e00a2e85fd7999076468e52bdbcfad8abb76b37a94b0"
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue