Title image by Steve Halama on Unsplash

I recently introduced ILP Torrent, an implementation of a torrent protocol based on WebTorrent that includes verification of payments from downloaders to content creators. What was not included in that post is a more in-depth explanation of how a torrent protocol in general works and what changes I made to the WebTorrent code base in order to allow for payments. Hopefully, this is going to make up for that.

The BitTorrent protocol – The short and simple version

The BitTorrent protocol is a peer-to-peer network for file sharing of all sorts. Some people use it to share music and movies (legally and illegally), companies use it to deploy new code on their servers, and open-source projects use it to fastly deliver files among the community. BitTorrent was not the first peer-to-peer file sharing protocol, there were Napster and Gnutella before it. However, it is the most efficient one because instead of downloading an entire file from one peer (like in Napster and Gnutella), files are split into parts and a torrent client downloads these parts from multiple peers.

Image from Wikimedia Commons and altered to be in English.

Let’s digest the above schema. On the right hand side we have a Web Server that provides .torrent files. For example, makes their popular Linux operating system available to download via Bittorrent and publishes the .torrent files accordingly. The client on the bottom downloads that .torrent file from the Web Server. Such a .torrent file includes the following information (source: Wikipedia):

- announce – the URL of the tracker(s)

- info:

- length – size of the file in bytes

- name – suggested filename where the file is to be saved

- piece length – number of bytes per piece. This is commonly 28 KiB = 256 KiB = 262,144 B.

- pieces – a hash list, i.e., a concatenation of each piece's SHA-1 hash. As SHA-1 returns a 160-bit hash, pieces will be a string whose length is a multiple of 20 bytes. If the torrent contains multiple files, the pieces are formed by concatenating the files in the order they appear in the files dictionary (i.e. all pieces in the torrent are the full piece length except for the last piece, which may be shorter).

A Tracker (on the top) is another server in the system that keeps track of who is currently making which piece of a file available, i.e. who is part of a Swarm. There are two types of participants in a Swarm, Seeders and Leechers. Seeders have the entire file, i.e. all the parts, available and may be the original creators of that file or have completed the download. Leechers are in the process of downloading a file and only have some parts available for others to download from them. The network participant on the bottom, the one that requested the .torrent file from the Web Server, is a Leecher because they only have 37% of all parts downloaded.

Instead of obtaining a .torrent file, a Leecher may also use a Magnet URI to identify a Tracker and to request a peer list from it. A Magnet URI is at least comprised of

- ih: info hash

- tr: tracker

and has the following format:


It includes an endpoint to the Tracker and a hash to identify the file. Additionally, it may include the file name (dn), such that torrent clients can display it during the download process.

Enabling payments in a torrent protocol

We want the creator of a file to be rewarded for their work. Hence, we need to add a license to the .torrent file to include all of the following information:

- announce

- info

- license:

- paymentPointer – proxy payment pointer hosted at a STREAM receipt verifier. This payment pointer proxies SPSP queries to the creator’s actual payment pointer hosted by the wallet, enabling STREAM receipts. (*)

- verifier – STREAM receipt verifier endpoint

- amount – price of the file, e.g. “100”

- assetCode – asset code the amount is denoted in, e.g. “USD”

- assetScale – asset scale of the amount. To avoid expressing decimal numbers all amounts are expressed as integers with an asset scale applied.e.g. “US dollars are commonly expressed with an asset scale of 2”, meaning that an amount of “100” corresponds to 1 USD (100 divided by 10 to the power of the scale: 2).

(*) A blog post on STREAM receipts and the STREAM receipt verifier is coming soon.

The Tracker will use the verifier endpoint to spend the file price as specified in amount, assetCode, and assetScale, before communicating a peer list. Currently, the whole price must be paid before the peer list is shared. In the future, the tracker could be upgraded to allow users to pay continuously as they request updated peer lists for downloading. This makes especially sense for downloading larger files like movies, which would probably also be more expensive than an image.

Payment pointer and verifier are also included in the Magnet URI, together with the usual information like info hash, file name, and tracker endpoint.

This allows the torrent client, in our case the browser, to add a Web Monetization meta tag to the website and to credit STREAM receipts.

The seeding and leeching flow

Let’s go into detail about how seeding and leeching works in ILP Torrent.

(1) Alice the Seeder wants to seed a file. She specifies the license as outlined in the previous section and submits that to her ILP Torrent Client (Alice’s Client).

(2) Alice’s client generates the .torrent file, also including the license. Additionally, it generates the magnet URI.

(3) Alice’s client announces the new file to the tracker, who remembers that Alice is a peer. It also parses the .torrent file and learns about the license.

(4) Finally, Alice’s client shares the magnet URI with Alice.

(5) Alice communicates the magnet URI to Bob the Leecher, who wants to download the file.

(6) Bob enters the magnet URI into his Client (Bob’s client)

