# HTTP Messages A specification for sending/receiving HTTP messages (request/response) via a remote server. Header / Body etc are encrypted, either in the `content` (small messages) or via a blossom server (larger requests).  ## Overview Enables a local client to make and receive http requests (PUT, POST, GET, PATCH etc) from a remote computer. Requires: * A trusted machine to process the messages (can be a home PC or Raspberry Pi) * A relay (can be untrusted) * A blossom server (can be untrusted) ## Architecture ```mermaid architecture-beta group user(internet)[Nostr Client] group cloud(cloud)[Untrusted Servers] group home(server)[Trusted Device] service client(internet) in user service blossom(database)[Blossom Storage] in cloud service relay(logos:aws-ec2)[Relays] in cloud service pc(logos:aws-elb) in home service bbi[Big Bad Internet] client:R -- L:blossom client:B -- L:relay pc:L -- R:blossom pc:L -- R:relay pc:B <--> T:bbi ``` ## Sequence Diagram ```mermaid sequenceDiagram autoNumber participant c as Nostr Client participant r as Nostr Relay participant b as Blossom Server participant s as HTTP Server Note over c: Convert <br>HTTP Request<br>into kind 21120 c-->>b: Encrypt & push payload (if large) c->>r: Publish <br>Event r<<-->>s: Fetch event Note over s: Decrypt event s<<-->>b: Fetch payload <br>(if large) Note over s: Make HTTP REQUEST Note over s: Get HTTP RESPONSE s-->>b: Encrypt & push <br>payload (if large) Note over s: Create kind<br>21120 (Response) s->>r: Publish Event r<<-->>c: Fetch event Note over c: Decrypt event b<<-->>c: Fetch payload (if large) Note over c: Convert<br> kind 21120 into<br> HTTP Response c-->>b: Delete REQUEST blob (if exists) c->>r: Delete REQUEST event ``` The remote server should periodically scan for expired RESPONSE events (and associated blossom blobs) and delete them. ## Server Advertisement Event (Kind 31120) To facilitate discovery of HTTP-over-Nostr servers, a dedicated event kind is used to advertise server availability. ```jsonc { "kind": 31120, "pubkey": "<pubkey of server operator>", "content": "HTTP-over-Nostr server", // Optional markdown description of the http server(s) "tags": [ ["d", "<hex pubkey of server>"], // Server pubkey that will be listening for requests ["relay", "wss://relay.one"], // Relay where server is listening (can have multiple) ["relay", "wss://relay.two"], ["expiry", "<unix timestamp>"], // How long this server will be online ], // other fields... } ``` Explanations: * `kind:31120` - BIP39 word #1120 ([message](https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt#L1120)) plus 30000 to make it addressable. * `"content"` - Optional description of the server in markdown * `"d"` - The hex pubkey of the HTTP server that will be processing requests * `"relay"` - Relays where this server is listening for kind 21120 events (can have multiple) * `"expiry"` - Timestamp after which this server advertisement should be considered expired. Can update this to 0 to indicate an expired event. Clients looking to use HTTP over Nostr can query for these kind 31120 events to discover available servers and may communicate with the server operator to get permission to use them. ## HTTP Requests - Kind 21120 Example **request** with a small payload. Payload is in `content` and `P` tag is the npub of the remote HTTP server. ```jsonc { "kind": 21120, "pubkey": "<pubkey>", "content": "$encryptedPayload", "tags": [ ["p", "<pubkey of remote server>"], // P tag entry, this is a REQUEST ["key","nip44Encrypt($decryptkey)"], ["r", "https://relay.one"], ["expiration",<unix timestamp>] ], // other fields... } ``` Explanations: * `kind:21120` - BIP39 word #1120 ([message](https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt#L1120)), plus 20,000 to be treated as ephemeral (not stored by relays). * `"content"` - encrypted JSON with location of blob **OR** the content itself (if under a threshold). NIP-44 is NOT used as the payload may be large, affecting bunker signing stability. * `"p"` - the pubkey of the remote HTTP server. * `"key"` - the decryption key for the `content` field, also the key for the blossom blob (if used). * `"expiration"` - remote servers should not process requests after this time. Relays SHOULD delete events after this time. * `"r"` - (optional) relay on which the response should be sent. ### HTTP Response - Kind 21121 Example **response** with a large payload. Valid JSON is in `content` and `E` tag is populated. For privacy, the requestor npub is NOT shown - the requestor instead should be fetching the response using the `E` tag. ```jsonc { "kind": 21121, "pubkey": "<pubkey>", "content": "encrypt({'url':'blossom.one','hash':'xx'},$decryptkey)", "tags": [ ["key","nip44Encrypt($decryptkey)"], ["E", "<request event id>"], // E tag entry, this is a RESPONSE ["expiration",<unix timestamp>] ], // other fields... } ``` Explanations: * `kind:21121` - A different kind is used for responses, to help with filtering and other use cases. * `"content"` - encrypted JSON with location of blob **OR** the content itself (if under a threshold). NIP-44 is NOT used as the payload may be large, affecting bunker signing stability. * `"key"` - the decryption key for the `content` field, also the key for the blossom blob (if used). * `"E"` - ID of the request event. Enables a response to be identified, and fetched. * `"expiration"` - remote servers should not process requests after this time. Relays SHOULD delete events after this time. There is no "p" tag as the "E" tag already identifies the request. ## Considerations This approach only makes sense in cases where privacy and anonymity are important, or if censorship is a concern. There are a number of drawbacks to the approach: - Complexity. Many more moving parts than a direct request. - Speed. Each request/response now requires: - Encrypting the payload - Loading to blossom (if large) - Signing event - broadcasting event - remote server fetching event - remote server decrypting event - remote server fetching blossom blob (if large) - remote server decrypting blossom blob (if used) - making the actual request And the same flow in reverse. So why would you use it? Several reasons: - enables a plethora of open source apps to be made available from inside private networks (localhost) but over nostr - maximum server privacy (no domain needed, or port forwarding) - Make regular API calls over Nostr