title | draft |
---|---|
HTTP-ILP |
1 |
A standard for paid HTTP requests.
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
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.
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) that she is already using, for instance 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 to the module, to receive payments.
Ankita creates a dedicated subaccount under her account at her Interledger service provider, and takes a note of that subaccount's credentials.
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:
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.
When the HTTP-ILP server module loads up, it calls plugin#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 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.
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.
The uploader tool contains an HTTP-ILP client module
like superagent-ilp or 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)
The paid HTTP request is fired off:
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.
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/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.
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.
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.
Once the HTTP-ILP client module receives the fulfillment, it will now retry its original request:
POST /upload HTTP/1.1
Host: myservice.example
Pay-Token: 7y0SfeN7lCuq0GFF5UsMYZofIjJ7LrvPvsePVWSv450
[...]
The request succeeds:
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.