(7) Bob’s client decodes the magnet URI and learns about the info hash, file name, tracker endpoint, payment pointer, and verifier endpoint.

(8) Using the payment pointer, Bob’s client starts streaming micropayments to Alice’s wallet.

(9) If the payments are successful, Alice’s wallet issues receipts to Bob’s client.

(10) Bob’s client submits the received receipts to the verifier, using a balance id (e.g. the Web Monetization request id).

(11) The verifier credits the receipts using the balance id.

(12) Using the balance id, Bob’s client now tries to retrieve a list of peers from the Tracker.

(13) The Tracker spends the receipts against the verifier, i.e. verifies them, and if that is successful, i.e. the receipts are valid and the balance is sufficient to pay the specified amount in the license,

(14) it responds to Bob’s client with a list of peers.

(15) Now, Bob’s client connects to Alice’s client and downloads the file part by part.

The implementation: Tweaking WebTorrent

Since I wanted to use Web Monetization micropayments to pay for the torrent license fees, WebTorrent was the obvious choice for a baseline. In contrast to most torrent clients like μtorrent or the BitTorrent Client, WebTorrent is a streaming torrent client for the browser (and node.js) that uses WebRTC for peer to peer communication. Hence Web Monetization can be used in conjunction with it.

As already highlighted in my non-technical blog post, WebTorrent comes with a bunch of modules of which I had to change:

- magnet-uri

- create-torrent

- parse-torrent

- bittorrent-tracker

- torrent-discovery

- and webtorrent itself

The dependency structure is depicted below:

Let me tell you about the tweaks I had to make.


This package encodes and decodes magnet URIs. In order to work for ILP Torrent, I added functionality to also encode and decode the payment pointer and the verifier endpoint.

⇒ ilp-magnet-uri


This package creates .torrent files. It now includes functionality to add a license to the .torrent file.

⇒ ilp-create-uri


This package parses .torrent files. Additions were made to also allow for parsing the license included in the .torrent file. It uses ilp-magnet-uri.

⇒ ilp-parse-uri


This package is 2-sided. It includes a client and a server where the client is used to connect to the server. On the client side, I changed extended WebSocketTracker to handle license information as well as the web monetization request id. I also disabled connection via HTTP and UDP since I don’t support these yet. Changes on the server side were more substantial. I extended the Swarm class to deal with receipt verification by spending against a designated endpoint at the verifier, using the web monetization request id. It retrieves the amount it should spend from the license. Only if this spend is successful, the server communicates the peer list.

⇒ ilp-bittorrent-tracker


The only change I made was to use ilp-bittorrent-tracker instead of bittorrent-tracker.

⇒ ilp-torrent-discovery


Finally, the webtorrent package uses ilp-create-torrent, ilp-parse-torrent, and ilp-torrent-discovery.

⇒ ilp-webtorrent

The code can be found here.

Improving usability: The STREAM Receipt Verifier Service

The first version of the demo of ILP Torrent, which was not announced publicly, required a content creator to have set up their own STREAM receipt verifier. Even though it is not hard, a virtual server is a prerequisite. Furthermore, to ensure security, it should be linked to a domain and TLS should be set up.

To reduce the burden and to make the demo easier to play with, I advanced the STREAM receipt verifier to allow for multiple payment pointers to be proxied. The receipt accounting, however, did not change and still only relies on a balance id, e.g. the Web Monetization request id.

In detail, I added an API that allows you to register and deregister your payment pointer. It creates a proxy payment pointer by hashing the original one and appending it to the verifier’s SPSP endpoint, e.g.

When the Web Monetization provider (e.g. Coil extension) requests STREAM credentials using this proxy payment pointer, the Verifier Service uses the hash to look up the actual payment pointer and forwards the request to the user’s wallet while also sharing the STREAM receipt secret. The response is then also forwarded to the Web Monetization provider, who opens a regular STREAM connection.

The code is available here and the STREAM Receipts Verifier Service for the ILP demo can be found here.

Further reading

- Non-technical post on ILP Torrent

- Just for fun: the announcement of BitTorrent

- BitTorrent Protocol Specification

- BitTorrent Economics paper

- WebTorrent docs

- WebTorrent intro video

- STREAM Receipts Specification

- Glossary entry: Receipt Verifier

Title image by Rio Hodges on Unsplash, with Web Monetization stamps added

I didn’t belong to the group of cool kids that circulated the latest movies on burned DVDs because they knew about The Pirate Bay, one of the most prominent platforms to exchange torrent files and magnet links to download content. My parents are not the greatest fans of technology so I was left to find out about this place that most people associate with piracy much later.

But setting file sharing via a torrent protocol equal with piracy does not do it justice. The technology is used by companies such as Facebook and Twitter to internally distribute software updates and allows open-source projects to host files peer-to-peer. Moreover, it is also a very fascinating game theoretic problem.

