9 Commits

Author SHA1 Message Date
hzrd149
2e4c3a0349 remove requirement for root "x" tag 2025-11-25 08:53:51 -06:00
hzrd149
d05228aae1 Merge branch 'master' into chunked-blobs 2025-11-25 08:50:30 -06:00
hzrd149
1d855e1633 move chunks to tags 2024-12-03 11:52:29 -06:00
hzrd149
51c93670aa replace merkle trees with simple hash 2024-11-14 07:45:05 -06:00
hzrd149
e39afe2149 simplify by removing cbor and full merkle tree 2024-11-12 10:04:25 +00:00
hzrd149
4d82f98b3c separate tree encoding from storage 2024-11-11 12:00:47 +00:00
hzrd149
8fc2e3dc31 small fix 2024-11-11 11:57:08 +00:00
hzrd149
a23d784673 typo 2024-11-11 11:55:54 +00:00
hzrd149
33714b6c0c add BUT-10 draft 2024-11-11 11:53:27 +00:00
9 changed files with 165 additions and 122 deletions

View File

@@ -1,6 +1,6 @@
# 🌸 Blossom - Blobs stored simply on mediaservers
Blossom uses [nostr](https://github.com/nostr-protocol/nostr) public / private keys for users identities.
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?
@@ -27,7 +27,7 @@ BUDs or **Blossom Upgrade Documents** are short documents that outline an additi
- [BUD-08: Nostr File Metadata Tags](./buds/08.md)
- [BUD-09: Blob Report](./buds/09.md)
- [BUD-10: Blossom URI Schema](./buds/10.md)
- [BUD-11: Nostr Authorization](./buds/11.md)
- [BUD-12: Chunked blobs](./buds/12.md)
## Endpoints
@@ -36,27 +36,28 @@ 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 /upload` [BUD-02](./buds/02.md#put-upload---upload-blob)
- `Authentication`: Signed [nostr event](./buds/11.md) (see [BUD-02](./buds/02.md#upload-authorization))
- `Authentication`: Signed [nostr event](./buds/02.md#upload-authorization-required)
- Return a blob descriptor
- `HEAD /upload` [BUD-06](./buds/06.md#head-upload---upload-requirements)
- `GET /list/<pubkey>` [BUD-02](./buds/02.md#get-listpubkey---list-blobs-unrecommended) _(optional, unrecommended)_
- `GET /list/<pubkey>` [BUD-02](./buds/02.md#get-listpubkey---list-blobs)
- Returns an array of blob descriptors
- `Authentication` _(optional)_: Signed [nostr event](./buds/11.md) (see [BUD-02](./buds/02.md#list-authorization))
- `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/11.md) (see [BUD-02](./buds/02.md#delete-authorization))
- `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/11.md) (see [BUD-02](./buds/02.md#upload-authorization))
- `Authentication`: Signed [nostr event](./buds/02.md#upload-authorization-required)
- `HEAD /media` [BUD-05](./buds/05.md#head-media)
- `PUT /media` [BUD-05](./buds/05.md#put-media)
- `Authentication`: Signed [nostr event](./buds/11.md) (see [BUD-05](./buds/05.md#upload-authorization))
- `Authentication`: Signed [nostr event](./buds/05.md#upload-authorization)
- `PUT /report` [BUD-09](./buds/09.md)
## Event kinds
| kind | description | BUD |
| ------- | ------------------- | ------------------ |
| `24242` | Authorization token | [11](./buds/11.md) |
| `24242` | Authorization event | [01](./buds/01.md) |
| `10063` | User Server List | [03](./buds/03.md) |
| `2001` | Merkle tree | [12](./buds/12.md) |
## License

View File

@@ -20,6 +20,55 @@ The header `Access-Control-Max-Age: 86400` MAY be set to cache the results of a
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
@@ -54,14 +103,14 @@ include a file extension in the URL that reflects the blob type (e.g. `.bin`, `.
The server may optionally require authorization when retrieving blobs from the `GET /<sha256>` endpoint
In this case, the server MUST first perform the base validation checks defined in [BUD-11](./11.md#base-validation), then perform the following additional checks:
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 authorization token MUST contain either a `server` tag (limiting the token to specific servers) or MUST contain at least one `x` tag matching the sha256 hash of the blob being retrieved (scoping the token to specific blob hashes). see [BUD-11](./11.md#tag-scoping).
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 authorization token for retrieving a single blob:
Example event for retrieving a single blob:
```json
{
@@ -79,7 +128,7 @@ Example authorization token for retrieving a single blob:
}
```
Example authorization token for retrieving multiple blobs from single server:
Example event for retrieving multiple blobs from single server:
```json
{
@@ -91,7 +140,7 @@ Example authorization token for retrieving multiple blobs from single server:
"tags": [
["t", "get"],
["expiration", "1708857340"],
["server", "cdn.example.com"]
["server", "https://cdn.example.com/"]
],
"sig": "e402ade78e1714d40cd6bd3091bc5f4ada8e904e90301b5a2b9b5f0b6e95ce908d4f22b15e9fb86f8268a2131f8adbb3d1f0e7e7afd1ab0f4f08acb15822a999"
}

View File

@@ -53,12 +53,12 @@ servers may rely on the file extension to serve the blob correctly.
### Upload Authorization (Optional)
Servers MAY require an authorization token when uploading blobs. The server MUST first perform the base validation checks defined in [BUD-11](./11.md#base-validation), then perform the following additional checks:
Servers MAY accept an authorization event when uploading blobs and SHOULD perform additional checks
1. The `t` tag MUST be set to `upload`
2. The authorization token MUST contain at least one `x` tag matching the sha256 hash of the body of the request. The `x` tag scopes the token to specific blob hashes (see [BUD-11](./11.md#tag-scoping)).
2. The authorization event MUST contain at least one `x` tag matching the sha256 hash of the body of the request
Example authorization token:
Example Authorization event:
```json
{
@@ -76,9 +76,7 @@ Example authorization token:
}
```
## GET /list/pubkey - List Blobs (Unrecommended)
**Note:** The `/list` endpoint is optional and unrecommended. It is not necessary for all servers to implement the `/list` endpoint. Servers MAY implement this endpoint, but are not required to do so.
## GET /list/pubkey - List Blobs (Optional)
The `/list/<pubkey>` endpoint MUST return a JSON array of [Blob Descriptor](#blob-descriptor) that were uploaded by the specified pubkey
@@ -88,13 +86,15 @@ The endpoint MAY support `since` and `until` query parameters to filter the list
Servers MAY reject a list request 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
### List Authorization (optional)
The server MAY require a `list` authorization token when listing blobs uploaded by the pubkey. If a server requires authorization it MUST first perform the base validation checks defined in [BUD-11](./11.md#base-validation), then it MUST perform the following additional checks:
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 token:
Example Authorization event:
```json
{
@@ -117,16 +117,20 @@ Servers MUST accept `DELETE` requests to the `/<sha256>` endpoint
Servers MAY reject a delete request for any reason and SHOULD respond with the appropriate HTTP `4xx` status code and an error message explaining the reason for the rejection
### Delete Authorization
### Delete Authorization (required)
Servers MAY require a `delete` authorization token when deleting blobs. If a server requires authorization it MUST first perform the base validation checks defined in [BUD-11](./11.md#base-validation), then MUST perform the following additional checks:
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. MUST contain at least one `x` tag matching the sha256 hash of the blob being deleted.
2. The authorization event MUST contain at least one `x` tag matching the sha256 hash of the blob being deleted
**Multiple `x` tags in the authorization token MUST NOT be interpreted as the user requesting to delete multiple blobs.**
When multiple `x` tags are present on the authorization event the server MUST only delete the blob listed in the URL.
Example authorization token:
**Multiple `x` tags MUST NOT be interpreted as the user requesting a bulk delete.**
Example Authorization event:
```json
{

View File

@@ -19,6 +19,12 @@ Clients MUST pass the URL of the remote blob as a stringified JSON object in the
}
```
Clients MAY set the `Authorization` header to an upload authorization event defined in [BUD-02](./02.md#upload-authorization-optional). When using authorization, the event MUST be of type "upload".
The `/mirror` endpoint MUST download the blob from the specified URL and verify that there is at least one `x` tag in the authorization event matching the sha256 hash of the download blob
**Multiple `x` tags in the authorization event MUST NOT be interpreted as the user requesting to mirror multiple blobs.**
The endpoint MUST return a [Blob Descriptor](#blob-descriptor) and a `2xx` status code if the mirroring was successful
or a `4xx` status code and error message if it was not.
@@ -30,20 +36,11 @@ Servers MAY use the `Content-Length` header to determine the size of the blob.
Servers MAY reject a mirror request for any reason and MUST respond with the appropriate HTTP `4xx` status code and an error message explaining the reason for the rejection.
### Upload Authorization
Servers MAY require an `upload` authorization token when mirroring blobs. The server MUST first perform the base validation checks defined in [BUD-11](./11.md#base-validation), then MUST perform the following additional checks:
1. The `t` tag MUST be set to `upload`
2. The authorization token MUST contain at least one `x` tag matching the sha256 hash of the downloaded blob. The `x` tag scopes the token to specific blob hashes (see [BUD-11](./11.md#tag-scoping)).
**Multiple `x` tags in the authorization token MUST NOT be interpreted as the user requesting to mirror multiple blobs.**
## Example Flow
1. Client signs an `upload` authorization token and uploads blob to Server A
1. Client signs an `upload` authorization event and uploads blob to Server A
1. Server A returns a [Blob Descriptor](./02.md#blob-descriptor) with the `url`
1. Client sends the `url` to Server B `/mirror` using the original `upload` authorization token
1. Client sends the `url` to Server B `/mirror` using the original `upload` authorization event
1. Server B downloads the blob from Server A using the `url`
1. Server B verifies the downloaded blob hash matches the `x` tag in the authorization token
1. Server B verifies the downloaded blob hash matches the `x` tag in the authorization event
1. Server B returns a [Blob Descriptor](./02.md#blob-descriptor)

View File

@@ -18,10 +18,12 @@ Servers MAY reject media uploads for any reason and should respond with the appr
### Upload Authorization
Servers MAY require a `media` authorization token to identify the uploader. If a server requires a `media` authorization token it MUST first perform the base validation checks defined in [BUD-11](./11.md#base-validation), then MUST perform the following additional checks:
Servers MAY require a `media` [authorization event](./02.md#upload-authorization-required) to identify the uploader
If a server requires a `media` authorization event it MUST perform the following checks
1. The `t` tag MUST be set to `media`
2. The authorization token MUST contain at least one `x` tag matching the sha256 hash of the body of the request.
2. MUST contain at least one `x` tag matching the sha256 hash of the body of the request
## HEAD /media
@@ -43,4 +45,4 @@ Clients MAY let a user selected a "trusted processing" server for uploading imag
Once a server has been selected, the client uploads the original media to the `/media` endpoint of the trusted server and get the optimized blob back
Then the client can ask the user to sign another `upload` authorization token for the new optimized blob and call the `/mirror` endpoint on other servers to distribute the blob
Then the client can ask the user to sign another `upload` authorization event for the new optimized blob and call the `/mirror` endpoint on other servers to distribute the blob

View File

@@ -18,7 +18,7 @@ The `HEAD /upload` endpoint MUST use the `X-SHA-256`, `X-Content-Type` and `X-Co
### Upload Authorization
The `HEAD /upload` endpoint MAY accept an `upload` authorization token using the `Authorization` header similar to what is used in the [`PUT /upload`](./02.md#upload-authorization) endpoint
The `HEAD /upload` endpoint MAY accept an `upload` authorization event using the `Authorization` header similar to what is used in the [`PUT /upload`](./02.md#upload-authorization-required) endpoint
If the server requires authorization to upload it may respond with the `401` status code, or if authorization was provided and is invalid or not permitted it may respond with `403` status code

View File

@@ -204,4 +204,3 @@ blossom:b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf?xs=
```
blossom:a7b3c2d1e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1.png?xs=cdn.example.com&xs=media.nostr.build&as=781208004e09102d7da3b7345e64fd193cd1bc3fce8fdae6008d77f9cabcd036&as=b53185b9f27962ebdf76b8a9b0a84cd8b27f9f3d4abd59f715788a3bf9e7f75e&sz=2547831
```

View File

@@ -1,77 +0,0 @@
# BUD-11
## Nostr Authorization
`draft` `optional`
Defines the authorization token format used by blossom servers to identify users. Authorization tokens are optional and servers MAY require them for various endpoints.
## Authorization tokens
Authorization tokens are signed nostr events proving to a server that the user (`pubkey`) has permitted an application to take an action on their behalf.
The authorization token MUST be a nostr event of kind `24242` and have a `t` tag with a verb of `get`, `upload`, `list`, `delete`, or `media`
Authorization tokens MUST have the `content` set to a human readable string explaining intended use. For example `Upload Blob`, `Delete old blobs`, `List Images`, etc
All authorization tokens 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 token should be considered expired.
## Tag scoping
Authorization tokens MAY include `server` and `x` tags to scope the token to specific servers or blob hashes.
- **`server` tag**: Limits the token to specific servers by domain name. If no `server` tags are present, the token is valid for all servers. Multiple `server` tags may be present to allow the token to be used on multiple servers. The value MUST be a domain name only (e.g., `cdn.example.com`), not a full URL.
- **`x` tag**: Scopes the token to specific blob hashes (similar to how `server` scopes to servers). Multiple `x` tags may be present for endpoints that require a sha256 hash. When `x` tags are present, the token is only valid for operations on the specified blob hashes.
Example authorization token:
```jsonc
{
"id": "bb653c815da18c089f3124b41c4b5ec072a40b87ca0f50bbbc6ecde9aca442eb",
"pubkey": "b53185b9f27962ebdf76b8a9b0a84cd8b27f9f3d4abd59f715788a3bf9e7f75e",
"kind": 24242,
"content": "Upload bitcoin.pdf",
"created_at": 1708773959,
"tags": [
["t", "upload"],
// Authorization tokens MAY have multiple "x" tags.
["x", "b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553"],
["expiration", "1708858680"]
],
"sig": "d0d58c92afb3f4f1925120b99c39cffe77d93e82f488c5f8f482e8f97df75c5357175b5098c338661c37d1074b0a18ab5e75a9df08967bfb200930ec6a76562f"
}
```
## Base validation
Servers must perform the following checks in order to validate the authorization token
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. If `server` tags are present, the server MUST verify that its domain name is present in at least one `server` tag. If no `server` tags are present, the token is valid for all servers.
## HTTP Authorization header
Using the `Authorization` HTTP header, the authorization token (kind `24242`) MUST be base64 encoded and use the Authorization scheme Nostr
Example HTTP Authorization header:
```
Authorization: Nostr eyJpZCI6IjhlY2JkY2RkNTMyOTIwMDEwNTUyNGExNDI4NzkxMzg4MWIzOWQxNDA5ZDhiOTBjY2RiNGI0M2Y4ZjBmYzlkMGMiLCJwdWJrZXkiOiI5ZjBjYzE3MDIzYjJjZjUwOWUwZjFkMzA1NzkzZDIwZTdjNzIyNzY5MjhmZDliZjg1NTM2ODg3YWM1NzBhMjgwIiwiY3JlYXRlZF9hdCI6MTcwODc3MTIyNywia2luZCI6MjQyNDIsInRhZ3MiOltbInQiLCJnZXQiXSxbImV4cGlyYXRpb24iLCIxNzA4ODU3NTQwIl1dLCJjb250ZW50IjoiR2V0IEJsb2JzIiwic2lnIjoiMDJmMGQyYWIyM2IwNDQ0NjI4NGIwNzFhOTVjOThjNjE2YjVlOGM3NWFmMDY2N2Y5NmNlMmIzMWM1M2UwN2I0MjFmOGVmYWRhYzZkOTBiYTc1NTFlMzA4NWJhN2M0ZjU2NzRmZWJkMTVlYjQ4NTFjZTM5MGI4MzI4MjJiNDcwZDIifQ==
```
## Endpoint-specific requirements (example)
Individual endpoints may extend the base validation with additional requirements. The following are examples of how endpoints use authorization tokens:
- **`/upload`** ([BUD-02](./02.md#upload-authorization)): May requires at least one `x` tag matching the sha256 hash of the blob being uploaded
- **`DELETE /<sha256>`** ([BUD-02](./02.md#delete-authorization)): May requires at least one `x` tag matching the sha256 hash of the blob being deleted
- **`PUT /media`** ([BUD-05](./05.md#upload-authorization)): May requires at least one `x` tag matching the sha256 hash of the media being uploaded
- **`GET /<sha256>`** ([BUD-01](./01.md#get-authorization)): May require either a `server` tag or at least one `x` tag matching the sha256 hash of the blob being retrieved
- **`GET /list/<pubkey>`** ([BUD-02](./02.md#list-authorization)): May require a `server` tag limiting the token to specific servers
- **`PUT /mirror`** ([BUD-04](./04.md#put-mirror---mirror-blob)): May requires at least one `x` tag matching the sha256 hash of the blob being mirrored
See the respective BUD documents for the exact requirements for each endpoint. All endpoints that accept authorization tokens MUST perform the base validation checks defined above before performing any endpoint-specific checks.

68
buds/12.md Normal file
View File

@@ -0,0 +1,68 @@
# BUD-12
## Chunked blobs
`draft` `optional`
Breaking large blobs into smaller chunks for distribution
### Chunking
The client MAY break large blobs into any number or size of chunks. although its recommended to use the size `1Mb` or `4Mb` for small and large chunks
Clients MUST NOT pad the remaining chunk, If clients need privacy they should use random chunk sizes and optionally encrypt the large blob
### Publishing
Clients should publish a `2001` kind event after chunking the file in order to store the list of chunks
The events MUST contain an ordered list of `chunk` tags with the sha256 hashes of the chunks
The `content` field MUST be a human readable description of the chunked file
```json
{
"tags": [
[ "chunk", "7e668b56a58c7891e0cf263ea3f093b75eebade23d663a45aa9920f347b3d671"],
[ "chunk", "9b9c44a91396f19fd8700986eb0586dff2dcccf96c75bc2caefef302bcd78da1"],
[ "chunk", "7a281548f1223664b855b10b08e59e84389ccabeb742517f6cd75eda2724a798"],
[ "chunk", "fadeccee86b123088bbc452df10e8fbc99d4c2f22a70ef7a35605ec8e439c345"],
[ "chunk", "5d62398419e6d136771541f3d2215e0ce31b1be45e99dbc64b43a4b734b447ca"],
]
}
```
### Metadata
The `2001` event MAY include additional metadata tags to help other clients know the filename, mime type or servers to download from
Metadata tags:
- `name` Filename
- `mime` Mime type of file
- `size` Total size in bytes of the file
- `server` (multiple) Recommended servers to download chunks from
### Examples
Example `2001` event
```json
{
"pubkey": "5328e6c743a50271745e16476590ba7ea3dc591c65f3a5e2b03430814c1dabc0",
"created_at": 1731405194,
"kind": 2001,
"content": "Large zip archive of documents",
"tags": [
[ "chunk", "7e668b56a58c7891e0cf263ea3f093b75eebade23d663a45aa9920f347b3d671"],
[ "chunk", "9b9c44a91396f19fd8700986eb0586dff2dcccf96c75bc2caefef302bcd78da1"],
[ "chunk", "7a281548f1223664b855b10b08e59e84389ccabeb742517f6cd75eda2724a798"],
[ "chunk", "fadeccee86b123088bbc452df10e8fbc99d4c2f22a70ef7a35605ec8e439c345"],
[ "chunk", "5d62398419e6d136771541f3d2215e0ce31b1be45e99dbc64b43a4b734b447ca"],
[ "name", "example.mp4" ],
[ "mime", "video/mp4" ],
[ "size", "4823449" ],
[ "server", "https://cdn.example.com" ],
[ "server", "https://nostr.download" ]
]
}
```