diff --git a/0014-http-ilp/0014-http-ilp.md b/0014-http-ilp/0014-http-ilp.md new file mode 100644 index 00000000..1181c425 --- /dev/null +++ b/0014-http-ilp/0014-http-ilp.md @@ -0,0 +1,144 @@ +--- +title: HTTP-ILP +draft: 1 +--- +# HTTP-ILP + +> A standard for paid HTTP requests. + +# Design Goals + +Design a protocol to pay for HTTP requests. + +Criteria: + +* Minimum number of roundtrips +* Interaction with HTTP server is via HTTP (actually HTTPS) calls only - we want to tie into the existing load balancing and security infrastructure (HTTPS), no need to run any JavaScript/Websocket client on the HTTP server or other shenanigans + +# Flows + +## 1. Setup + +###### Scenario + +**Ankita** is a *server admin* who owns a file hosting service at `myservice.example` and would like to provide an API where her users can upload files without having to register first. In order to enable this in a standards-compliant way, she decides to use HTTP-ILP. + +### 1.1. Server admin installs an ILP payment module + +After deciding to use HTTP-ILP, Ankita searches the web on instructions on how to set this up. She finds an open-source HTTP-ILP server module which is compatible with the REST framework ([Koa](https://koajs.com)) that she is already using, for instance [koa-ilp](https://github.com/justmoon/koa-ilp). + +She installs the HTTP-ILP server module. The module provides middleware which she adds to the different API endpoints in order to set prices for each one. + +According to the documentation of the HTTP-ILP server module, she learns that she can pass an [Interledgerjs plugin](../0004-ledger-plugin-interface/0004-ledger-plugin-interface.md) to the module, to receive payments. + +### 1.2. Server admin sets up a new receiver + + +Ankita creates a dedicated subaccount under her account at her Interledger service provider, and takes a note of that subaccount's credentials. + +### 1.3. Server admin enters credentials into the HTTP-ILP server module config + +Next, Ankita logs back into her server and edits a config file of the HTTP-ILP server module to enter the plugin type and credentials she obtained: + +```sh +export PLUGIN_NAME=ilp-plugin-btp-client +export PLUGIN_CONFIG={"server":"btp+wss://ankita+filepay:fxPERNaS4FGlC8H7eg6UfYVlglmFynFc8nh5la9PBGM@nexus.justmoon.com"} +``` + +Next, she restarts her server to load the new configuration. + +### 1.4. Paid HTTP server fetches receiver information + +When the HTTP-ILP server module loads up, it calls [plugin#connect](../0004-ledger-plugin-interface/0004-ledger-plugin-interface.md#connect) so that the plugin +gets a subscription to its ledger. Through this subscription, the plugin can listen for incoming payments. +The module then uses [plugin#getAccount](../0004-ledger-plugin-interface/0004-ledger-plugin-interface.md#getaccount) to look up the Interledger address from the plugin +and caches it in memory for a certain period. + +This completes the setup process. The server is now ready to receive paid API requests. + +## 2. Client interaction + +###### Scenario + +**Marat** is a *graphic designer* who would like to upload a mockup image to share with a client. His friend recommended a tool which doesn't require any signup or configuration and uses his existing ILP account to pay for the storage and bandwidth fees. + +![Sequence Diagram](sequence.png) + +### 2.1. Client generates a server-specific token from the hostname and its local secret: + +The uploader tool contains an HTTP-ILP client module +like [superagent-ilp](https://github.com/justmoon/superagent-ilp) or [ilp-curl](https://github.com/sharafian/ilp-curl). +This client module may for instance generate and locally save a `client_secret` using 256 bits of cryptographically secure randomness. + +Before making the paid HTTP request, the HTTP-ILP client module generates a **token**, for instance using such a `client_secret` and the `hostname` of the server it is about to make a request to: + +* Token: `HMAC(SHA256, client_secret, hostname)` + +### 2.1. Client attempts to call API using token + +The paid HTTP request is fired off: + +``` http +OPTIONS /upload HTTP/1.1 +Host: myservice.example +Pay-Token: 7y0SfeN7lCuq0GFF5UsMYZofIjJ7LrvPvsePVWSv450 +Unhash-Content-Length: 123 +``` + +Note that the client hasn't paid at this point and is only making the request to solicit a response from the server, that's why the OPTIONS verb is used. In this example, +`Unhash-Content-Length` is an application-specific header which describes the request which the client intends to make. + +### 2.2. Server responds with payment details + +The server returns an HTTP code of `204 No Content` and includes response headers showing the amount, an ILP address and a shared secret. +The amount expresses how much the request would have cost, as a decimal string and counted in the base unit of the ledger to which the ILP address belongs. + +``` http +HTTP/1.1 204 No Content +Pay: 10 us.nexus.ankita.~recv.filepay SkTcFTZCBKgP6A6QOUVcwWCCgYIP4rJPHlIzreavHdU +Pay-Balance: 0 +``` + +The client can now use the shared secret to create a condition to pay this host. The shared secret may for instance be generated by the server as follows: + +* Shared Secret: `HMAC(SHA256, receiver_secret, token)` + +The shared_secret is now a shared secret between the client and server, but will be unknown to any third-party connectors between them. + +### 2.3. Client initiates an ILP payment to refill its balance + +In order to refill its balance, the client now creates an ILP payment with the following properties: + +* Destination: `us.nexus.ankita.~recv.filepay` +* Amount: `100` +* Condition: `SHA256(fulfillment)` +* Memo: `pay_token` + +The `fulfillment` is generated from the shared secret using [PSK](../0016-pre-shared-key/0016-pre-shared-key.md). + +When the prepared payment reaches the server, it is fulfilled and the token's balance is increased. + +There is a chance that the HTTP-ILP server module will process the payment, but the fulfillment doesn't make it all the way back to the sender. + +### 2.6. Sender/client receives the fulfillment. + +Once the HTTP-ILP client module receives the fulfillment, it will now retry its original request: + +``` http +POST /upload HTTP/1.1 +Host: myservice.example +Pay-Token: 7y0SfeN7lCuq0GFF5UsMYZofIjJ7LrvPvsePVWSv450 +``` +``` +[...] +``` + +The request succeeds: + +``` http +HTTP/1.1 200 OK +Pay: 10 us.nexus.ankita.~recv.filepay SkTcFTZCBKgP6A6QOUVcwWCCgYIP4rJPHlIzreavHdU +Pay-Balance: 90 +``` + +Notice how the 100 units credit from the payment was added to the balance and the 10 unit cost for the current request was subtracted. diff --git a/0014-http-ilp/sequence.mmd b/0014-http-ilp/sequence.mmd new file mode 100644 index 00000000..dd28e56f --- /dev/null +++ b/0014-http-ilp/sequence.mmd @@ -0,0 +1,29 @@ +sequenceDiagram +participant Payer +participant Interledger +participant PayeeWallet +participant Payee +activate Payer +Payer->>+Payee: OPTIONS preflight +Payee-->>-Payer: 204 preflight response +Payer->>Interledger: Quote Request +activate Interledger +Interledger->>Payer: Quote Response +deactivate Interledger +Payer->>Interledger: Prepare Payment +activate Interledger +Interledger->>PayeeWallet: Prepare Payment +deactivate Interledger +activate PayeeWallet +PayeeWallet->>+Payee: Webhook Request +deactivate PayeeWallet +Payee-->>-PayeeWallet: Webhook Response +activate PayeeWallet +PayeeWallet-->>Interledger: Fulfill Payment +deactivate PayeeWallet +activate Interledger +Interledger-->>Payer: Payment Fulfilled +deactivate Interledger +Payer->>+Payee: POST Paid Request +Payee-->>-Payer: 200 OK +deactivate Payer diff --git a/0014-http-ilp/sequence.png b/0014-http-ilp/sequence.png new file mode 100644 index 00000000..f3b2b7d8 Binary files /dev/null and b/0014-http-ilp/sequence.png differ diff --git a/0014-http-ilp/sequence.svg b/0014-http-ilp/sequence.svg new file mode 100644 index 00000000..c54f15f0 --- /dev/null +++ b/0014-http-ilp/sequence.svg @@ -0,0 +1,20 @@ +PayerInterledgerPayeeWalletPayeeAttempt Request402 Payment RequiredQuote RequestQuote ResponsePrepare PaymentPrepare PaymentWebhook RequestWebhook ResponseFulfill PaymentPayment FulfilledRetry Original Request200 OKPayerInterledgerPayeeWalletPayee \ No newline at end of file