Why are these companies and open source projects using a torrent protocol to share files? The protocol allows massive savings on bandwidth. Let me explain that using Youtube as an example. Whenever we want to watch a video, we make a request to a Youtube server that hosts that video file. If we are authorized to view it, the server will respond with the file (or file stream) and our browser will display it. Given that Youtube has millions of users, a large number of them are requesting videos at the same time, they really have to scale up their infrastructure to have enough bandwidth available to handle all of that traffic.

What does a torrent protocol do differently? Originally, there is also one server that is the only one that hosts let’s say an image. A user comes along and wants to view it, so its client requests it from that server. As soon as it receives at least a part of the image, it automatically turns into a server, so it acts as client and server at the same time, allowing other users’ clients to request the image from it. This peer-to-peer network turns a one to many relationship into a many to many relationship.

The disadvantage of a torrent protocol, however, is that there is no single point of access control. In the case of Youtube, the server can check whether the client is authorized to view a video file and even make them pay. On the other hand, authentication / authorization and payments are not part of a torrent protocol. Hence, it is widely used to share content illegally. Giving just one example, the movie and TV industry estimate that video piracy accounts for at least $29 billion of revenue losses per year. Other affected industries are games, music, and publishing.

ILP Torrent attempts to solve this issue by adding payments to a torrenting protocol.

Adding licenses and payments to a torrent protocol

In order to share any file via a torrent protocol, a torrent file needs to be created. This file includes information about the file name, file size, and at least one initial connection endpoint, among others.

With the aim of processing any kind of payment prior to sharing a file, some sort of metadata needs to be included specifying the payment details and the corresponding usage rights. This is what I call a license. To keep it simple, that license includes a payment pointer, a price, and a verifier endpoint which is used to verify that the payment actually occurred to the provided payment pointer. For now, it is assumed that the payment of that license fee grants unlimited usage rights to the file.

Processing Payments

The above schema of the peer-to-peer network is missing one component – the torrent tracker, which is what I called an initial connection endpoint before. The tracker is a server that, instead of hosting a file as in the client-server Youtube example, it keeps track of which peer has which file or part of a file available for download and also stores a copy of the torrent file.

Every participant in the network, before becoming a peer, connects to a tracker. It requests a list of peers as well as information about which file parts each individual peer has available for download.

For a first demo, this is exactly where we can enforce payment. Instead of plainly requesting a list of peers, the ILP Torrent client has to prove that it paid for the file by passing STREAM receipt IDs along with the request. The ILP Torrent tracker uses the verifier endpoint stored in the torrent file to verify the receipts and if successful, returns the list of peers. The client is now able to download the file.

The implementation

There are various implementations of torrent protocols out there. The most prominent one is probably BitTorrent, which uses the UDP protocol for communication among peers and requires users to download a client. I wanted this demo to work in the browser and to use Web Monetization for license fee payments, hence I rely on another implementation — WebTorrent — which uses WebRTC for communication.

WebTorrent comes with a bunch of modules, of which I made adjustments or additions to the ones highlighted:

A more in-depth technical introduction of ILP Torrent will follow, where I’ll go into detail on torrent protocols in general and the specifics on what I changed in WebTorrent.

The demo

As explained in the Processing Payments section, the tracker verifies payments using a STREAM receipt verifier. Hence, the demo really consists of two parts – ILP Torrent and a Verifier Service. There will probably also be a post soon on the Verifier Service, so stay posted.

ILP Torrent allows you to download (leech) and upload (seed) a file. In order to upload a file, you need to specify all the license components. Right now these are a (proxy) payment pointer, a verifier endpoint, a price, a currency, and optionally a new file name. If you are not running your own STREAM receipt verifier, you may use the Verifier Service which is linked on the demo page.

Quick Interlude – The Verifier Service

The Verifier Service is hosted on a virtual server managed by Coil. After registering your individual payment pointer, it generates a proxy payment pointer which proxies all incoming SPSP requests to the server where your individual payment pointer is registered, let’s say at Uphold, and asks Uphold to include STREAM receipts in the STREAM packets. These receipts are credited to the Verifier service by your ILP Torrent client (in the browser) and spent by the ILP Torrent tracker before communicating the peer list.

The Verifier Service allows you

- To register a payment pointer:

- To test the generated proxy payment pointer:

- And to delete the proxy payment pointer from the server:

After reading until here, you really deserve to see a demo now. Here you go!

Let’s have a look at the individual steps of ILP Torrent:

The landing page allows you to download (leech) a file by just copy and pasting a magnet URI. A magnet URI is an identifier for a torrent file and in the case of ILP Torrent includes:

- A hash

- The file’s name

- The ILP Torrent tracker endpoint

- The proxy payment pointer

- And the verifier endpoint

Once you submit, the payment pointer is extracted from the magnet URI and the meta tag is added to the page. The extension starts streaming micropayments.

