diff --git a/Filesystem.md b/Filesystem.md new file mode 100644 index 0000000000..fa044527a3 --- /dev/null +++ b/Filesystem.md @@ -0,0 +1,109 @@ +NIP-FS +====== + +Private Encrypted File System +----------------------------- + +`draft` `optional` + +Defines a protocol for a private encrypted file drive using [Blossom](https://github.com/hzrd149/blossom) blob servers for file storage and Nostr relays for an encrypted file index. + +The file metadata event uses the Metadata event per NIP-Metadata with subtype as `files` + +--- + +## Kind 34578 — File Metadata Subtype + +One event per file, encrypted with the encryption key stored in the user metadata event. + +**Tags:** + +| tag | value | +|-----|-------| +| `d` | SHA-256 hash of the encrypted file blob | +| `t` | `"files"` — marks this as a file metadata record | +| `encrypted` | `nip44` | +| `client` | client identifier (e.g. `formstr-drive`) | + +**Content:** `nip44.v2.encrypt(json, driveConversationKey)` where plaintext is: + +```json +{ + "name": "", + "hash": "", + "size": , + "type": "", + "folder": "", + "uploadedAt": , + "server": "", + "encryptionKey": "", + "deleted": , + "previewHash": "", + "chunks": List<{"hash": "", "server": ""}> +} +``` + +--- + +## File Encryption + +Each **chunk** of a file is encrypted with a per-file ephemeral keypair using AES-GCM with NIP-44 v2 HKDF key derivation: + +1. Generate a random keypair `(sk, pk)` for the file +2. `conversationKey = getConversationKey(sk, pk)` +3. Generate random `nonce` (32 bytes) +4. `HKDF-SHA256(conversationKey, salt=nonce, info="nip44-v2")` → 44 bytes. +5. AES-GCM encrypt `base64(fileBytes)`: `key = derived[0:32]`, `iv = derived[32:44]` +6. Blob format (base64): `0x02 || nonce (32 bytes) || ciphertext` +7. Store `hex(sk)` as `encryptionKey` in the file metadata content + +--- + +## Directives + +- File Metadata events MUST be encrypted with `nip44.v2.encrypt` using the encryption key directly (not via the identity signer). +- The `d` tag of a File Metadata event MUST equal the SHA-256 hash of the **encrypted** chunks as returned by the Blossom server. +- File Metadata events MUST carry `["t", "files"]` to distinguish them from other metadata events. +- To rename or move a file to another folder: publish a new File Metadata event with the same `d` tag. +- Delete: Delete all the chunks from the blossom server, the preview(if applicable) and the file metadata event +- Clients MUST skip events whose content they cannot decrypt. +- Virtual folders are derived from the `folder` field; clients MUST NOT publish separate folder events. +- When multiple events share the same `d` tag, clients MUST use the one with the highest `created_at`. + +--- + +## Examples + +File Metadata event: + +```jsonc +{ + "kind": 34578, + "pubkey": "abc123...", + "created_at": 1700000001, + "tags": [ + ["d", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"], + ["t", "files"], + ["encrypted", "nip44"], + ["client", "formstr-drive"] + ], + "content": "", + "sig": "..." +} +``` + +Decrypted File Metadata content: + +```json +{ + "name": "report.pdf", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "size": 204800, + "type": "application/pdf", + "folder": "/work/docs", + "uploadedAt": 1700000001000, + "server": "https://blossom.primal.net", + "encryptionKey": "cafebabe5678...", + "chunks":[{"hash":"289y3899f23"}, {"hash": "2983ur92u9"}] +} +``` diff --git a/Metadata.md b/Metadata.md new file mode 100644 index 0000000000..a332818a3a --- /dev/null +++ b/Metadata.md @@ -0,0 +1,39 @@ +NIP-Metadata +====== + +Metadata events +----------------------------- + +`draft` `optional` + +Defines a protocol for metadata events. Metadata events hold metadata information about other events, or entities + +--- +## Kind 34578 — Metadata Event + +Metadata events use **kind 34578** (addressable). They are distinguished by their `d` tag and the presence of the `["t", ""]` tag. The content of a metadata event always has a json which is encrypted to the author's pubkey. The shape of the json depends on the sub type of the metadata event + +| tag | value | +|-----|-------| +| `d` | depends on the sub-type of the metadata event | +| `t` | `""` — sub-type of the event | + +--- + + +## Examples + +User metadata event: + +```jsonc +{ + "kind": 34578, + "pubkey": "abc123...", + "created_at": 1700000000, + "tags": [ + ["d", "0:abc123..."], + ], + "content": "", + "sig": "..." +} +``` \ No newline at end of file diff --git a/User-Metadata.md b/User-Metadata.md new file mode 100644 index 0000000000..e4088850d9 --- /dev/null +++ b/User-Metadata.md @@ -0,0 +1,62 @@ +NIP-UM +====== + +User-Metadata +----------------------------- + +`draft` `optional` + +Defines a protocol for user metadata events per NIP-Metadata. User Metadata events hold metadata information about the user such as a decoupled encryption key + +--- + +## Kind 34578 — User Metadata Event + +Stores the secret metadata information about the user inside the content field. It is identified by the `d` tag + +**Tags:** + +| tag | value | +|-----|-------| +| `d` | `"0:"` | + +**Content:** NIP-44 encrypted to the author's own pubkey (via identity signer). Plaintext is a JSON string. Currently, the proposal is that the data contains a `encryptionKey` attribute which will have the key used for decrypting/encrypting messages, files and other entities owned by the user. + +```json +{ + "encryptionKey": "" +} +``` + +The **conversation key** used for all file metadata is: + +``` +conversationKey = getConversationKey(driveSecretKey, getPublicKey(driveSecretKey)) +``` + +## Directives + +- The user metadata event must have a d tag `"0:"`. +- Clients MUST encrypt and decrypt the user metadata event using the identity signer (`nip44Decrypt(authorPubkey, content)/nip44Encrypt(authorPubkey, content)`). +- Clients MAY generate and publish a new metadata event warning the user that previous events may be lost. +- When multiple events share the same `d` tag, clients MUST use the one with the highest `created_at`. Clients MAY inform the user that they found multiple metadata events + +--- + +## Examples + +User metadata event: + +```jsonc +{ + "kind": 34578, + "pubkey": "abc123...", + "created_at": 1700000000, + "tags": [ + ["d", "0:abc123..."], + ], + "content": "", + "sig": "..." +} +``` +