diff --git a/Client.md b/Client.md new file mode 100644 index 0000000..73834cf --- /dev/null +++ b/Client.md @@ -0,0 +1,3 @@ +# Blossom Client Implementation + +Example implementation (Typescript) [blossom-client](https://github.com/hzrd149/blossom-client) diff --git a/Nostr.md b/Nostr.md index 3fb2513..2c2e0fb 100644 --- a/Nostr.md +++ b/Nostr.md @@ -4,20 +4,21 @@ Blossom uses nostr for public / private key identities. Users are expected to si ## User Server Discovery -Users can publish a kind `10063` event with a list of `r` tags indicating where other users should look to find their published blobs +Users may publish a kind `10063` event with a list of `r` tags indicating where others should look to find their published blobs ### Example ```json { - "kind": 10063, + "id": "90718dd2f481ad1d9dd72eab2b210d1b3d03231f114b0825bf967465748934f0", + "pubkey": "7d917f22b84356a3c4e5ef7ec6d4464fb1dc3258cbf58c58d8bf079580c12c91", "content": "", + "kind": 10063, + "created_at": 1708774162, "tags": [ ["r", "https://cdn.self.hosted"], ["r", "https://cdn.satellite.earth"] ], - "created_at": 1708454797, - "id": "...", - "sig": "..." + "sig": "805a0c00cdad7ae25de70740751b8e5985bec24bb6aead8c65e0cc33d6205dd5a06689b566e62589885ad86bfb55c5c7dfb5a9ce6ddb29cf04507fa76e485040" } ``` diff --git a/README.md b/README.md index 74dd68e..3f30020 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,28 @@ -# 🌸 Blossom +# 🌸 Blossom WIP Blobs stored simply on mediaservers ## What is it? -Blossom is a spec of http endpoints for storing blobs on publicly accessible servers +Blossom is a spec for a set of http endpoints that allows users to store blobs of data on publicly accessible servers -## How dose it work? +## What are blobs Blobs are packs of binary data addressed by their sha256 hash +## How dose it work? + Blossom Servers expose four endpoints for managing blobs - `GET /` (optional file `.ext`) - `PUT /upload` -- `GET /list` + - `Authentication`: Signed nostr event + - Return a blob descriptor +- `GET /list/` + - Returns an array of blob descriptors + - `Authentication` _(optional)_: Signed nostr event - `DELETE /` + - `Authentication`: Signed nostr event ## Blob Descriptor @@ -31,7 +38,9 @@ Servers may include additional fields in the descriptor like `magnet`, `infohash ## Nostr Identities -Blossom uses nostr public / private keys for identities. Users are expected to sign "Client Authentication" events to prove their identity when uploading or deleting blobs +Blossom uses nostr public / private keys for identities. Users are expected to sign authorization events to prove their identity when interacting with servers + +See [Nostr](./Nostr.md) ## Server Implementation @@ -39,4 +48,4 @@ See [Server](./Server.md) ## Client Implementation -Example Implementation: [blossom-client](https://github.com/hzrd149/blossom-client) (TypeScript) +See [Client](./Client.md) diff --git a/Server.md b/Server.md index 4b434bc..fd8b386 100644 --- a/Server.md +++ b/Server.md @@ -1,11 +1,180 @@ # Blossom Server Implementation +_All pubkeys must be in hex format_ + +## CORS + +Servers must set `Access-Control-Allow-Origin: *` and `Access-Control-Allow-Methods: GET, PUT, DELETE` headers on all endpoint to ensure compatibility with apps hosted on other domains + +## Authorization events + +Authorization events are used to identify the users pubkey with the server + +Authorization events must 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 have the `content` set to a human readable string explaining to the user what the events inteded use is. for example `Upload Blob`, `Delete family-picture.png`, `List Images`, etc + +Events must be kind `24242` and have a `t` tag with a verb of `get`, `upload`, `list`, or `delete` + +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": "8ecbdcdd5329200105524a14287913881b39d1409d8b90ccdb4b43f8f0fc9d0c", + "pubkey": "9f0cc17023b2cf509e0f1d305793d20e7c72276928fd9bf85536887ac570a280", + "kind": 24242, + "content": "Get Blobs", + "created_at": 1708771227, + "tags": [ + ["t", "get"], + ["expiration", "1708857540"] + ], + "sig": "30c531600ea076b5afb3cce634cc30072af051aa1d2fd3e22f4b4d3a3300f8ce00a8e7668c45c79d946f3561a9ca66755458823222e2f7b24887f8318a814718" +} +``` + +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== +``` + ## Endpoints -### GET / - Get Blob +### GET /sha256 - Get Blob -### GET /list +The `GET /` endpoint should return the contents of the blob with the `Content-Type` header set to the approperate MIME type -### PUT /upload +The endpoint must accept an optional file extension in the URL. ie. `.pdf`, `.png`, etc -### DELETE / +#### Get Authorization (optional) + +The server may optionally require authorization when fetching blobs from the `GET /` 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 approperate 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" +} +``` + +### GET /list/pubkey - List Blobs + +The `/list/` endpoint must return a JSON array of "blob descriptors" that where uploaded by the specified pubkey + +The endpoint should also support a `since` and `until` query parameter to filter the returned "blob descriptors" by the `created` field + +#### 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" +} +``` + +### PUT /upload - Upload Blob + +The `PUT /upload` endpoint .... + +Servers may reject an upload for any reason and must respond with the approperate HTTP `4xx` status code and the body as a human readable reason for the rejection + +#### Upload Authorization (required) + +Servers must accept an authorization event when uploading blobs + +Servers must perform additional checks on the authorization event + +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 + +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" +} +``` + +### DELETE /sha256 - Delete Blob + +Servers must accept `DELETE` requests to the `/` endpoint + +Servers may reject a delete request for any reason and must respond with the aproperate HTTP `4xx` status code and a human readable reason for the rejection + +#### Delete Authorization (required) + +Servers must accept an authorization event when deleting blobs + +Servers must 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" +} +```