As soon as the funds are sufficient, the ILP Torrent tracker releases a list of peers and the client downloads the file. Web monetization stops. Note that you are potentially also seeding to other clients now.

Now let’s have a look at seeding a new file and let’s assume that you already set up your proxy payment pointer. Fill out the details about your file and select it by browsing your file directory.

On submit, you receive the corresponding magnet URI.

In a private window, you can now try to download that file. Copy the magnet URI and paste it into the private instance of the app. After a short time, you should receive your file. Make sure to not price it too high because otherwise it can take quite some time before the funds are gathered.

Issues with this first demo implementation

1. Trust issues

The biggest elephant in the room for me is that the creator, i.e. the owner of the file that is seeded, has to trust that the ILP Torrent tracker performs the payment verification before sharing the list of peers. One could also make the peers perform this verification, but then the creator has to trust not just one entity but potentially thousands of people. One way of circumventing that could be a revenue split between the creator and the peers, where the peers verify payment to a revenue sharing payment pointer. This would also incentivize them to keep seeding the file after having downloaded it completely.

2. Piracy

To circumvent the ILP Torrent tracker and hence payment all together, one could imagine a secondary platform where seeders publish peer lists they have received from the ILP tracker and that can be added to a tweaked version of a torrent client. In that scenario, moving verification to the peers is also the solution.

3. Currency conversion

STREAM receipts do not include asset details (which currency and asset scale is being used) but include the amount received denominated in the receivers’ wallet’s asset details. The ILP Torrent tracker, when verifying payment, spends against the verifier endpoint, using the asset details provided in the license in the torrent file. The asset scale is assumed to be 9. ILP Torrent tracker itself is not doing any currency conversion because it does not know about the receiver’s asset details. Hence, when seeding a file, creators have to select the currency corresponding to their payment pointer. This could be solved when we introduce the Open Payments protocol, which will make asset details more accessible.

There are probably multiple other issues that I am not thinking of and that should be added to this list so feel free to reach out to me and let me know!

All the resources

- In-depth technical introduction (coming soon)

- For those who can’t wait for the technical blog post, here is the code

- ILP Torrent demo

- Verifier Service

- STREAM receipt verifier

At Coil, we have recently put a lot of effort into making our extension compatible with Tor Browser such that users that want more advanced privacy features can also support their favorite content creators.

The Tor maximalist may now argue that one should not install any extension on Tor Browser since an extension can introduce privacy vulnerabilities. While this is true in general, we have put a lot of thought into making the open-source Coil extension as privacy-preserving as possible. We just recently launched a blind signature token scheme based on the cryptographic scheme of PrivacyPass to allow our users to stream micropayments without revealing who they are (more details can be found in Ben’s article).

So what prevents the Coil extension from working out of the box in Tor Browser, since it is based on the already supported Firefox Quantum engine? The first problem was the extension’s dependency on IndexedDB. Just like in Firefox Privacy Browsing Mode, IndexedDB is not working in Tor Browser. It is a known bug the Firefox development team is working on. This was an easy fix: We simply disabled the (unused) feature that depended on IndexedDB and witnessed the first successful Web Monetization micropayment in Tor Browser. Unfortunately, some issues still occur:

* The Coil login screen is just blank

* The Coil explore page doesn’t load perfectly

* The Coil extension is stuck in the payment setup stage

* The Coil extension says the user is logged out while s/he is logged in

We are still investigating why this happens. Some issues seem to be related to the interaction between Tor Browser and Cloudflare. While Tor wants to empower their users by granting better privacy, some black sheep are exploiting that feature and conduct illegal activities using the Tor Network. If Cloudflare has noticed suspicious activity from a Tor exit node before, it may either blocklist this node, denying requests to Cloudflare proxied services, or it requests a CAPTCHA to be solved. The Coil extension needs to request JavaScript files, login details, as well as connection tokens from a server that is proxied by Cloudflare. Depending on the current circuit the user is on, their requests are successful or unsuccessful.

We realize that this is a horrible user experience so we are working on a solution, reaching out for help from the teams at Tor and Cloudflare. In the meantime, if the Coil login screen or explore page don’t load correctly, you can reset the Tor circuit and it should work once you have found a “clean” exit node. Enabling web monetization is a bit more tricky. Since the extension resources are only loaded once, when you open the browser, circuit reset does not do the trick. You will have to restart the browser to try again.

Since we at Coil strive to be as privacy conscious as possible, we will continue working on this issue until it is fixed. We will keep you updated!

Thanks to Georg @Tor Browser, Ben, Niq, Tiffany, Dees, and Stefan.

Image by Tumisu on Pixabay, modified by myself.

Right now, we live in a world where all of our communication has moved online. My friends and I used to meet once a month to play an escape room, now we play using an online game and Jitsi. My mom, who is a midwife, used to give seminars for pregnant women, now I taught her how to use Zoom to still be able to do her work and make a living. She still needs to somehow get a signature from the pregnant women, however, in order to invoice the health insurance for the seminar fee (I’m German, health insurance covers everything ;–) ). It would be so much more convenient if the women were paying while attending her online seminar.

Obviously, midwives are not the only occupation group that could benefit from an instantaneously incoming payment stream while delivering a service. Psychiatrists, teachers and tutors, fitness trainers, nutrition coaches, etc. come to mind right away. Additionally, real world content creators like musicians, who depend on live concerts to make a living, would be able to livestream it from anywhere in the world. And academics could host conferences virtually while attendees pay a fee to the organizers.

Web Monetization is an open-source JavaScript browser API that allows for a payment stream from the user/client to the website which is delivering content. It facilitates the Interledger Protocol (ILP) for said payment stream, making it currency agnostic. Hence, it is exactly the technology that can make monetized video calls happen and allows service providers to reach an international audience.

I started looking into implementing a web monetized version of a video call system about three weeks ago and quickly landed on Jitsi Meet since it is completely open source. However, Jitsi is so feature rich that a robust web monetized version will take me a little while to develop. In the meantime, I wanted to give an idea of what I have in mind and created a simple demo on Glitch using the Jitsi API.

If you can’t wait to try it out, here is the link:

The demo

The welcome page asks the host of an online meeting or video call to specify a room name and a payment pointer that will receive the streamed payments from the attendees.

Once they submit the form, hosts are redirected to a page that includes an embedded Jitsi meeting with the given room name. Additionally, there is a link displayed to share with attendees. Note that the hosts themselves do not stream any payments while being in the meeting.

Once attendees have obtained the link from the host, those that have Web Monetization enabled can join the meeting and stream micropayments while staying actively on the site. If they leave the tab and Web Monetization stops, they are automatically removed from the video call.

A potential attendee that does not have Web Monetization enabled will not be able to access the room on the demo page.

Here is a recording of the entire flow:

Why it’s just a demo

Let me address the elephant in the room first. Micropayments are too small to cover any consulting fees. Coil streams on average $0.36 per hour on behalf of our members. In the short term, features like tipping or manually increased payment rates could be an alternative. Ideally, however, the host can specify a stream rate when creating the room.

Additionally, the video call is created using the Jitsi API and embedded into the demo page. A demo call called “my-important-meeting-2020” can be found on The call itself, however, is hosted on Jitsi Meet and can be accessed on, circumventing the necessity to stream micropayments. Even if I hosted my own version of Jitsi Meet, the link to the actual room can be found when inspecting the HTML.

The only solution seems to be to bake Web Monetization right into the Jitsi Meet implementation, which is open-source, and not to rely on the API. But there is still a sneaky way for the tech nerd to circumvent payment. You can simply open the browser development tools and change the payment pointer in the HTML head. The host cannot instantaneously verify that they are receiving payments. To address this problem, the ILP community are working on STREAM receipts, which will allow third parties to verify payments into a certain wallet before serving content. Once this feature is rolled out, I’m keen to build a bullet-proof version of the Web Monetized video call system.

All the resources

* The demo

* The source code

* Jitsi

* Jitsi Meet implementation

* Jitsi Meet API

* Web Monetization

* Interledger Protocol

* STREAM receipt RFC

* STREAM receipt implementation

Thanks to Ben and Tiffany for helping me write this post.

Take me straight to the demo!

Most of us scroll through content every day. Twitter, Facebook, Instagram – you name it. People spend time and sometimes even money to produce that content. However, when creators upload their content to these platforms, they sign a perpetual and exclusive license which allows only the platforms to monetize that content. In return, all of us enjoy social media for free. But is it really free? Social media platforms use our content to sell targeted advertisement space, hence generating revenue from content that users created. That is their business model.

Web monetization allows for an alternative business model for the web. Instead of platforms being the only players benefiting from content, consumers stream micro-payments to content creators, for example with the help of a Coil membership and extension. Social media platforms could offer their services without selling advertisement space by introducing a revenue sharing scheme, for example probabilistic revenue sharing.

To demonstrate how a minimalistic Instagram alternative could look like, I implemented the Intersection Observer Demo on Glitch.

While you scroll through the gallery, different images come into focus. As soon as 75% of an image are in focus, the Intersection Observer picks up the payment pointer that is included in the HTML

<div class="pic" name="$">

<img src="">


and changes the content of the monetization meta-tag. While a Coil user visits the gallery, a counter shows how much money has been streamed to the content creator. For the demo, I took most pictures from unsplash and hence the payment pointers of those images belong to the non-profit Internet Archive (@internetarchive) and the charity Good Souls Group (@GoodXrp). One picture was taken by me (the South African Protea) so I included my payment pointer. You can find the source code here.

This is just a demo but if one added user management and upload functionality, we end up with a web monetized Instagram alternative (yes, without stories and all that stuff; but how would I know, I'm not on Instagram).

Stay tuned for more demos to come. You can already find a small selection on Coil's Glitch Team site.

I know I'm a little bit late to the party since the year has already started, but you know how life is. My family demanded my full attention over the holidays (at one point, I almost believed my new name was “could-you-please...”) so I didn't get to write this as initially planned.

Due to other obligations and visa restrictions, I have been non-active for about 4 months now so my first and very personal goal is to get back to speed with all the developments that have been happening during my absence. I already spent this morning looking into the Open Payments Protocol proposed by @adrianhopebailie and team and I am amazed by the work that has been put into it. It is definitely a step in the right direction if we want to use Interledger as a means of payment. Since I mainly worked on pull payments last year, I'm very much looking forward to getting involved in the development of the spec as well as the reference implementations.

Last year was actually my first year in the Interledger community and I am very grateful to all the people I met and that helped me to get started, especially @sharafian, @wilsonianb, @adrianhopebailie, @justmoon, and @evan. Without those people and the internship I did at Coil, I would have given up on Interledger. Even though almost everything is documented, I found it hard to find the right information. The 20 minute “Hello World” tutorial is missing and I didn't know where to start. The protocol itself is comprised of so many components that are all connected, that it is extremely difficult to break it into understandable pieces. Hence, I hope that 2020 will be the year of great documentation. We need short tutorials that explain the stack layer by layer, starting with the application layer and then moving down. By the end of this year, it should be possible for a newcomer like me to learn about Interledger without having one of the inventors sitting across the room from you.

Speaking of newcomers, I would like to see a lot more new faces in the community (or names on the forum or call, whatever works for our distributed community), especially women. Last October, I attended the Grace Hopper Celebration and talked about Interledger on a panel about the Internet of Value, which was extremely well received. I can see myself giving talks at meetups in Berlin or elsewhere this year to excite more people for Interledger.

Finally, I have been annoyed by the fact that more and more podcasts move away from being freely available (and giving me their ads in between the story) to being exclusively on Amazon, Spotify, you name it. Don't get me wrong, I'm not a fan of the ad model, but I also don't want to buy a subscription to all of these platforms. Just like for video, podcasts are an amazing use case for micropayments, especially since there is not the one big platform for them yet (video has youtube and netflix, audio has spotify and apple music). I would love to have a platform for podcasts using Interledger micropayments and web monetization such that there is an alternative for these content creators, just like Cinnamon created an alternative platform for video creators.

Happy New Year 2020!

I recently made my way back to San Francisco to attend San Francisco Blockchain Week 2019. The final event of this week was the DeFi hackathon. But after an amazing week of Crypto Economics Security Conference, Macro.WTF, and Epicenter Conference, not to forget about the numerous meetups and Halloween parties, I was pretty exhausted and not in the mood for a hackathon. I didn't want to commit to a team or a project and I also didn't have an idea myself. So on Friday night, after the opening ceremony, I called it a day and went back to my bed and breakfast for a good night's sleep.

The first thought that popped into my mind the next morning was “What if I could easily web monetize a Hugo static site?” It may seem weird that this is the first thing I would think of but considering the facts that (a) I also visited my friends at Coil during the week and that (b) I have been trying to convince my fiancé (who I call every morning) to enable web monetization on his blog, this is just putting two and two together.

As most of the readers probably know, web monetization is a standard. It is a JavaScript browser API which allows the creation of a payment stream from the reader to the content creator. The user needs a browser extension like Coil or Minute, or they are ahead of their time and use the Puma Browser. These will check the website for the existence of a meta tag called "monetization" which includes the payment pointer.

Full of energy, I headed back to the hackathon, googling Hugo themes on the bus and texting my friend Aaron about the idea. By the time I got to the venue, I had already decided on the Hugo theme I wanted to play with – Newsroom by Weru. My knowledge on Hugo in general was very limited, though. I have helped my fiancé create his page and I played with one on my own a while ago (mainly because my PhD supervisor keeps nagging me to finally create a personal website), but I wasn't too familiar with the whole framework. Luckily, documentation is excellent so I had a page up and running in no time.

Just for those of you who don't know this famous static website generator: Hugo makes it super easy for average Joe to create a beautiful website. Content creators write all their posts in markdown and all configurations are made in a global config.toml file. A theme is imported as a git submodule, which takes care of all the styling.

The Hack

The first step was to fork my chosen theme Newsroom and to enable web monetization in general. This was quite easy and only required an HTML template that was loaded in the page header and included the meta tag:

<meta name="monetization" content='{{ .Site.Params.monetization }}'>

To make it more user-friendly, the payment pointer is a variable set in the config.toml:


monetization = "$"

I created a demo site, imported my tweaked theme, set the payment pointer in the config.toml, and voilà, micropayments were flowing!

I could have stopped here and that would have been the smallest hack in history, but I deliberately chose a blog theme to also allow for exclusive content. And here is where it became tricky. First, I thought I would simply let the blog post creator wrap the entire part that is supposed to be exclusive in a div-tag. However, this has 2 disadvantages: (a) the user may not know what a div-tag is, and (b) Hugo doesn't recognize the markdown within the div-tag and thus completely destroys the layout. So that was not an option.

Alternative 2 was to include just one line above the content that is supposed to be exclusive in the markdown:

<div id="exclusive"></div>

I realize that this solution doesn't solve problem (a), but it is just one line that can be copy-pasted by the blogger (and let's face it, we are always copy-pasting stuff). Everything that is included below this line is made exclusive by the magic of JavaScript. How, you ask?

As mentioned before, web monetization is a JavaScript browser API. If web monetization is enabled on a site, i.e. a meta tag including the payment pointer exists, tools like the Coil extension or the Puma Browser create the variable document.monetization, which includes the state of monetization, pending or started. They also emit an event called monetizationstart whenever the stream of micropayments is starting.

My code filters all tags below the exclusive div-tag and hides this content by default. Whenever the monetizationstart event is emitted, it shows the content plus a little thank you note.

If document.monetization does not exist or the payment stream does not start, the content stays hidden.

I am aware of the fact that this is not the most elegant solution and that a clever person can still access the exclusive content without paying by using the developer console of the browser. If you are a passionate and skilled front-end developer, feel free to improve that! Here are the repositories:

- the theme:

- the demo:

The demo is still live and can be found here:

Thanks to Aaron Soskin for all the images, the great blog post on the demo page, as well as supporting me during the hack!

What is next for Hugo + Web Monetization?


Header image by Dragan Miljkovic on Unsplash

In its current design, the Interledger Protocol (ILP) only allows for push payments. Imagine BigCompany accepts ILP payments of any currency and Alice wants to utilize this to pay for CoolProduct. All she needs is an identifier for BigCompany's account—the payment pointer—maybe even a specific one for her customer account at BigCompany, and off the packets of value go. But what if CoolProduct is actually a CoolSubscription? Alice does not want to be bothered to send an equal amount to BigCompany's payment pointer every month in order to extend her subscription. She wants BigCompany to directly pull this amount from her account, just like using a credit card for recurring payments.

Recap: Push Payments

To understand the process of pull payments, let me first briefly recap how push payments work.

Alice runs the SPSP Client and BigCompany hosts the SPSP Server. In the simplest scenario, both are connected to the Interledger network through moneyd. BigCompany's SPSP Server exposes multiple HTTPS endpoints that are abstracted by payment pointers. Alice queries her corresponding customer payment pointer $ (1), which returns the ILP destination_account and shared_secret (2). Using these two parameters, Alice's SPSP Client opens a STREAM connection and sends value to BigCompany (3). BigCompany's SPSP server will update Alice's balance accordingly. Done.

If BigCompany were offering some CoolSubscription, Alice had to do this process every subscription interval to keep her plan. If she forgets it once, her subscription is cancelled and she has to go through the registration process again. This is way more tedious than just paying with credit card. If only there were a way to authorize BigCompany to pull the subscription fee every interval. Now there is!

Introducing Pull Payments

Pull payments are comprised of three stages:

1. the negotiation

2. the pull pointer creation

3. the actual pull

The negotiation phase

Before a pull can happen, the two counterparties—Alice and BigCompany—have to decide on the terms and conditions of the pull. Alice wants BigCompany to only pull the subscription fee, denoted in USD, every month for the next 12 months. BigCompany wants to have the opportunity to pull January's fee in February without losing out on these profits, i.e., BigCompany does not want the pull amount to be capped. If it already existed, a UI enabled version of this negotiation process would look like this:

Alice decides she really wants CoolSubscription, offered by BigCompany, so she taps on “Get” (1). BigCompany asks Alice to create a pull pointer, a special payment pointer including a token, having the sole purpose of paying for CoolSubscription (2). This pointer needs the following information:

- Amount: Amount, denoted in Asset Code, which can pulled by BigCompany each Interval.

- Asset Code: Asset Code corresponding to Amount, e.g USD.

- Asset Scale: The scale of Amount, e.g. an Amount of 1000 with a 

Asset Scale of 2 and an Asset Code of USD translates to 10.00 USD.

- Interval: ISO8601 duration, e.g. P0Y1M = 1 month, which describes how often BigCompany can pull Amount.

- Cycles: Number of times Interval is repeated.

- Cap: Defines whether any funds not pulled before the start of the next interval cycle is accumulated or expires.

- Start: (optional) ISO8601 UTC timestamp, representing the time at which BigCompany can pull for the first time.

Most of these fields are set by BigCompany and Alice cannot alter them. However, she should be able to change the Cycles field, indicating for how long she is subscribing to CoolSubscription. Granted, this negotiation phase is quite short. One could imagine a process where BigCompany first asks Alice what currency she wants to use, then how long she wants her subscription to last and based on these details, she receives a quote. If she doesn't like this quote, she changes currency and/or the number of cycles and BigCompany will offer her a new price. Both go back and forth until they are satisfied.

The pull pointer creation

Once Alice and BigCompany agree on the parameters of the subscription, Alice needs to create the according pull pointer. Let's imagine there was already a wallet provider that had a UI for pull pointer creation called The Wallet.

When Alice clicks “Create” in (3), she is redirected to her wallet, which asks her to double check the parameters of the pull pointer. She clicks “Create” and the pull pointer is generated (4). 

There are two ways to generate the pull pointer—online and offline. In the online version, The Wallet app sends a POST request to its SPSP Pull Server, an SPSP server that, instead of handling incoming payments, handles outgoing or pulled funds. The POST request includes all of the negotiated parameters, i.e.

$ http POST amount=1000 interval=P0Y1M cycles=12 cap=false assetCode=USD assetScale=2 Authorization:"Bearer test"

which it uses to create a database entry. It responds with the corresponding pull pointer:

HTTP/1.1 200 OK

Connection: keep-alive

Content-Length: 78

Content-Type: application/json; charset=utf-8

Date: Wed, 06 Mar 2019 00:42:30 GMT

Server: nginx/1.10.1


"pointer": "$"


The endpoint is password protected such that only authorized requests are processed. Alice needs to be online to create this pull pointer because her The Wallet app needs to communicate with the SPSP Pull Server.

Excursion: Offline pull pointer generation

While this is surely the case Alice is online for online shopping, one can think of use cases where she would like to create a pull pointer while being offline.

Maybe Alice is at her favorite beach bar in rural Mexico that does not have free Wifi (yes, these places do still exist) but the bar offers a special deal on beer. If Alice provides a pull pointer, she only pays 0.12 MXN (0.006 USD) per barn-megaparsec. Luckily, The Wallet app comes with the possibility to create offline pull pointers. These are pull pointers that include a JSON Web Token (JWT), signed by a secret that is shared between Alice's instance of The Wallet app and the The Wallet's SPSP Pull Server. When the SPSP Pull Server receives a pull request for this pointer, which does not exist in the SPSP Pull Server's database yet, it verifies and parses the JWT. Afterwards, it creates an entry for the pull pointer in its database and processes the pull. Naturally, the pull pointer is much longer because it needs to accommodate all of the negotiated parameters within the JWT, e.g.


Let us return to the CoolSubscription example. Alice's The Wallet app has created a pull pointer (4) with which she returns to the checkout process on BigCompany's webpage. She submits the pull pointer (5) and voilà, Alice has successfully subscribed to CoolSubscription (6).

The pull

Before BigCompany responds with its happy message of completion, it will pull for the first time. What does that entail?

BigCompany's SPSP Client queries the pull pointer that Alice provides (1) and receives the endpoint's ILP destination_account and shared_secret (2). It uses those two to open a STREAM connection to the SPSP Server. Once connected, it uses STREAM's receiveTotal to receive the funds it needs to activate Alice's subscription, i.e. the 10.00 USD (3). On completion, the SPSP Client will close the STREAM connection. At that point, it would trigger a webhook that redirects Alice to the completion view.

A month later, BigCompany will attempt to pull again and if it is successful, it will renew Alice's subscription for another month. This continues until all the cycles have passed. However, if Alice really enjoys her CoolSubscription, she can update her pull pointer and increase the number of cycles. BigCompany may also increase the price of CoolSubscription, forcing Alice to update the amount within the pull pointer. If she fails to do so, her subscription will automatically end. Technically, the update is another POST request to The Wallet's SPSP Server:

http POST cycles=24 Authorization:"Bearer test"

HTTP/1.1 200 OK

Connection: keep-alive

Content-Length: 225

Content-Type: application/json; charset=utf-8

Date: Wed, 04 Mar 2020 22:57:28 GMT

Server: nginx/1.10.1


"amount": "1000",

"assetCode": "USD",

"assetScale": "2",

"balanceAvailable": "0",

"balanceInterval": "1000",

"balanceTotal": "12000",

"cap": false,

"cycleCurrent": 12,

"cycles": "24",

"interval": "P0Y1M",

"start": "2019-03-06T00:42:30.893Z"


If Alice hates CoolSubscription, she can also cancel it at any point by updating the pull pointer and reducing the number of cycles.

Give me more details!

Obviously, this post mostly tells the story but does not go into much technical detail. Here are links to bridge the gap:

- SPSP spec

- SPSP pull payment spec

- SPSP Pull Server implementation

- SPSP protocol library including pull payment support: Pull Request

- SPSP CLI including pull payment support: Pull Request

Thanks to Brandon Wilson and Ben Sharafian for your helpful input when writing this article.

Other use cases