<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>The Interledger Community 🌱: Gavin Chait</title>
    <description>The latest articles on The Interledger Community 🌱 by Gavin Chait (@turukawa).</description>
    <link>https://community.interledger.org/turukawa</link>
    <image>
      <url>https://community.interledger.org/images/YcqHhaiHbsiPEJtnL1lLDxCfcLuTQAPShpaoVMnoQ6Y/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL3VzZXIv/cHJvZmlsZV9pbWFn/ZS80NjkvNjkzYTgw/ZDQtNTEwNS00ZGM4/LTk3YzctZTM1NDE1/Mzk3MTFhLmpwZw</url>
      <title>The Interledger Community 🌱: Gavin Chait</title>
      <link>https://community.interledger.org/turukawa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://community.interledger.org/feed/turukawa"/>
    <language>en</language>
    <item>
      <title>Hop Sauna social marketplace framework — ILF Ambassador Final Report</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Sat, 24 Jan 2026 08:45:25 +0000</pubDate>
      <link>https://community.interledger.org/hopsauna/hop-sauna-social-marketplace-framework-ilf-ambassador-final-report-35h3</link>
      <guid>https://community.interledger.org/hopsauna/hop-sauna-social-marketplace-framework-ilf-ambassador-final-report-35h3</guid>
      <description>&lt;p&gt;&lt;a href="https://codeberg.org/whythawk/hop-sauna" rel="noopener noreferrer"&gt;&lt;em&gt;Hop Sauna&lt;/em&gt;&lt;/a&gt; is a core technical stack aimed at developers to support implementing a federated, community-moderated web shop offering custom digital objects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/w_oVSNLUGpJiWvF4q9_qJ6P8K01Vq0j9gXWTbes1p2s/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3cyOXgzeDBs/cnBhMXplMzYzeXln/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/w_oVSNLUGpJiWvF4q9_qJ6P8K01Vq0j9gXWTbes1p2s/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3cyOXgzeDBs/cnBhMXplMzYzeXln/LmpwZw" alt="Gavin Chait presenting Hop Sauna at the 2025 Mexico City Interledger Summit" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Update
&lt;/h2&gt;

&lt;p&gt;Those who wish to lead change have a responsibility to demonstrate and build trust with the communities and individuals we hope will support us.&lt;/p&gt;

&lt;p&gt;Our online activity is fraught with &lt;a href="https://community.interledger.org/carolinesinders/by-design-the-hidden-harms-within-banking-apps-ilf-ambassador-final-report-4k29"&gt;dark patterns, described by Caroline Sinders in her Ambassador Report&lt;/a&gt;. For open payments to reach greater acceptance by regulators, buyers and sellers, we have to demonstrate greater ethical responsibility than the alternatives. More than that; we have to be easier to use, easier to understand, and safer for everyone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supporting African creators
&lt;/h3&gt;

&lt;p&gt;The community I most wish to bring online are African authors on the continent who struggle with financial exclusion.&lt;/p&gt;

&lt;p&gt;Even if you are a well-published, famous African author in the diaspora - a &lt;a href="https://suyidavies.com/" rel="noopener noreferrer"&gt;Suyi Davies&lt;/a&gt;, &lt;a href="https://nnedi.com/" rel="noopener noreferrer"&gt;Nnedi Okorafor&lt;/a&gt;, or &lt;a href="https://us.macmillan.com/author/tadethompson" rel="noopener noreferrer"&gt;Tade Thompson&lt;/a&gt; - that doesn't mean you can reach African readers. And, if you're an African living on the continent, you can give your work away on any of the major platforms - as &lt;a href="https://www.dilmandila.com/" rel="noopener noreferrer"&gt;Dilman Dila&lt;/a&gt; does on YouTube - but you struggle to charge for it.&lt;/p&gt;

&lt;p&gt;And then there are the horror stories. Anyone who has ever self-published on Amazon knows how hard it is to sell even one book. A few years ago &lt;a href="https://odekpeki.com/" rel="noopener noreferrer"&gt;Chovwe Ekpeki&lt;/a&gt;, a Nigerian author, edited an anthology of African scifi which he modestly called The Year's Best African Speculative Fiction. Listed on Amazon's US site, he managed to sell $2,000 of books. Just before he was due to be paid out, Amazon notified him that Nigerians weren't eligible to sell, and they closed his account, and took his - and his contributors - money. This was eventually sorted out, but it left us all with a sense of how tenuous is our relationship with these platforms.&lt;/p&gt;

&lt;p&gt;And so we come to our challenges: we are not able to gain access to readers, we can't get paid by the places we publish, and our readers can't always use the ways they are used to paying - like mobile money - online.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hop Sauna release
&lt;/h3&gt;

&lt;p&gt;I'm struck by just how fundamentally weird the contemporary internet has become. It's got the diversity, curiosity and innovation of a small agricultural town in middle America, or even somewhere in the Northern Cape. In many parts of the world, Facebook is the whole of the internet, just like Amazon is the place you shop. Discovery and purchase happens exclusively in properties controlled by such a small number of companies.&lt;/p&gt;

&lt;p&gt;You can judge how far into the barren wastes of the web you are by how often the site you're visiting pressures you into subscribing to their newsletters and build an account there. They're desperately worried they're about to lose you forever.&lt;/p&gt;

&lt;p&gt;When I started developing Hop Sauna, I wanted to break these walls open to the wild early internet. I want software that encourages connection, is moderated by the creators who sell their work on that site, has some friction to launching new products so that it feels more intentional, and I want payments to be low-cost, and direct between buyer and seller.&lt;/p&gt;

&lt;p&gt;Some of this leans into the growing community of ActivityPub social websites, like Mastodon, but a lot depends on Interledger's success with open payments.&lt;/p&gt;

&lt;p&gt;Consolidating everything I've learned this year is still a work in progress, and this report is a continuation of &lt;a href="https://community.interledger.org/hopsauna/hop-sauna-ilf-ambassador-progress-report-35oa"&gt;my interim report&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progress on Objectives, Key Activities
&lt;/h2&gt;

&lt;p&gt;If you know your Clausewitz, no plan survives contact with reality, or your Tyson, where everyone has a plan till they get punched in the face, these are my original goals, and how they evolved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goal 1: deliver a template developer stack ready for custom creation of creator community-moderated Open Payments-enabled web shop
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://codeberg.org/whythawk/hop-sauna/" rel="noopener noreferrer"&gt;Hop Sauna&lt;/a&gt; is available as an initial release. There is a great deal of documentation for developers, and much more to be done. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/aTuwV2z1aG4z7vbJuJtDkNzFhtHzOL1lZo7jM3Hn4MY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL25nZmphc2kw/b3lvYmM1eHBscmN3/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/aTuwV2z1aG4z7vbJuJtDkNzFhtHzOL1lZo7jM3Hn4MY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL25nZmphc2kw/b3lvYmM1eHBscmN3/LnBuZw" alt="Demonstration of open payments workflow on Hop Sauna" width="727" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This went more or less as expected, although the steepness of the learning curve, and the challenges of working with relatively immature software, did cause delays as I grappled with getting everything into my head.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goal 2: develop marketplace tools for facilitating buyer and seller exchanges, including for webhooks for responding to successful purchases, disputes and refunds
&lt;/h3&gt;

&lt;p&gt;Here I was limited by the provisional state of the &lt;a href="https://github.com/interledger/open-payments-python-sdk/tree/main" rel="noopener noreferrer"&gt;Python Open Payments Developer Kit&lt;/a&gt;. Over the course of my project, I refactored and redeveloped the kit, with the following updates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Model refactoring and fixes&lt;/strong&gt;: generated code resulted in models that don't return data, or duplications and deeply-nested &lt;code&gt;RootModels&lt;/code&gt; much of which could be deleted and simplified.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New convenience functions&lt;/strong&gt;: testing the API effectively requires building a full purchase workflow, but such a workflow is also useful to developers using the SDK. The &lt;code&gt;README&lt;/code&gt; in the &lt;code&gt;process&lt;/code&gt; folder details the main objectives and roadmap, and this update implements the simplest possible payments workflow for purchase and sale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updated tests&lt;/strong&gt;: all the tests should be refactored, especially as private endpoints have been committed to the repo. Tests for the convenience functions run through a complete workflow, and use the new environment variables to safely do a real-world test on the Rafiki test server.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is currently a &lt;a href="https://github.com/interledger/open-payments-python-sdk/pull/12" rel="noopener noreferrer"&gt;pull request&lt;/a&gt; awaiting review.&lt;/p&gt;

&lt;p&gt;Further development on the open payments aspect of Hop Sauna awaits on approval of this pull request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goal 3: kick-start cooperative creator marketplaces through engaging with stakeholders to align the template stack with their needs, and to develop a supportive environment for project sustainability
&lt;/h3&gt;

&lt;p&gt;Early in 2025, I convened a group of African SFF authors from around the world to discuss how best to serve their needs. The group includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cherylsntumy.wordpress.com/" rel="noopener noreferrer"&gt;Cheryl Ntumy&lt;/a&gt;: Ghana-based SFF author and founding member of Petlo Literary Arts, a non-profit that develops and promotes creative writing from Botswana. She is also a founding member of the Sauútiverse Collective, an African speculative fiction collaborative worldbuilding project.
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.dilmandila.com/" rel="noopener noreferrer"&gt;Dilman Dila&lt;/a&gt;: Ugandan writer, film maker and a social activist. His books include Where Rivers Go To Die, which was shortlisted for the PKD Awards (2024), his films include, “What Happened in Room 13”, has been watched more than 8 million times online. He is currently working on his third feature film, about decentralized governance.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ivorhartmann.blogspot.com/" rel="noopener noreferrer"&gt;Ivor Hartmann&lt;/a&gt;: Zimbabwean writer, editor, publisher, and visual artist. Awarded The Golden Baobab Prize (2009), finalist for the Yvonne Vera Award (2011), selected for The 20 in Twenty: The Best Short Stories of South Africa’s Democracy (2014), awarded third in the Jalada Prize for Literature (2015), and Nommo Awards nomination (2017). He also runs the StoryTime micro-press, publisher of the African Roar and AfroSF series of anthologies, and most recently ZamaShort.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://africa-alliance.org/ayodele-arigbabu" rel="noopener noreferrer"&gt;Ayodele Arigbabu&lt;/a&gt;: is a writer, architect and creative technologist with close to two decades of experience in delivering projects at the intersection of architecture, design, film, digital media, publishing, event production and curation, as well as in the development of digital and interactive arts and applications. His short story "You Live to Die Once" won the 2001 Liberty Bank Short Stories Prize; his poem "Livelihood" got an honourable mention at the 2003 Muson Poetry competition. His stage play Moremi: The Legend Retold was staged in December 2003 at the University of Lagos Main Auditorium to an appreciative audience, and went on to be performed in Oklahoma and at the National Theatre of Nigeria.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://suyidavies.com/" rel="noopener noreferrer"&gt;Suyi Davies Okungbowa&lt;/a&gt;: award-winning author of fantasy, science fiction and general speculative fiction. He has published various novels for adults, the latest of which is Son of the Storm (Orbit, 2021, first in the epic fantasy trilogy), The Nameless Republic (the second book in the series, Warrior of the Wind, 2023). His debut novel, David Mogo, Godhunter (Abaddon, 2019) won the 2020 Nommo Award for Best Speculative Novel.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://judeumeh.wordpress.com/" rel="noopener noreferrer"&gt;Jude Umeh&lt;/a&gt;: published author and thought leader on the intersection of emerging technology and Intellectual Property on Digital Content and Rights, Umeh currently works as a Director of AI Business Strategy with Salesforce Professional Services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Much of what follows is influenced by the guidance of this group. Many of them expressed reservations that they could understand or contribute to the tech, but I pointed out that it's not their job to understand the tech. It's my job to understand their needs and then reflect that back to them using the tech.&lt;/p&gt;

&lt;h2&gt;
  
  
  What impact does the project have on your perception of digital financial inclusion?
&lt;/h2&gt;

&lt;p&gt;I feel more encouraged after this past year than I was at its outset. I have entered my Ambassadorship with a "show, don't tell" strategy, preferring to build and demonstrate what I'm doing than to simply talk about it. The more I engage, listen and learn, the more viable my approach for federated, collaborative social marketplaces feels.&lt;/p&gt;

&lt;p&gt;Certainly, there is a long way to go, but the solutions offered need to be better than what exists, not be perfect on the first attempt.&lt;/p&gt;

&lt;p&gt;A lot rests on the success of the open-payments enabled payments providers and wallets, and these are still in a very early stage. However, if we don't also have the ability to rapidly implement webshops using open payments, then that time and investment will remain unfulfilled.&lt;/p&gt;

&lt;p&gt;Until then, the best hope for the future is to keep building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Impact &amp;amp; Target Audience(s)
&lt;/h2&gt;

&lt;p&gt;There is something in innovation I call the Milk Problem.&lt;/p&gt;

&lt;p&gt;Everyone knows what milk is. If I bring you a new type of milk, I don't need to explain what it is, just tell you how it is different from other milk and why you should buy it.&lt;/p&gt;

&lt;p&gt;Push your innovation further out, and you end up in a place where no-one knows what it is you're offering them. You first have to explain everything about that thing before you can get around to the product itself and why people should buy it.&lt;/p&gt;

&lt;p&gt;Hop Sauna is not "milk". It is a framework for a new type of federated social commerce which doesn't exist formally as generally-available software. Sure, it builds on social publishing and leans on existing commercial models, but it is complex to explain quickly. People don't necessarily have a mental model for it.&lt;/p&gt;

&lt;p&gt;My author's group discussed ways in which they want to be paid and I then structured this into a set of archetypes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;One-off&lt;/strong&gt;: a payment - usually of purchase and sale - between a buyer and seller which happens once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recurring&lt;/strong&gt;: a payment - usually a subscription, or structured fee - between a buyer and seller.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conditional&lt;/strong&gt;: a payment which is conditional on some additional factor - e.g. the seller is required to perform some task, or a threshold of purchases are required - before funds are transferred.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This covers everything from an Amazon purchase, to Spotify streaming subscriptions, to Kickstarter campaigns or even an Uber taxi trip.&lt;/p&gt;

&lt;p&gt;There are a myriad of these types of requirements. Each of them needed to be documented, understood, mapped to methods in Open Payments or ActivityPub, and then represented in ways that can be understood by developers, and potential buyers and sellers using that software.&lt;/p&gt;

&lt;p&gt;The challenge is many developers have little experience of the challenges of the financially excluded, and the financially excluded don't often get a voice in the technical solutions developed on their behalf.&lt;/p&gt;

&lt;p&gt;Hop Sauna needs to integrate this dynamic by default without causing anyone to realise they have no idea what milk is.&lt;/p&gt;

&lt;p&gt;I think I'm getting there. &lt;/p&gt;

&lt;h2&gt;
  
  
  Communications and Marketing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2025 Interledger Summit Mexico City
&lt;/h3&gt;

&lt;p&gt;I presented the first release of Hop Sauna at the 2025 Interledger Summit in Mexico City, as well as charting out where we are, and the sorts of apps I'd like to see arising from my work:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/lt6rJpJ7a94?start=3126"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As examples, consider cooperative versions of Uber, where groups of drivers regulate and manage themselves, and have a direct relationship with their passengers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/ea8EZBWF7aXhOBhdNsTAsJyzj_EjpmBvg_vcZXj50Gk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2t2b2ZidnVx/cmJ4MWpieGp5bG5l/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/ea8EZBWF7aXhOBhdNsTAsJyzj_EjpmBvg_vcZXj50Gk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2t2b2ZidnVx/cmJ4MWpieGp5bG5l/LnBuZw" alt="Uber alternative" width="421" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or a Booking.com alternative, where guesthouses and hotels don't have to pay intermediaries to handle their bookings for them:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/WdP4S_Zk7vty47HVJLuDVnVpoB3_fd9wC_HqXDMTJnU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2s4aXhjMG1w/eHNhOGxwbm51a2Zm/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/WdP4S_Zk7vty47HVJLuDVnVpoB3_fd9wC_HqXDMTJnU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2s4aXhjMG1w/eHNhOGxwbm51a2Zm/LnBuZw" alt="Booking.com alternative" width="421" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2025 Interledger Hackathon Mexico City
&lt;/h3&gt;

&lt;p&gt;I was a mentor at the hackathon, during which teams attempted to build new applications for open payments. This gave me first-hand experience of the types of challenges developers face when using open payment development kits, and also to gauge interest in more enhanced features for the toolkits.&lt;/p&gt;

&lt;p&gt;I also had the rare pleasure of having two teams adopt Hop Sauna during the event and deliver completed submissions using it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/w6qOcib1SETsVPi1iPuSJfsvNyRa2PdAY_CRT6xfz1Q/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2lyMm5pZmNu/emZzdHB2NXF5OWFy/LkpQRw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/w6qOcib1SETsVPi1iPuSJfsvNyRa2PdAY_CRT6xfz1Q/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2lyMm5pZmNu/emZzdHB2NXF5OWFy/LkpQRw" alt="6am, an hour before deadline, a team successfully completes their app using Hop Sauna" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Podcast
&lt;/h3&gt;

&lt;p&gt;Cheryl, Dilman, &lt;a href="https://www.jeremiahlee.com/" rel="noopener noreferrer"&gt;Jeremiah Lee from the Foundation&lt;/a&gt;, and I collaborated on a &lt;a href="https://podcast.interledger.org/@InterledgerSalon/episodes/unlocking-possibilities-the-role-of-open-payments-in-african-speculative-fiction" rel="noopener noreferrer"&gt;podcast, published in December last year&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This covers much of the ground we considered during my Ambassadorship, ranging from the cascade of transactions necessary to get money from publishers in the US into the hands of authors, to platform exclusion, to the multiple ways we need to borrow identities from others with more privilege just to get things done.&lt;/p&gt;

&lt;p&gt;I have also begun engaging with some of the key developers and infrastructure designers of the fediverse to ensure that Hop Sauna plays well with others.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/A6gcEQawtgBzYT9IAi4P11oOjI3XKzsjy9EhnJorUWg/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3RoanNxajRv/ODZkN3ZjNTlkM3My/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/A6gcEQawtgBzYT9IAi4P11oOjI3XKzsjy9EhnJorUWg/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3RoanNxajRv/ODZkN3ZjNTlkM3My/LmpwZw" alt="Hop Sauna plays well with others, presented by Gavin at the Interledger Summit" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hop Sauna extension
&lt;/h3&gt;

&lt;p&gt;There is an initial set of moderation hooks, and I will be focusing on extending this in Q1 2026.&lt;/p&gt;

&lt;p&gt;My working plan so far is to implement the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Site level:

&lt;ul&gt;
&lt;li&gt;Universal block lists&lt;/li&gt;
&lt;li&gt;Geotargeting - fairly blunt, but using IP addresses to isolate "bad places"&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Actor level:

&lt;ul&gt;
&lt;li&gt;Individual blocks/mutes at site and actor level&lt;/li&gt;
&lt;li&gt;No reply except for direct messages, and limit direct messages by default to followers only (they can open this, if they wish)&lt;/li&gt;
&lt;li&gt;Detachable reposts for the inevitable shark-baiting. Given many stacks aren't yet implementing this. I figure it works more like a mute.&lt;/li&gt;
&lt;li&gt;Geotargeting of individual actors with options to block, show different information, or offer a different price. This works on multiple levels from the way I was originally thinking of it (offering books for sale only in certain countries) to the limitation of products which may be illegal outside of certain jurisdictions.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This mostly reduces clutter and problems on the commercial server, but a repost on another server probably supports replies there. Again, a functional mute on main.&lt;/p&gt;

&lt;p&gt;The more specific commercial limits will be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Dunbar's_number" rel="noopener noreferrer"&gt;Dunbar's limit&lt;/a&gt; of 150 creators (any number of supporters, depending on the type of service - on my Kindle-alt, readers have accounts to store and read their books).&lt;/li&gt;
&lt;li&gt;All Actor Services (products) can only be made public by an approved editor with those rights.&lt;/li&gt;
&lt;li&gt;Messages are all generated, meaning you can only send a message to your followers when a specific type of action occurs (publishing a book, offering a period-limited discount, that sort of thing).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that stops individuals reposting their product announcements on their other socials, but there they would be limited by the rules of those communities.&lt;/p&gt;

&lt;p&gt;The Dunbar's limit includes an extension - the stack name cannot be a creator server. As example, the Kindle-alt will be called Qwyre (rebuilding my existing &lt;a href="https://qwyre.com" rel="noopener noreferrer"&gt;https://qwyre.com&lt;/a&gt; which stopped working commercially when Coil was closed, but still works perfectly well as my main ereader). Qwyre itself will be closer to something like JoinMastodon, and eventually become a federated search engine for books from all the different imprints (servers).&lt;/p&gt;

&lt;p&gt;I think it's critical to ensure distribution and discovery to prevent agglomeration effects that overwhelm safe communities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qwyre for self-publishing
&lt;/h3&gt;

&lt;p&gt;I have a new novel. My intention is to dust off the old &lt;a href="https://qwyre.com" rel="noopener noreferrer"&gt;Qwyre&lt;/a&gt; and build a new Kindle-alternative where I can collaboratively publish my work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Support
&lt;/h2&gt;

&lt;p&gt;I would appreciate the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code review&lt;/li&gt;
&lt;li&gt;Feature development&lt;/li&gt;
&lt;li&gt;Internationalisation&lt;/li&gt;
&lt;li&gt;UX / UI&lt;/li&gt;
&lt;li&gt;Unicorn Hunters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This latter is aimed at developers who want to build on Hop Sauna with apps targeting existing monopoly commercial tech, like Amazon, Kickstarter, Uber, etc.&lt;/p&gt;

&lt;p&gt;Oh, and funding. I'm always looking for funding to support continued development.&lt;/p&gt;

&lt;p&gt;I wish to thank Interledger for their support, and look forward to everyone's thoughts on progress to date.&lt;/p&gt;

</description>
      <category>progressreport</category>
      <category>ilfambassadorfinalreport</category>
    </item>
    <item>
      <title>Hop Sauna — ILF Ambassador Progress Report</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Wed, 16 Jul 2025 17:35:54 +0000</pubDate>
      <link>https://community.interledger.org/hopsauna/hop-sauna-ilf-ambassador-progress-report-35oa</link>
      <guid>https://community.interledger.org/hopsauna/hop-sauna-ilf-ambassador-progress-report-35oa</guid>
      <description>&lt;p&gt;&lt;a href="https://codeberg.org/whythawk/hop-sauna" rel="noopener noreferrer"&gt;&lt;em&gt;Hop Sauna&lt;/em&gt;&lt;/a&gt; is a core technical stack aimed at developers to support implementing a federated, community-moderated web shop offering custom digital objects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/yXKyT-KBCyYSswvTa9jsPgs2xdnxitORURExz6WvIsw/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3Rta3F3OTVi/dzdxbzdkcG01b21y/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/yXKyT-KBCyYSswvTa9jsPgs2xdnxitORURExz6WvIsw/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3Rta3F3OTVi/dzdxbzdkcG01b21y/LmpwZw" alt="Sample landing Hop Sauna landing page featuring generated creator profiles" width="800" height="765"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Update
&lt;/h2&gt;

&lt;p&gt;Any system permitting commercial exchange exists at the intersection of what is technically possible, ethically responsible, and permissible under the patchwork of international regulatory compliance.&lt;/p&gt;

&lt;p&gt;Building a commercial website is relatively straightforward. Frameworks - like Django or Drupal - exist. You can integrate Stripe or PayPal quite quickly. But, once you're done, such a site offers little advantage over listing your products on Amazon or eBay, with the huge downside of being functionally invisible to the larger world. If you're already famous, moving to your own infrastructure may have advantages, but if you're less known, or just starting out?&lt;/p&gt;

&lt;p&gt;But, what happens if you live in a part of the world not supported by these platforms, or are just tired of the ethical and business practices of the platforms?&lt;/p&gt;

&lt;p&gt;A functional, community-oriented commercial platform would have the following characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A community of peers, selling similar or compatible works, where membership is a function of acceptance by that community,&lt;/li&gt;
&lt;li&gt;Review of business behaviour (or moderation of activity) is by the community, ensuring that members are held to agreed norms,&lt;/li&gt;
&lt;li&gt;Costs are kept to the minimum with members paying for shared infrastructure, but otherwise keeping their own revenue, or contributing in some agreed way to their community.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each community would also interoperate with other communities which offer similar works and where their community standards are compatible. This interoperability ensures collective discovery, much like the &lt;a href="https://en.wikipedia.org/wiki/Webring" rel="noopener noreferrer"&gt;webrings&lt;/a&gt; of the early internet when search was still in its infancy. Discovering one link in the network provides access to a wider world.&lt;/p&gt;

&lt;p&gt;A single website can be structured however the developer chooses, but the moment information needs to be exchanged between sites - federated - interoperability standards become critical. It would also be helpful if that standard already supported interactions between millions of people.&lt;/p&gt;

&lt;p&gt;Or, to summarise, it would be nice to go where the tea is already being made.&lt;/p&gt;

&lt;p&gt;And these then are the objectives of my project.&lt;/p&gt;

&lt;p&gt;Building a website based on intrinsic interoperability means that one is never building in isolation. Federation requires a set of standardised methods for services sharing the same protocol to query and interpret data from each other.  &lt;a href="https://www.w3.org/TR/activitypub/" rel="noopener noreferrer"&gt;ActivityPub&lt;/a&gt; and &lt;a href="https://www.w3.org/TR/activitystreams-core/" rel="noopener noreferrer"&gt;ActivityStreams&lt;/a&gt; provide the protocols we need, but these exist side-by-side with the ordinary protocols of the site itself.&lt;/p&gt;

&lt;p&gt;To contextualise this, think of email. Where email fails is that there is no way to moderate who gets to email you. So you get spam. ActivityPub enforces &lt;em&gt;authenticated fetch&lt;/em&gt;, meaning people have to prove who they are before they can send you a message. That process of being able to send authenticated messages between authenticated actors (people) can be used for anything, and the first use-case is social media (think &lt;a href="https://joinmastodon.org/" rel="noopener noreferrer"&gt;Mastodon&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The main challenge for anyone building from scratch is that, while the protocols themselves are &lt;a href="https://github.com/boyter/activitypub/blob/main/announce-post.md" rel="noopener noreferrer"&gt;well-documented&lt;/a&gt;, you need test servers to send messages to so that you can see if your software is capable of two-way communication. Fortunately, &lt;a href="https://activitypub.academy/" rel="noopener noreferrer"&gt;ActivityPub.Academy&lt;/a&gt; by &lt;a href="https://mastodon.social/@crepels" rel="noopener noreferrer"&gt;Sebastian Jambor&lt;/a&gt; exists. But a lot of stuff isn't clear, and ActivityPub itself is like a Matryoshka doll of nested metadata which needs to be tested before it can be used.&lt;/p&gt;

&lt;p&gt;An image can be listed as a URI reference, or as a data structure where the link can be named "url" or "href". And then there's the undocumented best-practices, like how exactly should you implement authenticated fetch safely?&lt;/p&gt;

&lt;p&gt;Say you have a person on your website who has chosen to be unlisted and protect their privacy. You get a request from an external website to interact with this person. Authenticated fetch requires a named "actor" to send back a query to the sending website, allowing each website to authenticate the other. Site &lt;em&gt;Albert&lt;/em&gt; sends a message from &lt;em&gt;Bradley&lt;/em&gt; to your website &lt;em&gt;Clarice&lt;/em&gt; to the person &lt;em&gt;Dominique&lt;/em&gt;. Which identity should respond to &lt;em&gt;Bradley&lt;/em&gt;? If this is a wild spam message designed to check if such an identity exists prior to attempting some scam, sending a message back from &lt;em&gt;Clarice&lt;/em&gt; would confirm that their identity exists without yet confirming who &lt;em&gt;Bradley&lt;/em&gt; is, or if they are who they say they are, or whether &lt;em&gt;Dominique&lt;/em&gt; even wants to talk to them.&lt;/p&gt;

&lt;p&gt;In Amazon or Facebook, if you want to send a message to another person on the same site, you both have to be logged in. It's a relatively straightforward problem.&lt;/p&gt;

&lt;p&gt;With ActivityPub, an undocumented solution is to create a universal site actor which acts as an agent for all identities on this initial approach. Once &lt;em&gt;Bradley&lt;/em&gt; is verified, and you can then test if &lt;em&gt;Dominique&lt;/em&gt; is open to contact from &lt;em&gt;Bradley&lt;/em&gt;, only then do you permit &lt;em&gt;Dominique's&lt;/em&gt; identity to be exposed to this external identity.&lt;/p&gt;

&lt;p&gt;And so it goes.&lt;/p&gt;

&lt;p&gt;Each interaction requires solving two sets of problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do you get this code to do what it is supposed to do? Not a trivial problem.&lt;/li&gt;
&lt;li&gt;How do I design this so that my community members are kept safe, and are able to keep themselves safe? Even more challenging in the absence of mature systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have also implemented multi-language support - both statically in the app, and dynamically in text provided by creators and admins (i.e. you could directly serve different translations via the same posts) - from the beginning.&lt;/p&gt;

&lt;p&gt;This is very slow going. &lt;/p&gt;

&lt;p&gt;However, when you get it right, you suddenly get a whole bunch of things working at once.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/69Z8EfjKW7ArHTKIMU4XnZQHGJuANPltCmBo7qJ9UW8/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzEwaDJ0b28y/dHZzcWo0Ynp1d3U0/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/69Z8EfjKW7ArHTKIMU4XnZQHGJuANPltCmBo7qJ9UW8/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzEwaDJ0b28y/dHZzcWo0Ynp1d3U0/LmpwZw" alt="My profile info from Wandering.shop displayed on Hop Sauna" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This image displays &lt;a href="https://wandering.shop/@GavinChait" rel="noopener noreferrer"&gt;my Wandering.shop profile&lt;/a&gt; on the Hop Sauna build. To get what may seem intrinsically simple running requires everything else - authenticated fetch, appropriate &lt;em&gt;webfinger&lt;/em&gt; endpoints - to be working.&lt;/p&gt;

&lt;p&gt;Hopefully this demonstrates the value of these protocols to a community-based webshop - you can know your customer, moderate who gets to be a customer, and maintain relationships with your customers.&lt;/p&gt;

&lt;p&gt;And so, my update is that progress is happening, but in bursts. Along the way I have also kept an eye on the &lt;a href="https://github.com/interledger/open-payments-python-sdk" rel="noopener noreferrer"&gt;Python Open Payments SDK&lt;/a&gt; which I will need as I head deeper into implementing web monetization and payments. Now, payments themselves are a complex set of products. Simple purchases are complemented with subscriptions, milestone-based fundraising, and other variations. Some are artifacts of the way credit cards work and aren't directly transferable to open payments.&lt;/p&gt;

&lt;p&gt;I have convened a small group of African authors to discuss the requirements they have from a federated platform, and many of the initial suggestions raise additional questions. Take implementing a Kickstarter-style fundraiser. Credit cards allow for deferred payments, a mechanism largely developed because the high cost of transactions means that reversing a charge creates such a cascade of fees that it makes the whole thing impossible. Instead, a vendor can put a "hold" on a promise to pay, and then keep that until some criteria are met (or not) then either release the hold, or execute the transaction.&lt;/p&gt;

&lt;p&gt;Open payments has no such method, because it doesn't need it. But, without a hold, how do you run a criteria-based fundraiser? Well, you need an escrow service. Some third-party which will hold the funds until such time as the criteria are met, or not, and then release or return the funds. And now we've crossed from a technical software problem to a regulatory one.&lt;/p&gt;

&lt;p&gt;An &lt;a href="https://en.wikipedia.org/wiki/Affordance" rel="noopener noreferrer"&gt;affordance&lt;/a&gt; is a set of possible actions which arise from the design of a thing. You may wish people to use the thing you made in specific ways, but if your design also permits other ways of acting then you can be sure those ways will be tested. And, unfortunately, affordances that permit bad behaviours impose an external cost on our communities. These &lt;a href="https://en.wikipedia.org/wiki/Externality" rel="noopener noreferrer"&gt;externalities&lt;/a&gt; - like spam from email, or racist autonomous language models - arise naturally because of design decisions that, while technically clever, didn't take into account ethical and regulatory responsibilities.&lt;/p&gt;

&lt;p&gt;Which is a longish way of saying that the creator group is raising requirements that require an ethical and technical assessment, and why Kickstarter-style fundraising will not be supported in the first software release until such time as there are mature escrow services to support the function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progress on Objectives, Key Activities
&lt;/h2&gt;

&lt;p&gt;The key goals are time-sequenced.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Goal 1&lt;/strong&gt;: deliver a template developer stack ready for custom creation of creator community-moderated Open Payments-enabled web shops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goal 2&lt;/strong&gt;: develop marketplace tools for facilitating buyer and seller exchanges, including for webhooks for responding to successful purchases, disputes and refunds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goal 3&lt;/strong&gt;: kick-start cooperative creator marketplaces through engaging with stakeholders to align the template stack with their needs, and to develop a supportive environment for project sustainability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Goal 1&lt;/strong&gt; is about 80% complete. This has taken quite a lot longer than I initially hoped largely owing to limited documentation. I spent a &lt;em&gt;lot&lt;/em&gt; of time reading through other people's implementations in programming languages that are quite foreign to me (or - given coding is deeply personal - using approaches in languages I'm familiar with that are as foreign). &lt;a href="https://codeberg.org/superseriousbusiness/gotosocial" rel="noopener noreferrer"&gt;GoToSocial&lt;/a&gt; is a tremendous project, written in Go, that I have trawled through for database structure and various solutions. The &lt;a href="https://codeberg.org/bovine" rel="noopener noreferrer"&gt;Bovine&lt;/a&gt; project is also key to my work, with a lot of undocumented features necessitating constant review of the code.&lt;/p&gt;

&lt;p&gt;Along the way, I had to "solve" quite a few seemingly non-standardised decisions. Take hashtags, now in common use for theme discovery. Very few implementations support UTF8, meaning that Japanese or Arabic hashtags aren't universally supported. I support this, but I balked at supporting emoji-based hashtags.&lt;/p&gt;

&lt;p&gt;What remains to be done is moderation. However, everything in ActivityPub is a message which then gets acted upon depending on the type of message it is. Moderation requests and responses are types of messages that have an impact on how the targeted identity gets to behave (banned, muted, time-outs), and deciding on what is appropriate, and what flexibility of action to permit site administrators, is itself a set of affordances with its own considerations of externalities.&lt;/p&gt;

&lt;p&gt;There is also a newly-proposed &lt;a href="https://www.fediscovery.org/" rel="noopener noreferrer"&gt;fedidiscovery&lt;/a&gt; standard which is intended to support general search and discovery across federated websites. I will need to explore this with a view to ensuring support. The current standard envisages centralised "hubs" which act to harvest data from sites which opt in, and against which other services can then run queries in an optimised way. This would require an additional "hop sauna search" instance and is currently out of scope.&lt;/p&gt;

&lt;p&gt;I am due to begin &lt;strong&gt;Goal 2&lt;/strong&gt; development this month, and have been talking through the availability of APIs and methods for implementation with the kind help of Uchi Uchibeke at &lt;a href="https://chimoney.io/" rel="noopener noreferrer"&gt;Chimoney&lt;/a&gt;. There are currently only a small number of financial services supporting open payments, although more will be joining as Interledger-supported projects launch. As I get deeper into this objective, I'll build a list and try assess their level of maturity to include in the developer documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal 3&lt;/strong&gt; is to keep the deliverable "honest". I have invited a small group of African speculative fiction authors to discuss what a self-publishing "imprint" would require to support creators. It's far too easy to deliver software which solves problems but in ways that are impenetrable for non-technical people to use. When the software can be easily understood and used by this group, then I will know it can do the job. However, their requirements also raise areas where - even with the best of intentions - open payments financial accessibility will fall short. This has surfaced a needed output for the project where I map existing types of creator financial services (auctions, markets, conditional fundraisers, etc) into open payments equivalents, highlighting where services may not yet support implementation. This group will continue discussions till the end of the project with a potential workshop still to be organised at the end.&lt;/p&gt;

&lt;h2&gt;
  
  
  What impact does the project have on your perception of digital financial inclusion?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Hop Sauna&lt;/em&gt; is not intended to reveal or explore problems of financial inclusion, but to build a developer toolkit to support inclusion. With that caveat, exploring implementation does reveal what is still lacking. At this stage, open payments can handle the core transactional requirements of a webshop - one-off payments and subscriptions - but will require intermediaries to support criteria-based fundraising.&lt;/p&gt;

&lt;p&gt;There are also interoperability requirements that can limit cross-border payments (or, increase the costs) even with open payments. The &lt;a href="https://papss.com/" rel="noopener noreferrer"&gt;Pan-African Payment and Settlement System (PAPSS)&lt;/a&gt; is a pan-African initiative which appears equivalent to the EU open-banking approach that has massively reduced costs and complexity for doing banking across Europe. But ... it is fairly new and a long way from full implementation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hop Sauna&lt;/em&gt; will be released into what is still an immature implementation environment. While mindful of extending inclusion, I also need to ensure that it supports what absolutely will work right now, rather than what might be available in future.&lt;/p&gt;

&lt;p&gt;Plus, there are the affordances of open payments which remain as-yet unexplored. Once you can make microscopic, rapid peer-to-peer payments, what types of services or opportunities may yet emerge? Something for our creator group to consider...&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Impact &amp;amp; Target Audience(s)
&lt;/h2&gt;

&lt;p&gt;There's an awesome patisserie near where I live. They serve incredibly creative confectionaries; each a flavour journey. And it's all vegan.&lt;/p&gt;

&lt;p&gt;This isn't something you'd know unless you went looking to find out. The objective of the people behind this delight is show not tell. I'm the exact type of person they're targeting ... someone who would never consider vegan by default, but am perfectly happy to be educated if it comes in an accessible way.&lt;/p&gt;

&lt;p&gt;Until there is widespread adoption of open payments in commercial interactions, the opportunity to impact financially-excluded communities will remain notional. Or, until the tea is made &lt;em&gt;where excluded communities are&lt;/em&gt; we're not succeeding. &lt;em&gt;Hop Sauna&lt;/em&gt; is not a destination along that path, but a key enabler to ensure it is easier for developers to create accessible products which happens to support using open payments by default. My objective isn't to tell developers that open payments makes markets more accessible, but to build a compelling suite of tools that speeds up their development process and supports widespread adoption without open payments being experienced as anything other than a straightforward payments implementation.&lt;/p&gt;

&lt;p&gt;Importantly, this is a moving target. &lt;a href="https://mastodon.social/@pixelfed/114817157970310394" rel="noopener noreferrer"&gt;PixelFed&lt;/a&gt;, the federated photo-sharing app, has announced an intention to develop micropayments support using Web Monetization. This is likely to be the first major, and mainstream, implementation that will hit the fediverse. That massively raises the exposure of open payments, while also surfacing any potential community concerns which I can account for (or try to mitigate) as I develop.&lt;/p&gt;

&lt;p&gt;The impact of what &lt;em&gt;Hop Sauna&lt;/em&gt; does, and what I achieve through this ambassadorship, will take time and I expect to be held to these objectives over the long-term.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communications and Marketing
&lt;/h2&gt;

&lt;p&gt;So far, I've kept this very quiet. Sausage-making is not an attractive process, and technical development is not much of a spectator sport. I have written on some &lt;a href="https://community.interledger.org/hopsauna/the-battle-between-the-commute-and-the-community-or-the-strangeness-of-online-monopolies-2olf"&gt;initial development considerations&lt;/a&gt; and - once I release the first &lt;em&gt;Hop Sauna&lt;/em&gt; ActivityPub build - I'll start writing more frequently on what I'm doing.&lt;/p&gt;

&lt;p&gt;This will initially be with developers and then, as I get deeper into developing commercial products, with creators and creator communities.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;From this month, &lt;strong&gt;Goal 2&lt;/strong&gt; takes priority till the end of the project, with implementation of open payments and the various mechanisms to create products and methods for payments. A creator should be able to create a product, then associate regional / currency and price data to their product. The software needs to handle refunds, complaints and time-bound price discounts.&lt;/p&gt;

&lt;p&gt;I will also - once the demo version is near completion - facilitate a workshop with the authors collaborating with me on the needs assessment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Support
&lt;/h2&gt;

&lt;p&gt;I would appreciate technical code reviews on the template stack (Python/FastAPI and TypeScript/NuxtJS), as well as help with the Open Payments SDK. Also, non-technical, looking for members of cooperatives or creative communities working collaboratively on creative commercial activities, discussing their needs so I can ensure that the template has them covered.&lt;/p&gt;

&lt;p&gt;Also, if anyone reading specialises in developing user interfaces at the intersection of commerce and social media and has ideas, as well as experience with Nuxt/Tailwind, please get in contact. I may have some budget to pay for your time to help sort out the frontend interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/kQrnX6ct66A5Vi59x4S5RemEl4hALfCHn8KkRg62fwQ/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzQ4MnZ5bTBk/cWVzM2IxYTJhZHBt/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/kQrnX6ct66A5Vi59x4S5RemEl4hALfCHn8KkRg62fwQ/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzQ4MnZ5bTBk/cWVzM2IxYTJhZHBt/LmpwZw" alt="Generated creator profile on Hop Sauna build" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>progressreport</category>
      <category>ilfambassadorreport</category>
    </item>
    <item>
      <title>The battle between the Commute and the Community, or the strangeness of online monopolies</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Sat, 24 May 2025 09:46:23 +0000</pubDate>
      <link>https://community.interledger.org/hopsauna/the-battle-between-the-commute-and-the-community-or-the-strangeness-of-online-monopolies-2olf</link>
      <guid>https://community.interledger.org/hopsauna/the-battle-between-the-commute-and-the-community-or-the-strangeness-of-online-monopolies-2olf</guid>
      <description>&lt;p&gt;If the town where you live was more like the internet, there would be one large department store where hundreds of vendors, all selling more-or-less the same things, would all be yelling at once while waving long lists of customer ratings to persuade you to buy from them. When you go through to pay, the department store - whose employees you never see - would extract anywhere between 30% to 70% of the price as their cut before passing the rest onto the vendor.&lt;/p&gt;

&lt;p&gt;There would be no charismatic little streets with cafes spilling onto the pavement, or the bustle of people chatting, laughing and living. Instead, what you'd have is commerce stripped to its essence, "red in tooth and claw".&lt;/p&gt;

&lt;p&gt;The challenge for any community recovering from this centralisation is how difficult it is change behaviours when there is no - seeming - compelling reason to do so. Unless something dramatic happens.&lt;/p&gt;

&lt;p&gt;In 2020, with COVID, many of us got to experience that change. Amidst the pain, we also experienced a liberation of community. Restaurants spilling onto pavements. Shopping close to home. Living in community. And we liked it. Since then our battle between the Commute and the Community has become polarising and heated.&lt;/p&gt;

&lt;p&gt;Gradually, perhaps imperceptibly, this battle has also been coming to the internet through federated, interoperable social media like Mastodon, BlueSky and their ilk. Hop Sauna, my 2025 Ambassadorship, is intended to push this further into the commercial space, recovering our online economic communities.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/svy_6ItuiGXBhKncNEIYNemzs2pUbigvJMcLjp-K3Iw/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3Bpa3Ezc3Vt/dG0xbG12d2g3eGk5/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/svy_6ItuiGXBhKncNEIYNemzs2pUbigvJMcLjp-K3Iw/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3Bpa3Ezc3Vt/dG0xbG12d2g3eGk5/LmpwZw" alt="Draft landing page for the new Hop Sauna community webshop" width="800" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does a webshop need to do?
&lt;/h2&gt;

&lt;p&gt;Anyone can launch a new webshop; barriers to entry are low, but barriers to success are enormous.&lt;/p&gt;

&lt;p&gt;Any commercial stack must support two key behaviours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discovery&lt;/strong&gt; is the opportunity to find the beings and doings of others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery&lt;/strong&gt; is the opportunity to express our being through our doings, or to enjoy the beings and doings of others. To be creative or legitimately acquire the creativity of others.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can't rely on search engines to help you. You need - in the words of Raymond Ackerman - to "go where the tea is being made". To be part of what people already do.&lt;/p&gt;

&lt;p&gt;Engaging with people where they already have community must play well with that community - creators, buyers and sellers, makers and users, readers and writers - and ensure their safety by ensuring:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple and ethical rules which apply to all, ensuring diversity, equity and inclusion,&lt;/li&gt;
&lt;li&gt;Respect for the privacy and protection of its community,&lt;/li&gt;
&lt;li&gt;Protection of its community from force or fraud,&lt;/li&gt;
&lt;li&gt;Clear, transparent and fair means of discovery,&lt;/li&gt;
&lt;li&gt;Simple and low-cost means of delivery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An accessible &lt;strong&gt;federated&lt;/strong&gt; stack must also offer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple ways to deploy the same stack independently,&lt;/li&gt;
&lt;li&gt;Tools for migration between stacks without losing supporters or anything purchased,&lt;/li&gt;
&lt;li&gt;Equal opportunity for discovery across a federated network of equivalent services, subject to adherence to a common set of ethical rules,&lt;/li&gt;
&lt;li&gt;Moderation tools to support enforcement of ethical rules, whether the moral subject is an individual, or an entire other server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why is federation necessary to compete with monopolies?
&lt;/h2&gt;

&lt;p&gt;Any &lt;strong&gt;centralised&lt;/strong&gt; stack (be it Facebook, Amazon, eBay, Uber, AirBnB or Etsy) offers both discovery and delivery. But, they are also &lt;em&gt;rent-seeking&lt;/em&gt;, in that they extract most of the value created by their creators, and do very little to uphold ethical values. Ethics are an expense and, in any case, controversy is good for "engagement".&lt;/p&gt;

&lt;p&gt;They're equivalent to feudal landlords, charging their tenant farmers a significant share of what they produce and holding to themselves the right to turf any of their tenants off the land at any time, for any reason, rendering them instantaneously destitute.&lt;/p&gt;

&lt;p&gt;Given these online spaces are entirely virtual, how did we get to a place where land is scarce, and landlords can do what they will?&lt;/p&gt;

&lt;p&gt;The short answer is that - without ethics, standards and discipline - feudalism is inevitable. Learning why, and how it came to be, is crucial to understanding how to avoid it.&lt;/p&gt;

&lt;p&gt;The early internet of the 1990s was a wild place. Along with blogs and early commercial sites were random interactive experiments with a tendency to explode, and lots and lots of porn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discovery&lt;/strong&gt; was largely down to curated lists, &lt;a href="https://en.wikipedia.org/wiki/Webring" rel="noopener noreferrer"&gt;webrings&lt;/a&gt;, web bookmarks, and search engines. Any of these could be open to spoofing, and so clicking on any link ran the risk of ending up on a porn site, or crashing your computer as some poorly configured website generated infinite new pages.&lt;/p&gt;

&lt;p&gt;Yahoo offered curated search results. That's right, early Yahoo hand-curated every link, which scaled about as well as you can imagine it would.&lt;/p&gt;

&lt;p&gt;Google arrived with a new algorithmic approach: weighting links by the number of inbound references. Suddenly, for a brief while, you could find things and - with discovery stabilised - &lt;strong&gt;delivery&lt;/strong&gt; could really begin.&lt;/p&gt;

&lt;p&gt;Every company started offering their own online shops. With that, they needed to ensure discovery, and the advertising market of contextual links exploded. Unfortunately, so did "search engine optimisation", referral links, and "me-too" sites.&lt;/p&gt;

&lt;p&gt;Companies with economic heft could rise above the noise, but small startups had no way through the morass. Enter Amazon, eBay, Bookings and Facebook.&lt;/p&gt;

&lt;p&gt;The challenge of selling online is discovery and delivery. If, though, you went inside a gated mall then - in exchange for a significant share of your income - you got protection and exposure.&lt;/p&gt;

&lt;p&gt;Twitter and Facebook offered their sequential feed of people and services you were actually interested in by opting in to what they posted. Amazon, Booking, Uber and Etsy allowed you to buy things you could discover using a search engine that actually delivered on what you were looking for.&lt;/p&gt;

&lt;p&gt;And again, for a while, that worked. But exploitation soon followed.&lt;/p&gt;

&lt;p&gt;As before, the problem is "noise" which includes spam, chum, fraud, and hate speech. Worse, creators are deliberately trapped. Whether by relying on centralised systems of discovery (Google, Facebook, Twitter, Instagram) or delivery (Amazon, Uber, AirBnB, Etsy), where do you go?&lt;/p&gt;

&lt;p&gt;The noise of &lt;a href="https://en.wikipedia.org/wiki/Enshittification" rel="noopener noreferrer"&gt;&lt;strong&gt;enshittification&lt;/strong&gt;&lt;/a&gt; really took off with the acquisition of Twitter in 2022 and an opportunity arose from IndieWeb's &lt;a href="https://indieweb.org/POSSE" rel="noopener noreferrer"&gt;POSSE&lt;/a&gt; (Publish (on your) Own Site, Syndicate Elsewhere) philosophy when Mastodon's &lt;a href="https://www.w3.org/TR/activitypub/" rel="noopener noreferrer"&gt;ActivityPub&lt;/a&gt; implementation took off.&lt;/p&gt;

&lt;p&gt;Federation means that interoperable websites share a common way of communicating and sharing information. You can finally &lt;strong&gt;discover&lt;/strong&gt; and curate a community. More importantly, federation returned moderation decision-making to small communities.&lt;/p&gt;

&lt;p&gt;The rejoinder from the centralised stacks is that Mastodon is tiny, so why would you bother, but that's a bit like complaining that your favourite coffee shop only seats 20 while the huge department store can accommodate millions. Is that what you &lt;em&gt;want&lt;/em&gt; from a café?&lt;/p&gt;

&lt;p&gt;And so that is the task before me as I continue to build out Hop Sauna. A webshop that not only uses the open payments protocols to reduce the costs of exchange, but also offers communities an opportunity to moderate themselves while not losing the opportunity to be discovered.&lt;/p&gt;

&lt;p&gt;I would love to hear from you … what features and requirements do you have from a community-moderated and federated webshop?&lt;/p&gt;

</description>
      <category>ilfambassadorreport</category>
      <category>tech</category>
      <category>engineering</category>
      <category>ambassadors2025</category>
    </item>
    <item>
      <title>Hop Sauna, a social marketplace framework - introduction and roadmap</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Sun, 13 Apr 2025 13:51:58 +0000</pubDate>
      <link>https://community.interledger.org/hopsauna/hop-sauna-a-social-marketplace-framework-introduction-and-roadmap-2p9b</link>
      <guid>https://community.interledger.org/hopsauna/hop-sauna-a-social-marketplace-framework-introduction-and-roadmap-2p9b</guid>
      <description>&lt;p&gt;The online new is no longer absorbed into society with the unquestioning rapture of the 1990s. Neither should it be, especially when it comes to new ways to make and receive payments.&lt;/p&gt;

&lt;p&gt;Low-cost, low-fee transactions are not a "solved" problem in federated online communities. Every community server raises funds using PayPal, Patreon or various other "tip" portals rather than integrating payments directly into their systems, forfeiting significant fees along the way. Creators still sell on Amazon, or other centralised portals, while promoting their work via multiple social channels. And little of that is available to creators working and living in the global South.&lt;/p&gt;

&lt;p&gt;Nothing here is connected or integrated. All of it costs more than it needs to.&lt;/p&gt;

&lt;p&gt;The logic of open payments is that instead of every website using the extensive banking infrastructure of credit cards and an authentication process involving multiple fee-charging intermediaries, payments are instead made via interoperable digital wallets. Individuals manage cash in and out of their wallets in whatever ways their service-provider supports.&lt;/p&gt;

&lt;p&gt;The protocol for interacting with these wallets uses the open payments architecture, and each website must then manage interaction with these APIs. That often involves a lot of duplicated boilerplate code, not just at the transaction level, but also in defining and managing products and fees, authenticating payments are received, and then acting on that authentication.&lt;/p&gt;

&lt;p&gt;This is tedious even with mature and well-documented payment libraries, but in a new protocol can be confusing, frustrating, and a barrier to adoption. Such barriers - if the incentive is sufficient - often produce new centralised intermediaries to act as an interface to that complexity. Best to get in early with an open source alternative.&lt;/p&gt;

&lt;p&gt;The purpose of the Interledger Foundation Ambassadorships is to support early-stage efforts at building bridges into the adoption and use of the open payments protocols. My Ambassadorship is all about enhancing accessibility to a technical protocol that most shouldn't need to know about to experience value from it.&lt;/p&gt;

&lt;p&gt;Putting new payment systems into use is less about telling people, than putting it where they most need it as simply as possible.&lt;/p&gt;

&lt;p&gt;My project, &lt;strong&gt;Hop Sauna&lt;/strong&gt;, is an open-source template stack ready for custom development of community-moderated Open Payments-enabled web shops, including a base server and client, with documentation, to support rapidly creating new types of federated commercial social applications.&lt;/p&gt;

&lt;p&gt;My technical deliverables are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Payments web shop template stack.&lt;/li&gt;
&lt;li&gt;Open Payments Python SDK and template components.&lt;/li&gt;
&lt;li&gt;A dedicated documentation site for the template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first step along the way is a &lt;a href="https://codeberg.org/whythawk/base-fastapi-nuxt-project" rel="noopener noreferrer"&gt;base template stack&lt;/a&gt;, which is already available for use, along with all the documentation required to develop and extend it. &lt;/p&gt;

&lt;p&gt;This foundation will become Hop Sauna, which will integrate the Open Payments API, along with full digital product and purchase management. The objective is that sellers add their wallet / API keys and then the stack manages everything for them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Digital product creation, registration, and pricing (including multiple currencies),&lt;/li&gt;
&lt;li&gt;Purchase process between service providers (wallet services) for seller and buyer,&lt;/li&gt;
&lt;li&gt;Payment authentication and administration for the seller,&lt;/li&gt;
&lt;li&gt;Digital product distribution for the buyer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The template manages an exchange, authenticates that exchange and then approves distribution. The specific nature of what is distributed, and how, is a factor for the developer to build. It is their unique "product". Developers will not have to implement any part of the product or payments process.&lt;/p&gt;

&lt;p&gt;Effectively, it will be a foundation to many of the required coding challenges in the server and client, permitting standardised exchange of digital goods. &lt;/p&gt;

&lt;p&gt;Towards the end of my Ambassadorship, I will begin looking to the next phase of my work, which is ensuring that the stack supports the needs of creators. I will run a small creator's workshop where collaborators will discuss their particular requirements and allow me to ensure that the stack is able to fulfil these needs.&lt;/p&gt;

&lt;p&gt;If you want to get involved, I will need code reviewers, and - especially for the open payments SDK - technical collaborators to speed up development.&lt;/p&gt;

&lt;p&gt;If you have any questions or suggestions, please get in contact.&lt;/p&gt;

</description>
      <category>ilfambassadorreport</category>
      <category>tech</category>
      <category>engineering</category>
      <category>ambassadors2025</category>
    </item>
    <item>
      <title>'Empathy' - new African speculative fiction by Cheryl Ntumy on Qwyre.com</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Mon, 09 Jan 2023 08:24:07 +0000</pubDate>
      <link>https://community.interledger.org/qwyre/empathy-new-african-speculative-fiction-by-cheryl-ntumy-on-qwyrecom-1fa1</link>
      <guid>https://community.interledger.org/qwyre/empathy-new-african-speculative-fiction-by-cheryl-ntumy-on-qwyrecom-1fa1</guid>
      <description>&lt;p&gt;&lt;a href="https://qwyre.com" rel="noopener noreferrer"&gt;Qwyre&lt;/a&gt; is starting 2023 with the release of &lt;a href="https://qwyre.com/stream/bdce8427-c0b0-4fa5-9892-902ff34e88d4" rel="noopener noreferrer"&gt;&lt;strong&gt;Empathy&lt;/strong&gt;&lt;/a&gt;, a new work of African speculative fiction by Ghanaian writer, Cheryl Ntumy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/BDRU0cpmvZwNTit9mwgDCC87pCxgWCGesDl60eoGays/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzE3M3RwZWxt/ZDZrdWIycDhwbjdt/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/BDRU0cpmvZwNTit9mwgDCC87pCxgWCGesDl60eoGays/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzE3M3RwZWxt/ZDZrdWIycDhwbjdt/LmpwZw" alt="Cover for 'Empathy' of a pink rose fading into a pure black background" width="78" height="125"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In &lt;em&gt;Empathy&lt;/em&gt;, a mother tries to save her son from himself in this haunting tale set at the brutal intersection of alien intelligence and humanity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cheryl's work is monetised and you can &lt;a href="https://qwyre.com/stream/bdce8427-c0b0-4fa5-9892-902ff34e88d4" rel="noopener noreferrer"&gt;read it here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qwyre.com" rel="noopener noreferrer"&gt;Qwyre&lt;/a&gt; is a streaming payments platform for collaborative publishing, and an ereader, and received a grant from the &lt;em&gt;Interledger Foundation&lt;/em&gt;. Tap left or right on the page to go forwards or back.&lt;/p&gt;

</description>
      <category>publishing</category>
      <category>scifi</category>
      <category>artificialintelligence</category>
      <category>ai</category>
    </item>
    <item>
      <title>Qwyre.com - What we learned, and our ethical foundations and plans for future development [Grant Report #2]</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Tue, 29 Mar 2022 09:19:02 +0000</pubDate>
      <link>https://community.interledger.org/qwyre/qwyrecom-what-we-learned-and-our-ethical-foundations-and-plans-for-future-development-grant-report-2-4kof</link>
      <guid>https://community.interledger.org/qwyre/qwyrecom-what-we-learned-and-our-ethical-foundations-and-plans-for-future-development-grant-report-2-4kof</guid>
      <description>

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I'll be sharing these experiences during April's Grant for the Web Community Call. Please come along and let me know what you think.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Register here&lt;/strong&gt; for the &lt;a href="https://www.eventbrite.com/e/grant-for-the-web-project-skill-share-community-event-tickets-310769438757" rel="noopener noreferrer"&gt;Grant for the Web Project Skill Share Community Event&lt;/a&gt; 10am EST / 4pm CEST on Monday 25 April&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Streaming micropayments - web monetization - is new. And the online new is no longer absorbed into society with the unquestioning rapture of the 1990s. Neither should it.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://bfi.uchicago.edu/wp-content/uploads/Gandal-Neil-etal-An-examination-of-the-cryptocurrency-pump-and-dump-ecosystem.pdf" rel="noopener noreferrer"&gt;cryptocurrency pump and dump fraud&lt;/a&gt; to &lt;a href="https://en.wikipedia.org/wiki/Theranos" rel="noopener noreferrer"&gt;wildly hyperbolic promises&lt;/a&gt; to the tech industry's &lt;a href="https://www.theguardian.com/technology/2021/dec/06/rohingya-sue-facebook-myanmar-genocide-us-uk-legal-action-social-media-violence" rel="noopener noreferrer"&gt;complicity in genocide&lt;/a&gt; ... there are many reasons for the average person to be sceptical of the new.&lt;/p&gt;

&lt;p&gt;So, while the &lt;a href="https://community.webmonetization.org/qwyre/qwyre-com-a-guide-for-integrating-coil-web-monetization-apis-for-creatives-doing-the-job-once-1f56" rel="noopener noreferrer"&gt;technical challenges&lt;/a&gt; are great, the social ones - of building trust, resilience, and engagement - are even greater.&lt;/p&gt;

&lt;p&gt;Yet the promise of disintermediated micropayments is greater still. It is up to us, the community of web monetization makers, to earn that trust.&lt;/p&gt;

&lt;p&gt;What follows may seem unduly negative, but it helps to take stock. To have a realistic appraisal of what challenges us as we take our first tentative steps in this new medium. And, mostly, for me to recognise what I got right, wrong, and still have to reckon with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Makers are not speculators. Risk is not theirs to take.
&lt;/h2&gt;

&lt;p&gt;I used to run a small development project in the shanty towns around the formal neighborhoods of Cape Town. An ubiquitous feature of every street are tiny spaza kiosks selling small packets of sugar, soap, coffee or whatever, repackaged from much larger boxes. What is in those packets isn't some no-name brand, cheapest of the cheap. Nope. Brands. Omo. Lux. Five Roses. Hulett. All the comforting South African logos.&lt;/p&gt;

&lt;p&gt;For a simple reason.&lt;/p&gt;

&lt;p&gt;When you are poor you have no room for risk. Too much goes wrong on a daily basis for which you have no protection. You cannot afford to buy the &lt;em&gt;wrong&lt;/em&gt; brand of soap.&lt;/p&gt;

&lt;p&gt;Creators on the internet may not be quite as poor, but they're on the cusp. Most are not superstars, but have multiple sources of income, cross-subsidising their creative pursuits with whatever &lt;em&gt;ad hoc&lt;/em&gt; work they can take. Setting up &lt;a href="https://qwyre.com" rel="noopener noreferrer"&gt;Qwyre.com&lt;/a&gt; and telling authors, "Hey, come publish here, you'll get paid in real-time and direct to your payment pointer." isn't a quick win.&lt;/p&gt;

&lt;p&gt;Sampling the writing community yielded responses like, "Cryptocurrency? Hell no!"&lt;/p&gt;

&lt;p&gt;It doesn't matter that publishing on Qwyre is simpler than publishing on Amazon, there is no halo effect from established brands. Payments aren't via Visa or Mastercard. Creators can't use their own credit cards, Stripe or PayPal accounts. &lt;/p&gt;

&lt;p&gt;Instead, creators have to sign up for a whole new "wallet" and generate a "pointer" on a web service replete with promotion of speculative cryptocurrency investment.&lt;/p&gt;

&lt;p&gt;It certainly helps that Uphold and Gatehub are working on complying with regulatory authorities in the US and EU, but that takes time. It's also happening now, so when people do go to sign up, they're sometimes told that new accounts aren't available in their region while this regulatory process unfolds.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Lessons:&lt;/strong&gt; I made the technical process of uploading creative work and publishing as easy as possible, but payment pointers and getting paid are - like most publishing platforms - not part of the core workflow. It's not a technical challenge, but a critical part of their user experience.&lt;/p&gt;

&lt;p&gt;I will be redeveloping the publishing experience to integrate payment information and ensure writers know I take their getting paid as equal to them publishing and being read.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making monopolised proprietary lock-in impossible
&lt;/h2&gt;

&lt;p&gt;The tech industry has awoken to &lt;a href="https://www.theverge.com/2022/3/28/23000148/eu-dma-damage-whatsapp-encryption-privacy" rel="noopener noreferrer"&gt;new EU legislation which mandates prying open the locked-down world of text apps&lt;/a&gt;, like WhatsApp. Such lockin should never have been tolerated in the first place, and a world in which the underlying technology had been designed around open interoperability protocols wouldn't have supported tech monopolies in quite the same way, or caused such pandemonium when eventually dismantled. After all, we take it for granted you can phone anyone on any network.&lt;/p&gt;

&lt;p&gt;But just because we came up with a new way to support micropayments doesn't mean we haven't stored up a future where a small number of companies monopolise monetization.&lt;/p&gt;

&lt;p&gt;As Eric Parker, South African franchise guru and co-founder of Nandos, used to say, "Run your small company as if you were a big company." &lt;/p&gt;

&lt;p&gt;He didn't mean "exploit your workers, offshore your profits, and lobby politicians". What he was talking about was technical and management debt. That the mistakes you make in the early days become part of the foundation of your company and impossible to remove later on. They will hurt you forever, and most are avoidable. If you hire only white males from the same schools at the beginning, and twenty years later you wonder how you have fomented a bro-led toxic culture hostile to women and diversity, you're being deliberately ignorant of the world around you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To summarise the summary: &lt;strong&gt;Start as you mean to go on.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our case that means &lt;strong&gt;make monopolies impossible from the beginning&lt;/strong&gt;. Don't give yourself the option. Make it easy for your users to leave. Let them take their stuff and go whenever they want.&lt;/p&gt;

&lt;p&gt;I can't do much about how Coil, Uphold or Gatehub may choose to run their businesses, for good or ill. But I can control my own.&lt;/p&gt;

&lt;p&gt;That means make leaving easy. Creators must be able to take their work - their epubs - and publish them elsewhere. There must never be the option for me to centralise payments. Creators must always be paid direct. But these are relatively easy. There's harder stuff on interoperability I'm not sure how to solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you have a successful link to your work, how do you take that with you? Probably impossible, unless I figure out how authors can use their own domain names?&lt;/li&gt;
&lt;li&gt;What happens if someone already has their own website but wants to integrate with mine for search and reading interface, is that possible?&lt;/li&gt;
&lt;li&gt;What about other platforms doing the same thing? Should we have shared APIs and expose each other's work? Should a reader on Qwyre be able to read a work on my platform on a different app?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you think about Amazon, or Spotify, your library isn't available on other platforms. Even when you outright buy the work, you're locked in to reading or listening on their app. I have multiple copies of some things simply because of this lack of interoperability. And it's not right.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Lessons:&lt;/strong&gt; It's easy to get lost in the technical challenge of making interoperability work, but that's to lose sight of needing to have enough readers and creators to make this necessary in the first place. &lt;em&gt;Start as you mean to go on&lt;/em&gt; doesn't mean &lt;em&gt;do it now&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So ... this is a commitment to making interoperability core to Qwyre - to using open standards (like &lt;em&gt;epub&lt;/em&gt; and not inventing some alternative locked-down &lt;em&gt;mobi&lt;/em&gt; standard instead) - but not to be so obsessed with this that it overwhelms a primary need to build a community of happy readers and creators in the first place.&lt;/p&gt;

&lt;p&gt;Crucially, it is also to be a vocal champion against monopolies, and for open interoperability. To be mindful of what I make, and what I do, to ensure I am in accord with these standards.&lt;/p&gt;




&lt;h2&gt;
  
  
  Patience and the new
&lt;/h2&gt;

&lt;p&gt;One challenge of engaging with tech-driven innovation is watching the wave lift boats all around you, but not yours. Too early, too late, not in the right place ... you end up obsessively running weird - and unnecessary - A/B testing. What about this colour? If the button was 2mm higher on the page? How about a different font?&lt;/p&gt;

&lt;p&gt;Before we had the monopoly stacks, I remember grazing widely on search engines and news portals. We certainly have a winner-takes-all problem, but see my previous point.&lt;/p&gt;

&lt;p&gt;It will take time to build confidence. Time for the various tech components to mature. Time to become part of the world.&lt;/p&gt;

&lt;p&gt;So, what should you be doing in the absence of users. What should I be doing in the absence of readers and creators?&lt;/p&gt;

&lt;p&gt;I'm running a &lt;a href="https://qwyre.com/short-story-contest" rel="noopener noreferrer"&gt;short-story competition&lt;/a&gt; although the pushback from communities where I've sought to promote it has been weird. It's not ok to promote it because of the payments process. Promoting writers to publish because they will get paid by readers while they read is, somehow, not acceptable, but charging readers for a published work and not paying the writer a live share of that is traditional and acceptable.&lt;/p&gt;

&lt;p&gt;Until monetization becomes as mainstream as credit cards, it's going to be difficult for it to be an innocuous and inoffensive way of simply getting paid.&lt;/p&gt;

&lt;p&gt;So, however long it takes to attract other writers and readers is going to take patience. Whether I like it or not doesn't enter into it.&lt;/p&gt;

&lt;p&gt;But ... I am a writer. I can write for my own platform, and that will help me improve the app in measurable ways that make writing and publishing more pleasing for others.&lt;/p&gt;

&lt;p&gt;You can see my doing that now. Here's a short story called &lt;a href="https://qwyre.com/?import=https%3A%2F%2Fqwyre.com%2Fif-only-they-couldn%27t-talk.epub" rel="noopener noreferrer"&gt;"If only they couldn't talk"&lt;/a&gt;. Go ahead. It's free. You don't even need to enable monetization to read it.&lt;/p&gt;

&lt;p&gt;Similarly, I need to engage, as much as this navel-gazing introvert is capable of. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Lessons:&lt;/strong&gt; It's easy to over-think slow-uptake as indicative of commercial failure, but in a new industry - where so many of the concepts and technology are still very immature - it may simply be that you're too early. Spending emotional and financial bandwidth leaping about trying different things may leave you burned-out and spent. &lt;/p&gt;

&lt;p&gt;It is better to have a consistent plan, one that fits within the limits of your mental and financial health, and keep going. There are certainly examples of overnight wonders, but there are just as many of plodders who got there slow and steady.&lt;/p&gt;

&lt;p&gt;I commit to writing more, promoting more (but not obsessing about likes or hits ... I'm not particularly good at social media), and using the awesome thing I made.&lt;/p&gt;




&lt;p&gt;Being supported by Grant for the Web has been an amazing and creative experience. I've enjoyed the community, feedback, and support from the team. I know this is only the beginning, and I hope the alumni of these first few years get to gather in a few years time and look back in wonder and gratitude at all we achieved.&lt;/p&gt;

&lt;p&gt;Good luck, and go read something (on &lt;a href="https://qwyre.com/?import=https%3A%2F%2Fqwyre.com%2Fif-only-they-couldn%27t-talk.epub" rel="noopener noreferrer"&gt;Qwyre.com&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>grantreports</category>
      <category>publishing</category>
      <category>ethics</category>
      <category>scifi</category>
    </item>
    <item>
      <title>Qwyre launches its first web-monetized African speculative fiction short-story competition</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Tue, 22 Mar 2022 14:57:12 +0000</pubDate>
      <link>https://community.interledger.org/qwyre/qwyre-launches-its-first-web-monetized-african-speculative-fiction-short-story-competition-134b</link>
      <guid>https://community.interledger.org/qwyre/qwyre-launches-its-first-web-monetized-african-speculative-fiction-short-story-competition-134b</guid>
      <description>&lt;p&gt;African speculative fiction is finally achieving recognition. Nnedi Okorafor and Tade Thompson have won all the awards. All of them. And amazing anthologies like &lt;em&gt;Dominion&lt;/em&gt; edited by Oghenechovwe Donald Ekpeki and Zelda Knight have introduced some amazing authors, like Dilman Dila and Mame Bougouma Diene, to a wider audience.&lt;/p&gt;

&lt;p&gt;We can do more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qwyre.com" rel="noopener noreferrer"&gt;Qwyre.com&lt;/a&gt; is live and we want to showcase awesome writers while also promoting web monetization for authors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/Xqw5aG00VRUH_O1UVH8QZ0RzTKUDFoqFmPsrfrbEAhY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3R0dTlrZTN3/eXpsMW1rNnE4cmht/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/Xqw5aG00VRUH_O1UVH8QZ0RzTKUDFoqFmPsrfrbEAhY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3R0dTlrZTN3/eXpsMW1rNnE4cmht/LmpwZw" alt="Qwyre.com landing page and navigation bar" width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are offering $50 to each of the ten best African speculative fiction short-stories published on Qwyre.com by 30 April 2022.&lt;/p&gt;

&lt;p&gt;The rules are simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5,000 words, but if your story works longer or shorter, do that. Don't let a word count get in the way of a good story.&lt;/li&gt;
&lt;li&gt;Competition only open to African speculative fiction.&lt;/li&gt;
&lt;li&gt;Multiple submissions are fine, but you can only win for one.&lt;/li&gt;
&lt;li&gt;Original is preferred but – if you have rights to republish – a second publication is ok. This is especially welcomed if you've self-published elsewhere.&lt;/li&gt;
&lt;li&gt;Abide by &lt;a href="https://qwyre.com/get-streaming" rel="noopener noreferrer"&gt;Qwyre's rules&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most fiction competitions end there, with the prize. But Qwyre can offer everyone - including those who aren't amongst the ten - the opportunity to earn direct from readers from the moment they're published.&lt;/p&gt;

&lt;p&gt;Web monetized work earns about 36 cents per hour, or 0.6 cents per minute. For every minute someone reads your work on Qwyre, you get paid.&lt;/p&gt;

&lt;p&gt;To put this in perspective, streaming music services pay about 0.003 to 0.12 cents per 5-minute song. A 5,000 word short-story would take about 12 minutes to read and earn 7.2 cents. You need to be read 700 times to earn $50.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Qwyre authors share in this income directly and in real time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've self-published on Amazon, you know how their approach works. They take all the money and, some interminable time later, they pay you your share. Web monetization means you get paid in real time. Instead of us taking the money, calculating shares, and sending to different people, we divert payments to parties as people read.&lt;/p&gt;

&lt;p&gt;If you agree to pay an editor 15%, and 85% for yourself, then there is a three-way split: 12:73:15 (this ratio takes into account Qwyre's 15% share). Web monetization is per-second payments, and we push payments to each party in this agreed ratio. Everyone gets paid right away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which means - just like other publishing platforms - you need to create an account so you can be paid.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's how all this works ...&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing your work on Qwyre.com
&lt;/h2&gt;

&lt;p&gt;First things first. &lt;em&gt;Open your veins and bleed.&lt;/em&gt; Write something. Once you have something written, proceed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prepare your work for publication
&lt;/h3&gt;

&lt;p&gt;There are plenty of ways independent authors can self-publish. That lack of any friction between the author and the reader is one reason so much is produced, and with such low quality. Commercially-published work benefits from the simple barrier of being edited. Just &lt;em&gt;that&lt;/em&gt; surfaces poor prose and obscure, or irrational, storytelling.&lt;/p&gt;

&lt;p&gt;Qwyre is about collaborative publication, so here's the workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Start at your new creator's workshop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/Tn7LTatCfCAgcBQ0ux-9KNU2VnQdgSaq6PW_FyXqEJ4/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2k5dTB2OGRp/bXRmaTUycHExZ3Br/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/Tn7LTatCfCAgcBQ0ux-9KNU2VnQdgSaq6PW_FyXqEJ4/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2k5dTB2OGRp/bXRmaTUycHExZ3Br/LmpwZw" alt="Creator's workshop page" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you'll find links to the &lt;em&gt;docx&lt;/em&gt; convertor, as well as panels to review your works in progress, and offers for editors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;a href="https://qwyre.com/convert/description" rel="noopener noreferrer"&gt;Convert your Word document &lt;em&gt;docx&lt;/em&gt; to an &lt;em&gt;epub&lt;/em&gt;&lt;/a&gt;&lt;/strong&gt;, in a three step process where you'll describe your work (title, author, publishing date, keywords, that sort of thing ...), upload the document and cover, and then build the standards-compliant epub. This allows Qwyre to stream your work to readers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/wmo6cUnJ66PvXqzLh9mfIx5_dlg-AtyosCVgcv9UFsE/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2lpbGExZGx2/YWkwaWtjM2x0bXAw/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/wmo6cUnJ66PvXqzLh9mfIx5_dlg-AtyosCVgcv9UFsE/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2lpbGExZGx2/YWkwaWtjM2x0bXAw/LmpwZw" alt="Describe your work so we know what it is" width="562" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Import your &lt;em&gt;epub&lt;/em&gt;&lt;/strong&gt;, either during the &lt;em&gt;docx&lt;/em&gt; conversion process or, if you've produced your &lt;em&gt;epub&lt;/em&gt; in some other way, direct into the platform:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/5uU9YgSLYsgReF-pr0_9RtSCR4tkAEFsZwCTSeyjb6o/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2RqbXQ1NTg4/bnhpOWl0MTZjd25x/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/5uU9YgSLYsgReF-pr0_9RtSCR4tkAEFsZwCTSeyjb6o/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2RqbXQ1NTg4/bnhpOWl0MTZjd25x/LmpwZw" alt="Import an epub, either from a link or a local file" width="657" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is intended to be as easy as possible. If you've got an external link or a local file, either can be imported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Review your work&lt;/strong&gt; and check that metadata (your descriptions and keywords), the contents and sample text are presented as expected:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/W1ktSHaePxa2k9ALfl0eoDepGa4IWJlVoCFNVaTi9fA/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzk5NGo4MWpj/bGh0dWdsbWRwaHRw/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/W1ktSHaePxa2k9ALfl0eoDepGa4IWJlVoCFNVaTi9fA/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzk5NGo4MWpj/bGh0dWdsbWRwaHRw/LmpwZw" alt="Review panel drawn direct from the epub" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The metadata and text are derived entirely from the &lt;em&gt;epub&lt;/em&gt; you uploaded. If anything is incorrect it may be because of non-conformance to the epub standard, or because the text is wrong in the source. Correct that, upload a new epub, and ensure you're happy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Prepare a contract&lt;/strong&gt; with your revenue share between yourself and prospective editors. This is what will be shared between the author and editor during monetization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/7Zo_yk-qpQDbS2Y9l8jI9CePygOB8-TZg4mYslQyyAk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzlqbHAyeXI5/N2VhdnN2amhwenE1/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/7Zo_yk-qpQDbS2Y9l8jI9CePygOB8-TZg4mYslQyyAk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzlqbHAyeXI5/N2VhdnN2amhwenE1/LmpwZw" alt="Attempting to claim ownership of 'The Magician' by W. Somerset Maugham" width="712" height="748"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anyone can be an editor&lt;/strong&gt; and I encourage writers to offer to edit other writers work. Not only does this help you improve as a writer, but you also have the opportunity to share in the revenue of a greater diversity of work. &lt;strong&gt;Editors are paid too!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Offline collaboration for publication&lt;/strong&gt; is an important part of the creative process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/wHVC-eo2jRv5fY4YAHhJC3EtGb8TLvOG_NMVhOuT1qY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2V6bnJqam02/cmY5MDZhOGY1eWdh/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/wHVC-eo2jRv5fY4YAHhJC3EtGb8TLvOG_NMVhOuT1qY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2V6bnJqam02/cmY5MDZhOGY1eWdh/LmpwZw" alt="The contract and publication review panel" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The review and collaboration process is initiated via email. Sharing the editor's email address with the author only when offered, and then getting out of their way once they finalise their contract. Qwyre is not going to own the editorial process and the parties to the creative work should edit and review as they prefer.&lt;/p&gt;

&lt;p&gt;Only once both parties have agreed the contract, and agreed the work is ready for publication, can the creator release the work onto the platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Set up your payment pointer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can't use your credit card and need a dedicated payment pointer to start receiving streaming payments. This is straightforward and well-described here &lt;a href="https://webmonetization.org/docs/ilp-wallets" rel="noopener noreferrer"&gt;Digital Wallet and Payment Pointers&lt;/a&gt; for each of the current wallet providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://webmonetization.org/docs/uphold#find-your-payment-pointer" rel="noopener noreferrer"&gt;Uphold&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webmonetization.org/docs/gatehub" rel="noopener noreferrer"&gt;Gatehub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have your pointer, simply save it to your personal profile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're ready to be read!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Readers can search for published works and start streaming&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/mA0ARajWjYHuac-jTP8e1JVbr4mazrj2OPjio11lt6I/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3Y3czJsamVq/MjBkdmVwdWY3ajRh/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/mA0ARajWjYHuac-jTP8e1JVbr4mazrj2OPjio11lt6I/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3Y3czJsamVq/MjBkdmVwdWY3ajRh/LmpwZw" alt="Mobile-friendly epub streaming" width="432" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is as little friction between reader and reading as possible. You don't need to login to start reading a work. You don't even need to pay for the initial pages. The app works for free, giving a reader the opportunity to anonymously try out the app, and your creative work.&lt;/p&gt;

&lt;h2&gt;
  
  
  A call to readers and writers
&lt;/h2&gt;

&lt;p&gt;So ... there it is. I created Qwyre in part because of my frustration with app-based self-publishing services and my desire to read more African speculative fiction.&lt;/p&gt;

&lt;p&gt;I you can write, &lt;strong&gt;please write&lt;/strong&gt;. If you can edit, &lt;strong&gt;please edit&lt;/strong&gt;. And if you can't do either, &lt;strong&gt;help me get this message out!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have any questions or concerns &lt;a href="https://qwyre.com/contact-us" rel="noopener noreferrer"&gt;email me&lt;/a&gt;, or drop a comment in the chat. I would love your help in spreading the word and helping to draw out writers to Qwyre and web monetization. And I hope, soon, to be reading your work.&lt;/p&gt;

</description>
      <category>grantreports</category>
      <category>publishing</category>
      <category>scifi</category>
      <category>competitions</category>
    </item>
    <item>
      <title>Qwyre.com — Converting a digital work into a monetized stream [Grant Report #1]</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Mon, 03 Jan 2022 18:04:55 +0000</pubDate>
      <link>https://community.interledger.org/qwyre/qwyrecom-converting-a-digital-work-into-a-monetized-stream-grant-report-1-51gh</link>
      <guid>https://community.interledger.org/qwyre/qwyrecom-converting-a-digital-work-into-a-monetized-stream-grant-report-1-51gh</guid>
      <description>&lt;p&gt;Projects are like London buses. You wait for hours, standing alone in the pouring rain at midnight freezing your important bits and wondering if you'll ever be warm again, when two come along together.&lt;/p&gt;

&lt;p&gt;And so I found myself, six months ago, navigating two wonderful projects while hoping that I'd pull it off. &lt;/p&gt;

&lt;p&gt;Gentle reader. Working 14-hour days, seven days a week, for six months, is not a healthy pursuit. Hence the break in these monthly reports. My apologies. But the last month has been well spent with an &lt;a href="https://qwyre.com" rel="noopener noreferrer"&gt;interim release&lt;/a&gt; for you to play with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/Xqw5aG00VRUH_O1UVH8QZ0RzTKUDFoqFmPsrfrbEAhY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3R0dTlrZTN3/eXpsMW1rNnE4cmht/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/Xqw5aG00VRUH_O1UVH8QZ0RzTKUDFoqFmPsrfrbEAhY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3R0dTlrZTN3/eXpsMW1rNnE4cmht/LmpwZw" alt="Qwyre.com landing page and navigation bar" width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the most gratifying new features (for me, as a lazy person) is that you can paste a link to an epub and simply import direct into the app. And here a shout-out to &lt;a href="https://standardebooks.org/" rel="noopener noreferrer"&gt;Standard eBooks&lt;/a&gt; which are a wonderful community project reformatting public domain books into standards-compliant epubs.&lt;/p&gt;

&lt;p&gt;If you're a self-published author and want to link to the app direct and don't want your readers to have to faff about, generate a link yourself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/ZlUMKSmyOazNHf_FNJDsyZ7bitfPz3bU3uGUx7YO8ac/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzR5MjV3amc0/NTZsaXF6cTI4Y3Zk/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/ZlUMKSmyOazNHf_FNJDsyZ7bitfPz3bU3uGUx7YO8ac/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzR5MjV3amc0/NTZsaXF6cTI4Y3Zk/LmpwZw" alt="Workshop external link generator" width="644" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting digital objects into digital streams
&lt;/h2&gt;

&lt;p&gt;Web Monetization is a per-second payment stream and it requires a per-second product. Some things are naturals for this, and they're usually live or where the digital service is tightly linked to a platform. Text isn't normally streamed, but music and movies can easily be downloaded as well.&lt;/p&gt;

&lt;p&gt;Streaming means deliberately breaking data into chunks and streaming those in a format that permits live, iterative, building of the digital object from these chunks while also serving the content in that dynamic object.&lt;/p&gt;

&lt;p&gt;For text, that means a stream of words. Except they also have to carry markup so they're formatted properly on arrival. Then the page needs to be updated without offending the reader. It's not trivial.&lt;/p&gt;

&lt;p&gt;For anyone else stuck with this, here is an exceptional &lt;a href="https://stackoverflow.com/questions/49330858/display-dynamic-html-content-like-an-epub-ebook-without-converting-html-to-epub/52194914#52194914" rel="noopener noreferrer"&gt;Stackoverflow answer&lt;/a&gt; that details not just the high-level strategy, but also provides a great foundation in code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Progress on objectives
&lt;/h2&gt;

&lt;p&gt;The status as of the previous updates (&lt;a href="https://community.webmonetization.org/qwyre/qwyre-com-a-framework-for-ethics-and-technical-development-in-fiction-publishing-3nln" rel="noopener noreferrer"&gt;1&lt;/a&gt;, &lt;a href="https://community.webmonetization.org/qwyre/qwyre-com-what-does-a-minimum-loveable-product-even-look-like-22bm" rel="noopener noreferrer"&gt;2&lt;/a&gt;, &lt;a href="https://community.webmonetization.org/qwyre/qwyre-com-a-guide-for-integrating-coil-web-monetization-apis-for-creatives-doing-the-job-once-1f56" rel="noopener noreferrer"&gt;3&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Convert &lt;em&gt;docx&lt;/em&gt; into &lt;em&gt;epub&lt;/em&gt; using &lt;a href="https://github.com/whythawk/chapisha" rel="noopener noreferrer"&gt;Chapisha&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Read &lt;em&gt;epub&lt;/em&gt; using &lt;a href="https://github.com/futurepress" rel="noopener noreferrer"&gt;Futurepress&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Monetize using &lt;a href="https://coil.com" rel="noopener noreferrer"&gt;Coil&lt;/a&gt; (and please do read my &lt;a href="https://community.webmonetization.org/qwyre/qwyre-com-a-guide-for-integrating-coil-web-monetization-apis-for-creatives-doing-the-job-once-1f56" rel="noopener noreferrer"&gt;lengthy write-up&lt;/a&gt; on how to do this).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this report, these additional features are complete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Importing an *epub" for publication,&lt;/li&gt;
&lt;li&gt;Agreeing, and signing, a contract with an editor,&lt;/li&gt;
&lt;li&gt;Converting a work to a stream source,&lt;/li&gt;
&lt;li&gt;Streaming the work in response to web monetization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Preparation of a work for publication
&lt;/h3&gt;

&lt;p&gt;There are plenty of ways independent authors can self-publish. That lack of any friction between the author and the reader is one reason so much is produced, and with such low quality. Commercially-published work benefits from the simple barrier of being edited. Just that surfaces poor prose and obscure, or irrational, storytelling.&lt;/p&gt;

&lt;p&gt;Qwyre is about collaborative publication, so here's the workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Start at your new creator's workshop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/Tn7LTatCfCAgcBQ0ux-9KNU2VnQdgSaq6PW_FyXqEJ4/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2k5dTB2OGRp/bXRmaTUycHExZ3Br/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/Tn7LTatCfCAgcBQ0ux-9KNU2VnQdgSaq6PW_FyXqEJ4/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2k5dTB2OGRp/bXRmaTUycHExZ3Br/LmpwZw" alt="Creator's workshop page" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you'll find links to the &lt;em&gt;docx&lt;/em&gt; convertor, as well as panels to review your works in progress, and offers for editors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Import your &lt;em&gt;epub&lt;/em&gt;&lt;/strong&gt;, either during the &lt;em&gt;docx&lt;/em&gt; conversion process or, if you've produced your &lt;em&gt;epub&lt;/em&gt; in some other way, direct into the platform:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/5uU9YgSLYsgReF-pr0_9RtSCR4tkAEFsZwCTSeyjb6o/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2RqbXQ1NTg4/bnhpOWl0MTZjd25x/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/5uU9YgSLYsgReF-pr0_9RtSCR4tkAEFsZwCTSeyjb6o/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2RqbXQ1NTg4/bnhpOWl0MTZjd25x/LmpwZw" alt="Import an epub, either from a link or a local file" width="657" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is intended to be as easy as possible. If you've got an external link or a local file, either can be imported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Review the work&lt;/strong&gt; and check that metadata, the contents and sample text are presented as expected:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/W1ktSHaePxa2k9ALfl0eoDepGa4IWJlVoCFNVaTi9fA/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzk5NGo4MWpj/bGh0dWdsbWRwaHRw/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/W1ktSHaePxa2k9ALfl0eoDepGa4IWJlVoCFNVaTi9fA/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzk5NGo4MWpj/bGh0dWdsbWRwaHRw/LmpwZw" alt="Review panel drawn direct from the epub" width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The metadata and text are derived entirely from the &lt;em&gt;epub&lt;/em&gt; you uploaded. If anything is incorrect it may be because of non-conformance to the epub standard, or because the text is wrong in the source. Correct that, upload a new epub, and ensure you're happy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Prepare a contract&lt;/strong&gt; with your revenue share between yourself and prospective editors. This is what will be shared between the author and editor during monetization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/7Zo_yk-qpQDbS2Y9l8jI9CePygOB8-TZg4mYslQyyAk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzlqbHAyeXI5/N2VhdnN2amhwenE1/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/7Zo_yk-qpQDbS2Y9l8jI9CePygOB8-TZg4mYslQyyAk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzlqbHAyeXI5/N2VhdnN2amhwenE1/LmpwZw" alt="Attempting to claim ownership of 'The Magician' by W. Somerset Maugham" width="712" height="748"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's another reason to only permit publication after review by an editor. Here I'm attempting to claim authorship of &lt;a href="https://standardebooks.org/ebooks/w-somerset-maugham/the-magician" rel="noopener noreferrer"&gt;'The Magician' by W. Somerset Maugham&lt;/a&gt;. No, that should never be accepted. Any editor should catch that right away and refuse even to accept an offer to collaborate on the work in the first place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Offline collaboration for publication&lt;/strong&gt; is an important part of the creative process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/wHVC-eo2jRv5fY4YAHhJC3EtGb8TLvOG_NMVhOuT1qY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2V6bnJqam02/cmY5MDZhOGY1eWdh/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/wHVC-eo2jRv5fY4YAHhJC3EtGb8TLvOG_NMVhOuT1qY/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2V6bnJqam02/cmY5MDZhOGY1eWdh/LmpwZw" alt="The contract and publication review panel" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The review and collaboration process is initiated via email. Sharing the editor's email address with the author only when offered, and then getting out of their way once they finalise their contract. Qwyre is not going to own the editorial process and the parties to the creative work should edit and review as they prefer.&lt;/p&gt;

&lt;p&gt;Only once both parties have agreed the contract, and agreed the work is ready for publication, can the creator release the work onto the platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Readers can search for published works and start streaming&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/mA0ARajWjYHuac-jTP8e1JVbr4mazrj2OPjio11lt6I/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3Y3czJsamVq/MjBkdmVwdWY3ajRh/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/mA0ARajWjYHuac-jTP8e1JVbr4mazrj2OPjio11lt6I/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3Y3czJsamVq/MjBkdmVwdWY3ajRh/LmpwZw" alt="Mobile-friendly epub streaming" width="432" height="880"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is as little friction between reader and reading as possible. You don't need to login to start reading a work. You don't even need to pay for the initial pages. The app works for free, giving a reader the opportunity to anonymously try out the app, and the creative work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Seamless call to monetization&lt;/strong&gt;, and a judgement call ...&lt;/p&gt;

&lt;p&gt;The sign-up process involves two steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a Qwyre.com account&lt;/li&gt;
&lt;li&gt;Creating a Coil.com subscription and linking that to Qwyre.com&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One reason was to physically seperate Qwyre.com from monetization. The app itself stores nothing about monetization out of the browser. There's no account to delete or concern about data probity. Sure, not forcing the user to create an account would simplify sign-up. That way we only have to worry about the reader signing up with Coil.&lt;/p&gt;

&lt;p&gt;However ... I need some way to keep track of where the reader has gotten to in streaming any work so that they are not double-charged for the same thing across multiple devices. That requires a login.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/B59CvKYx1Qnle1HxqtZI1d30aHOCwldm5c_L7hg3Fc0/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzE1ZnRkaHE2/ZjM0Z3lwMmtoeGpr/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/B59CvKYx1Qnle1HxqtZI1d30aHOCwldm5c_L7hg3Fc0/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzE1ZnRkaHE2/ZjM0Z3lwMmtoeGpr/LmpwZw" alt="Interstitial call to create an account and sign-up with Coil" width="641" height="713"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the reader has authenticated everything, they are still in complete control of the monetization process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/4kOCsClmiq90iD3X_KCjrxGdQhO8xxr86Lm-40HAeOU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL211aTZ2d3dm/OHY3czY2czd6NGVj/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/4kOCsClmiq90iD3X_KCjrxGdQhO8xxr86Lm-40HAeOU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL211aTZ2d3dm/OHY3czY2czd6NGVj/LmpwZw" alt="Interstitial for logged-in readers asking them to start streaming" width="435" height="881"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Development and monetization receipts ...
&lt;/h2&gt;

&lt;p&gt;This is harder than it needs to be. There are no development APIs, so you're working with live Coil accounts, and the largest challenge is proving that you are getting paid.&lt;/p&gt;

&lt;p&gt;Webmonetization offers a &lt;a href="https://webmonetization.org/docs/receipt-verifier" rel="noopener noreferrer"&gt;receipt verifier service&lt;/a&gt;. This is yet another third-party layer between your app and payment. Instead of embedding wallet pointers into your code, you now interpose a verifier endpoint: &lt;code&gt;$receipt-verifier.example/%24wallet.example%2Falice&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It's a complex requirement and immediately runs into CORS issues, meaning you'll need to implement this all yourself creating additional opportunities for broken/dangerous code.&lt;/p&gt;

&lt;p&gt;Some recommendations I'd like to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We need some developer endpoints. I want to test out what happens when payments fail in various scenarios. This is hard to do with a live account.&lt;/li&gt;
&lt;li&gt;The Web Monetization scripts returns a series of &lt;a href="https://webmonetization.org/docs/api" rel="noopener noreferrer"&gt;events&lt;/a&gt;. It would be tremendous if this included a receipt by default. If I want to implement my own receipts verifier, fine, but not having it here creates sequencing issues. Receipts need to be verified continuously for continuous payments. The more endpoints to pole, the more chance for glitches and delays.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;The last outstanding feature is the search engine. Once that is done, I'll release the final version. I also intend to run a demo with the &lt;a href="https://www.africansfs.com/" rel="noopener noreferrer"&gt;African Speculative Fiction Society&lt;/a&gt; members and get some feedback on the work to date. The 2021 Nommo Awards were just announced so go find some African scifi to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  What community support would benefit your project?
&lt;/h2&gt;

&lt;p&gt;I'd appreciate thoughts and feedback on the publication workflow, as well as on the interim release at &lt;a href="https://qwyre.com" rel="noopener noreferrer"&gt;Qwyre.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>grantreports</category>
      <category>publishing</category>
      <category>ethics</category>
      <category>scifi</category>
    </item>
    <item>
      <title>Qwyre.com - A guide for integrating Coil &amp; Web Monetization APIs for creatives doing the job once</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Fri, 03 Sep 2021 17:46:05 +0000</pubDate>
      <link>https://community.interledger.org/qwyre/qwyre-com-a-guide-for-integrating-coil-web-monetization-apis-for-creatives-doing-the-job-once-1f56</link>
      <guid>https://community.interledger.org/qwyre/qwyre-com-a-guide-for-integrating-coil-web-monetization-apis-for-creatives-doing-the-job-once-1f56</guid>
      <description>&lt;p&gt;There are two principle types of developer: journeymen and creatives. A journeyman gains experience through repetition. Overly-technical or complex documentation isn't a problem since the journeyman uses it more like landmarks. The creative coder is developing software as a means to express their idea. They know where they want to go but are covering the ground once.&lt;/p&gt;

&lt;p&gt;This guide for integrating Coil &amp;amp; Web Monetization APIs is written for creatives.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why implement the API?&lt;/strong&gt; It is your responsibility - as creative - to ensure that your prospective subscribers and supporters can easily, and quickly, i) understand what it is that you are offering, and ii) pay for it in a way that is easy and doesn't scare them. You cannot rely on them to install a browser plugin for a technology they may never have heard of. &lt;/p&gt;

&lt;p&gt;There is a secondary reason ... the API offers you more flexibility and creativity in what you can offer. It is more expressive.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Notes to the guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Approach
&lt;/h3&gt;

&lt;p&gt;The first step is to have a &lt;code&gt;high-level&lt;/code&gt; understanding of exactly what needs to happen to authenticate a subscriber and authorise web monetization. This may feel like repetition, but that means we go through this twice. First to get a broad understanding, then into the &lt;code&gt;technical detail&lt;/code&gt; and &lt;strong&gt;gotchas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I recommend you follow along in the reference documentation (see &lt;strong&gt;References&lt;/strong&gt; at the end), &lt;strong&gt;and&lt;/strong&gt; this guide. This guide compliments the documentation and is &lt;strong&gt;not&lt;/strong&gt; a substitute for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Development stack
&lt;/h3&gt;

&lt;p&gt;I make the assumption you know how to code and that you know enough about best-practice in app development that I won't need to include everything. I provide most code required for implementation, but the tech stack is very specific:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Nuxt.js&lt;/strong&gt;&lt;/a&gt; - A Vue.js framework, based on Node.js, for the Progressive Web App &lt;code&gt;frontend&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;FastAPI&lt;/strong&gt;&lt;/a&gt; - A Python framework for building APIs, for the server &lt;code&gt;backend&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The coding languages are &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://www.python.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/a&gt; with annotations (specifically &lt;a href="https://pydantic-docs.helpmanual.io/" rel="noopener noreferrer"&gt;&lt;strong&gt;Pydantic&lt;/strong&gt;&lt;/a&gt;). These annotations ensure code consistency and reduce the number of gotchas.&lt;/p&gt;

&lt;p&gt;The complete development stack is available as a Docker Compose base project generator:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/whythawk" rel="noopener noreferrer"&gt;
        whythawk
      &lt;/a&gt; / &lt;a href="https://github.com/whythawk/full-stack-fastapi-postgresql" rel="noopener noreferrer"&gt;
        full-stack-fastapi-postgresql
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Full stack, modern web application generator. Using FastAPI, PostgreSQL as database, Nuxt3, Docker, automatic HTTPS and more.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Gotchas
&lt;/h3&gt;

&lt;p&gt;There are a &lt;em&gt;LOT&lt;/em&gt; of gotchas. These will be presented as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; a gotcha is an easy-to-miss detail, which - once overlooked - will cause you hours / days of lost time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Consider this a first draft, and please do respond in the comments below if anything is unclear, or you see a better / more efficient way of doing things.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;




&lt;h2&gt;
  
  
  Web Monetization is about &lt;code&gt;authentication&lt;/code&gt; and &lt;code&gt;authorisation&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Your app is in the middle between your subscribers and Coil as monetization provider. Your objectives are &lt;code&gt;authentication&lt;/code&gt; - prove who you are - and &lt;code&gt;authorisation&lt;/code&gt; - prove you have authority to perform an action.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your app needs to perform each of these tasks with each party.&lt;/li&gt;
&lt;li&gt;You need to tool up your app so that it has the necessary credentials and scripts to perform these tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are four separate components you'll need to build:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/zr-U-vcE4gDKX3jZFbvFyhMSmF7uWFVGOISLHVInZTU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2VwcWxvN3Vh/c3luNDE5M2NhNmY0/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/zr-U-vcE4gDKX3jZFbvFyhMSmF7uWFVGOISLHVInZTU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2VwcWxvN3Vh/c3luNDE5M2NhNmY0/LnBuZw" alt="monetization-guide-flowchart" width="721" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authenticate your app with Coil:&lt;/strong&gt; you'll do this once for each app - a minimum of twice, for your development and production environments, and is largely a manual process with application via email and an online form,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embed the OAuth Web Monetization (OWM) script in your app:&lt;/strong&gt; again, once, as you develop your app,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your subscriber authenticates your rights to their monetisation account:&lt;/strong&gt; for each new subscriber,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your subscriber authorises your right to monetize your content:&lt;/strong&gt; for each subscriber, every time they permit monetization, and refreshed every 30 minutes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how your subscriber will experience the process:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. They visit your app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/az1UfYps9N077bO-CoWWn7yj--tg5VFkUNnmYJdFCjk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2p5bHU5cGE5/aHN1ZDNkcmV6NjEx/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/az1UfYps9N077bO-CoWWn7yj--tg5VFkUNnmYJdFCjk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2p5bHU5cGE5/aHN1ZDNkcmV6NjEx/LmpwZw" alt="monetization-app-landing-page" width="512" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. They decide to authorise your app to receive payments and click on a link you generate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/jD-92sxWSAAO8UVeChIS6zCNi4yGW2FDwlUAjsXx1jE/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2I1MnJjZjBx/MjdwdWRyOXYyd3Nr/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/jD-92sxWSAAO8UVeChIS6zCNi4yGW2FDwlUAjsXx1jE/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2I1MnJjZjBx/MjdwdWRyOXYyd3Nr/LmpwZw" alt="monetization-app-sign-in-with-coil" width="509" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. They are referred to Coil where they must approve authentication.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/4EeflDjcfeVrt7qhFpkG4fWektn5Lf4F9x3JRlZu0Sk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzZkOWw5aDI0/OGl2aXppZGM3dGc4/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/4EeflDjcfeVrt7qhFpkG4fWektn5Lf4F9x3JRlZu0Sk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzZkOWw5aDI0/OGl2aXppZGM3dGc4/LmpwZw" alt="monetization-app-subscriber-coil-auth" width="525" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Coil refers the subscriber back to a specific receiving page where your app needs to process the token it receives and authenticate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/oDLnGd5GxA0keLZSJ058H6olwu26GeK6AesIZZGpVzk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2t0NmwyOGNx/a2Q4d2I1dGZvZDRm/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/oDLnGd5GxA0keLZSJ058H6olwu26GeK6AesIZZGpVzk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2t0NmwyOGNx/a2Q4d2I1dGZvZDRm/LmpwZw" alt="monetization-app-authenticating" width="509" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Your app updates the subscriber and invites them to begin streaming.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/goD61MeDAFDkN2bn58k4qnuHWVGPye7zse7yzNI_XpI/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2VkOHA4aDBl/MndnYjZudzlkcnZv/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/goD61MeDAFDkN2bn58k4qnuHWVGPye7zse7yzNI_XpI/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2VkOHA4aDBl/MndnYjZudzlkcnZv/LmpwZw" alt="monetization-app-ready-to-stream" width="508" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Only one of these steps requires your subscriber to leave your app and make an active authentication decision. Thereafter, how you decide to start and stop monetization is up to you.&lt;/p&gt;

&lt;p&gt;Now for the technical details.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Authenticate your app with Coil
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/QA1sG7WNpFSVPZkzhQNX6Ebc05wy5GXIXfVbZ6SM7GE/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3hhbHp5MWhy/dGE2d3ljYndnMXJs/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/QA1sG7WNpFSVPZkzhQNX6Ebc05wy5GXIXfVbZ6SM7GE/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3hhbHp5MWhy/dGE2d3ljYndnMXJs/LnBuZw" alt="image" width="800" height="145"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;At the end of this process you will have:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;client_id&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client_secret&lt;/code&gt; converted to base64.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a strictly manual process and takes some time. Do it earlier rather than later. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow Coil's &lt;a href="https://help.coil.com/docs/dev/oauth-api" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to register your developer account, and email &lt;code&gt;devs@coil.com&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It is normal to wait a week or more for them to respond, when they do, you will be able to register apps and request &lt;code&gt;authentication tokens&lt;/code&gt;,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://coil.com/oauth_register" rel="noopener noreferrer"&gt;https://coil.com/oauth_register&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client app name: qwyre-local&lt;/li&gt;
&lt;li&gt;Redirect URIs (comma-separated): &lt;a href="https://localhost:3000" rel="noopener noreferrer"&gt;https://localhost:3000&lt;/a&gt;, &lt;a href="https://localhost:3000/support-us" rel="noopener noreferrer"&gt;https://localhost:3000/support-us&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Logo URI: &lt;a href="https://localhost:3000/qwyre-logo.svg" rel="noopener noreferrer"&gt;https://localhost:3000/qwyre-logo.svg&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Make a note of the app registration details as they are not listed anywhere afterwards, and you must have them exact for the next step.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; before you decide on the &lt;code&gt;redirect URI&lt;/code&gt; pause to think. This URI will become the most important page in managing this process, and it may not work for you if that page is &lt;code&gt;root&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you receive your app authentication token from Coil, you will need to register with &lt;a href="https://openid.net/connect/" rel="noopener noreferrer"&gt;Coil's Open ID Connect (OIDC)&lt;/a&gt; provider to exchange the token for a client ID and client secret. You can do this from a &lt;code&gt;bash&lt;/code&gt; command line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://help.coil.com/docs/dev/post-oauth-reg#register-your-app-with-the-oidc-provider" rel="noopener noreferrer"&gt;Coil's documentation&lt;/a&gt; is relatively straightforward. Dig out the app registration terms you used above:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;request&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST https://coil.com/oauth/reg \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer Pb8w98v18ikkZyy26nxXK5OKDDsN6kfEJVmQ2id9tbC' \
  -d \
  '{
    "redirect_uris":["http://localhost:3000","http://localhost:3000/support-us"],
    "client_name": "qwyre-local",
    "tos_uri": "https://localhost:3000/privacy/",
    "policy_uri": "https://localhost:3000/privacy/",
    "logo_uri": "http://localhost:3000/qwyre-logo.svg"
  }'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; &lt;code&gt;tos_uri&lt;/code&gt; and &lt;code&gt;policy_uri&lt;/code&gt; must be &lt;code&gt;https&lt;/code&gt; otherwise the request will fail. It doesn't matter whether your development environment is not certified (although you can issue yourself a self-signed certificate if you like). The end-points are not tested, only the form of the URI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;response&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "client_id": "314ac134-fc3c-4d28-bf43-ccb75a2f9fb2",
  "client_secret": "uVE2t7y1QvyM78PlBA3aQAUh6syXVw7P2XBr4QDsS2yrkETR6al9YFpH4NDloXh5",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is abbreviated, and is the only time you'll get this, so save these details somewhere safe. These are the only two keys you need (for now): &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;client_secret&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; Not really a gotcha, since it's clearly flagged in the docs, however, before you can use your &lt;code&gt;client_secret&lt;/code&gt;, you need to make the string web-safe since you will be sending this in your first GET request. That requires converting it to base 64. In Python, that's pretty easy:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import base64
base64.b64encode(bytes(client_secret, 'utf-8'))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Then use that string as your &lt;code&gt;client_secret&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. Embed the OAuth Web Monetization (OWM) script in your app
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/WdSozIOLmif5tmmdc4FG5hAH6nF6h5-3qUmmh94K1Ds/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzYyYnl3bjl3/aGMzNW40YmNqdGhw/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/WdSozIOLmif5tmmdc4FG5hAH6nF6h5-3qUmmh94K1Ds/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzYyYnl3bjl3/aGMzNW40YmNqdGhw/LnBuZw" alt="monetization-2-embed-owm-script" width="701" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;At the end of this process you will have:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Embedded Coil's OWM script in your app.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://help.coil.com/docs/dev/oauth-web-monetization-script" rel="noopener noreferrer"&gt;Coil describes this&lt;/a&gt; as a relatively straightforward process. It is not.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; the Coil OWM script does not play well with Nuxt.js and, I'm assuming, any Node-based JavaScript/TypeScript framework that performs server-side-rendering and hydration. If you don't know what this means, just be aware that this process is the source of many gotchas, not just when implementing web monetization.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Coil recommends you create a base &lt;code&gt;index.html&lt;/code&gt; file as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta name="monetization" content="$wallet.example.com/~alice"&amp;gt;
    &amp;lt;script&amp;gt;
      if (document.monetization) {
        document.monetizationExtensionInstalled = true
      } else {
        document.monetization = document.createElement('div')
        document.monetization.state = 'stopped'
      }
    &amp;lt;/script&amp;gt;
    &amp;lt;script src="https://cdn.coil.com/coil-oauth-wm.v7.beta.js" defer&amp;gt;
    &amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;p&amp;gt;Testing web monetization via coil polyfill using btp token.&amp;lt;/p&amp;gt;
    &amp;lt;script&amp;gt;
      document.coilMonetizationPolyfill.init({ btpToken: 'e0y1...' })
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You'll notice three important components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;meta&lt;/code&gt; tag with the destination wallet for streaming payments,&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;script&lt;/code&gt; tag with &lt;code&gt;defer&lt;/code&gt; import of the OWM script,&lt;/li&gt;
&lt;li&gt;And an &lt;code&gt;if else&lt;/code&gt; statement setting up the &lt;code&gt;div&lt;/code&gt; for interacting with &lt;code&gt;document.monetization&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Something that I spent quite a bit of time frustrated by is that I couldn't interact with the OWM script in any way &lt;strong&gt;until after&lt;/strong&gt; it had been initialised by creating that seemingly innocuous &lt;code&gt;div&lt;/code&gt;. This has implications for the way Node interacts with the script.&lt;/p&gt;

&lt;p&gt;The recommended way to include external / third-party scripts in Node is via a &lt;code&gt;config.js&lt;/code&gt; file. For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Global page headers: https://go.nuxtjs.dev/config-head
// https://vue-meta.nuxtjs.org/api/#script
// https://javascript.info/script-async-defer
head: {
    title: "Qwyre",
    meta: [
      { charset: "utf-8" },
      { name: "viewport", content: "width=device-width, initial-scale=1" },
      { name: "monetization", content: process.env.COIL_PAYMENT_POINTER },
      { hid: "description", name: "description", content: "" },
      { src: "https://cdn.coil.com/coil-oauth-wm.v7.beta.js", defer: true },
    ],
    link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then you would initialise the script in your &lt;code&gt;layout&lt;/code&gt; (base html templates to structure any pages). &lt;strong&gt;This does not work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this time, you need to bypass Node/Nuxt.js and initialise your &lt;code&gt;app.html&lt;/code&gt; (ordinarily not customised, and left to the framework to manage):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html {{ HTML_ATTRS }}&amp;gt;
      &amp;lt;head {{ HEAD_ATTRS }}&amp;gt;
        {{ HEAD }}
        &amp;lt;script&amp;gt;
            if (document.monetization) {
              document.monetizationExtensionInstalled = true
            } else {
              document.monetization = document.createElement('div')
              document.monetization.state = 'stopped'
            }
          &amp;lt;/script&amp;gt;
          &amp;lt;script src="https://cdn.coil.com/coil-oauth-wm.v7.beta.js" defer&amp;gt;&amp;lt;/script&amp;gt;
      &amp;lt;/head&amp;gt;
      &amp;lt;body {{ BODY_ATTRS }}&amp;gt;
        {{ APP }}
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even then, you will get some flaky responses. I am unclear as to whether this is normal, or related to the compromises.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Your subscriber authenticates your rights to their monetisation account
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/jvcCV8GelkqlDGGmQ-u6ghu3VZwDprSVCDAlRC8s0QI/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2ZlcDhrdm9l/dTFubGRrdG44MzM0/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/jvcCV8GelkqlDGGmQ-u6ghu3VZwDprSVCDAlRC8s0QI/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL2ZlcDhrdm9l/dTFubGRrdG44MzM0/LnBuZw" alt="monetization-3-subscriber-auth" width="701" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;At the end of this process you will have:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up TypeScript interfaces for &lt;code&gt;requests&lt;/code&gt; and &lt;code&gt;responses&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Set up all API request code,&lt;/li&gt;
&lt;li&gt;Set up a proxy for queries to Coil,&lt;/li&gt;
&lt;li&gt;Store the &lt;code&gt;refresh_token&lt;/code&gt; as a cookie, and &lt;code&gt;access_token&lt;/code&gt; and &lt;code&gt;sub&lt;/code&gt; in the &lt;code&gt;store&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;[Optional] Request your subscriber's personal information.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe go get some coffee. This is going to get very technical, very fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up your TypeScript interfaces
&lt;/h3&gt;

&lt;p&gt;It is very easy to get lost amongst the keys and their requirements. TypeScript interfaces permit us to define what data we need to &lt;code&gt;request&lt;/code&gt;, and what we expect in &lt;code&gt;response&lt;/code&gt;. These are &lt;a href="https://help.coil.com/docs/dev/oauth-api" rel="noopener noreferrer"&gt;documented&lt;/a&gt; over several pages, but we'll pool them all in one, then use them throughout what follows. It will make things easier.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;./interfaces/coil.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /* eslint-disable camelcase */

    export interface ICoilUserResourceRequest {
      response_type: string
      scope: string
      client_id: string
      state: string
      redirect_uri: string
      prompt: string
    }

    export interface ICoilUserResourceResponse {
      code: string
      state: string
      scope: string
    }

    export interface ICoilUserTokenRequest {
      code: string
      grant_type: string
      redirect_uri: string
    }

    export interface ICoilUserTokenResponse {
      access_token: string
      expires_in: number
      id_token: string
      refresh_token: string
      scope: string
      token_type: string
    }

    export interface ICoilUserTokenRefreshRequest {
      refresh_token: string
      grant_type: string
      scope: string
    }

    export interface ICoilUserTokenRevokeRequest {
      token: string
    }

    export interface ICoilUserInfo {
      email?: string
      sub: string
    }

    export interface ICoilUserBTP {
      btpToken: string
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That should be fairly readable. Interfaces for &lt;code&gt;request&lt;/code&gt; are followed by a counterparty &lt;code&gt;response&lt;/code&gt;. We make the request, we receive the response. It should all validate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up all API request code
&lt;/h3&gt;

&lt;p&gt;We'll go through this step-by-step ... I'm using &lt;a href="https://axios-http.com/docs/intro" rel="noopener noreferrer"&gt;Axios&lt;/a&gt; to make the requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Generate and present the referral URI to your subscriber&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; While Coil states that you can refer the subscriber to either of a &lt;code&gt;signup&lt;/code&gt; or &lt;code&gt;login&lt;/code&gt; page, the reality is only &lt;code&gt;login&lt;/code&gt; works. Note this for the &lt;code&gt;prompt&lt;/code&gt; key in the API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Nuxt.js, create &lt;code&gt;./api/coil.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import axios from "axios"
import {
  ICoilUserResourceRequest,
  ICoilUserTokenRequest,
  ICoilUserTokenResponse,
  ICoilUserTokenRefreshRequest,
  ICoilUserTokenRevokeRequest,
  ICoilUserInfo,
  ICoilUserBTP,
} from "@/interfaces"

export const coil = {

    getUserResource(coilState: string): string {
        // https://help.coil.com/docs/dev/get-oauth-auth#request-parameters
        // NOTE: currently the 'signup' prompt doesn't work
        // https://www.valentinog.com/blog/url/
        const getResourceURL = new URL(`${process.env.coilUrl}/oauth/auth`)
        const data: ICoilUserResourceRequest = {
          response_type: "code",
          scope: "simple_wm openid",
          client_id: (process.env.coilID as unknown) as string,
          state: coilState,
          redirect_uri: `${process.env.baseUrl}/support-us`,
          prompt: "login",
        }
        for (const [key, value] of Object.entries(data)) {
          getResourceURL.searchParams.append(key, value)
        }
        return getResourceURL.href
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; &lt;code&gt;coilState&lt;/code&gt; is a really important token you will use to ensure you can trust the response from Coil (i.e. no spoofing of your website by sending random scripts to your processing page). Effectively, you generate a random string, send that to Coil along with your request, then Coil sends it back. It is &lt;strong&gt;YOUR&lt;/strong&gt; responsibility to validate the &lt;code&gt;state&lt;/code&gt; you receive as being identical to the one you sent. I generate these as UUIDs:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In &lt;code&gt;frontend&lt;/code&gt; create &lt;code&gt;./utilities/keys.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function generateUUID(): string {
  // Reference: https://stackoverflow.com/a/2117523/709884
  // And: https://stackoverflow.com/a/61011303/295606
  return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (s) =&amp;gt; {
    const c = Number.parseInt(s, 10)
    return (
      c ^
      (crypto.getRandomValues(new Uint8Array(1))[0] &amp;amp; (15 &amp;gt;&amp;gt; (c / 4)))
    ).toString(16)
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You will call and use the generated URI in your &lt;code&gt;redirect_uri&lt;/code&gt; page script. &lt;/p&gt;

&lt;p&gt;In &lt;code&gt;frontend&lt;/code&gt;, redirect processing page &lt;code&gt;./pages/support-us.vue&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.coilRequestURI = coil.getUserResource(this.coilState)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And then use this to generate the "Sign in with Coil" button:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;a class="mt-9" :href="coilRequestURI"
  &amp;gt;&amp;lt;SvgIcon terms="w-56 h-10" icon="svgCoilWhite"
/&amp;gt;&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; you defined which page you want Coil to return the subscriber's response as &lt;code&gt;redirect_uri&lt;/code&gt;. It is this page where all the following processing will take place. Mine is &lt;code&gt;./pages/support-us.vue&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; You can't make requests from the &lt;code&gt;frontend&lt;/code&gt;. You run straight into &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;Cross-Origin Resource Sharing (CORS)&lt;/a&gt; errors where the requests and / or responses are blocked. You need to proxy your requests. This is especially painful if you were hoping for a Single-Page Application, or a static app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fortunately, I'm using FastAPI on the &lt;code&gt;backend&lt;/code&gt; so it's relatively straightforward to set it up to proxy requests. We'll do that in the next section, so for here just note that the URIs for &lt;code&gt;axios.post&lt;/code&gt; and &lt;code&gt;axios.get&lt;/code&gt; are being requested from the &lt;code&gt;backend&lt;/code&gt;, not Coil.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Receive and use response token to request subscriber authentication keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The coil redirect will return to your response page, along with &lt;code&gt;query&lt;/code&gt; terms in the path. It may look something like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;response&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/
?code=CU6LG36vKvVmUbF9QWFwj7F5zvY
&amp;amp;state=b5f1872f-9d32-5f31-819d-5a4daeab4ea9
&amp;amp;scope=simple_wm openid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;First, parse this to extract the parameters and then send this for processing. I'm using the Nuxt Store to manage browser state.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;frontend&lt;/code&gt;, in redirect processing page &lt;code&gt;./pages/support-us.vue&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (
  this.$route.query.code &amp;amp;&amp;amp;
  this.$route.query.state &amp;amp;&amp;amp;
  this.$route.query.scope
) {
  const payload: ICoilUserResourceResponse = {
    code: this.$route.query.code as string,
    state: this.$route.query.state as string,
    scope: this.$route.query.scope as string,
  }
  await this.getUserToken(payload)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The asyncronous await to &lt;code&gt;getUserToken&lt;/code&gt; populates the store and triggers the cascade of authentication requests we need. In &lt;code&gt;./store/monetization/actions.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async getUserToken({ commit, dispatch }, payload: ICoilUserResourceResponse) {
    if (getCoilState() !== payload.state) {
      await commit("setRequestError", true)
    } else {
      try {
        try {
          const response: AxiosResponse = await coil.getUserToken(payload.code)
          const data: ICoilUserTokenResponse = response.data
          await commit("setCoilToken", data.access_token)
          saveCoilRefresh(data.refresh_token)
        } catch (error) {
          await dispatch("checkCoilError", error)
        }
      } catch (error) {
        await dispatch("checkCoilError", error)
      }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This requires a request to the coil API, and then we save the authentication token to the store, and the refresh token as a persistent cookie in the subscriber's browser.&lt;/p&gt;

&lt;p&gt;This is the critical self-validation call to ensure that the response you receive is valid and from Coil:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (getCoilState() !== payload.state) ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is the next api call to Coil to pass back the token you received and exchange it for your subscriber's authentication and refresh tokens:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await coil.getUserToken(payload.code)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Back to &lt;code&gt;./api/coil.ts&lt;/code&gt; and pop this into the &lt;code&gt;export&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async getUserToken(coilCode: string) {
    // https://help.coil.com/docs/dev/post-oauth-token/index.html#request-an-access-token-for-an-authenticated-user
    const data: ICoilUserTokenRequest = {
      code: coilCode,
      grant_type: "authorization_code",
      redirect_uri: `${process.env.baseUrl}/create`,
    }
    return await axios.post&amp;lt;ICoilUserTokenResponse&amp;gt;(
      `${process.env.apiUrl}/api/v1/coil/proxy/oauth/token`,
      data,
      requestHeaders()
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is a &lt;code&gt;post&lt;/code&gt; and you pass your app's authentication keys to Coil in the header:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;requestHeaders()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That function is included in &lt;code&gt;./api/coil.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function requestHeaders() {
  const token = btoa(`${process.env.coilID}:${process.env.coilSecret}`)
  return {
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: `Basic ${token}`,
    },
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; Note again that you're escaping your two keys, &lt;code&gt;client_id&lt;/code&gt; and &lt;code&gt;client_secret&lt;/code&gt;, as base64. Here using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/btoa" rel="noopener noreferrer"&gt;btoa&lt;/a&gt;. I don't know if this was just me, but I found that I had to do this 'twice'. I first converted &lt;code&gt;client_secret&lt;/code&gt; to base64, then generated a new base64 string on the result &lt;code&gt;btoa(client_id:client_secrect_base64)&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Coil returns a response &lt;code&gt;json&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;response&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "access_token": "eyJhbGciOi...JSUzI1NfsQ",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUz...I1NiIsInR5",
  "refresh_token": "dzfKQUEFYXEZ2~WKq5t0atT36X~",
  "scope": "simple_wm", "openid",
  "token_type": "Bearer"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Store the &lt;code&gt;refresh_token&lt;/code&gt; as a cookie, and &lt;code&gt;access_token&lt;/code&gt; and &lt;code&gt;sub&lt;/code&gt; in the &lt;code&gt;store&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This &lt;code&gt;access_token&lt;/code&gt; authenticates you to request a BTP token which is what you need to begin streaming money. This token is only valid for an hour, and so I don't bother storing it in a persistent cookie, leaving it in the store. However, the &lt;code&gt;refresh_token&lt;/code&gt; is valid indefinitely and so you need to take care with it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ETHICS:&lt;/strong&gt; You may be tempted to store this &lt;code&gt;refresh_token&lt;/code&gt; in your database. &lt;strong&gt;DO NOT DO SO&lt;/strong&gt;. It doesn't belong to you. It belongs to your subscriber. Should they wish to revoke your use of that token, it should never be a request to your database. They should have the power to simply clear their browser cookies.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Request your subscriber's personal information
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Optional:&lt;/strong&gt; Depending on how your app works, you may want your subscriber's personal information (user id and/or email). This is a separate request. It is &lt;strong&gt;not&lt;/strong&gt; needed to authorise streaming.&lt;/p&gt;

&lt;p&gt;In the store &lt;code&gt;./store/monetization/actions.ts&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async getUserInfo({ commit, dispatch, state }) {
    try {
      const response: AxiosResponse = await coil.getUserInfo(state.coilToken)
      const data: ICoilUserInfo = response.data
      await commit("setCoilSub", data.sub)
    } catch (error) {
      await dispatch("checkCoilError", error)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Back in &lt;code&gt;./api/coil.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async getUserInfo(accessToken: string) {
    return await axios.get&amp;lt;ICoilUserInfo&amp;gt;(
      `${process.env.apiUrl}/api/v1/coil/proxy/user/info`,
      requestUserHeaders(accessToken)
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; you are using both a new API endpoint for this request, as well as a new header. &lt;code&gt;https://api.coil.com&lt;/code&gt;, and this &lt;code&gt;header&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function requestUserHeaders(accessToken: string) {
  return {
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: `Bearer ${accessToken}`,
    },
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Where &lt;code&gt;accessToken&lt;/code&gt; is the &lt;code&gt;access_token&lt;/code&gt; you received from &lt;code&gt;getUserToken&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In addition, your request to &lt;code&gt;getUserInfo&lt;/code&gt; is a &lt;code&gt;get&lt;/code&gt; request. That's a minor change here, but a big change on your proxy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;response&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "sub": "22028a7c-b720-7a3e-4387-6c9845f6201b",
  "email": "alice@coil.com"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Set up a &lt;code&gt;backend&lt;/code&gt; proxy for queries to Coil
&lt;/h3&gt;

&lt;p&gt;As described earlier, all of the API calls in the Nuxt &lt;code&gt;frontend&lt;/code&gt; are proxied to the FastAPI &lt;code&gt;backend&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The way this works is, your &lt;code&gt;frontend&lt;/code&gt; interacts with the subscriber and manages the monetization state. It then sends any queries it has direct to the server and immediately refers back any responses. It stores nothing and remembers nothing.&lt;/p&gt;

&lt;p&gt;Python code follows, using &lt;a href="https://www.python-httpx.org/" rel="noopener noreferrer"&gt;HTTPX&lt;/a&gt; as the http client.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ETHICS:&lt;/strong&gt; You'll see that, while your subscriber is anonymous (as are their payments), control over when to charge them and how to do so is not actually in their control. It's entirely in yours. That means you have to deliberately - from the beginning - make it easy to &lt;strong&gt;NOT ABUSE YOUR SUBSCRIBER&lt;/strong&gt;. The only way you can do that effectively is if you resolve never to store any auth keys anywhere near your backend, but leave them entirely in your subscriber's browser. This is the reason we only proxy at the backend and not manage the entire process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What follows is longer than it needs to be because of yet another gotcha.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; Coil's API has two inconsistencies. The first is that not all requests are &lt;code&gt;POST&lt;/code&gt;. There is one &lt;code&gt;GET&lt;/code&gt;. The second is that not all requests are to the same URI. It switches from &lt;code&gt;https://coil.com&lt;/code&gt; to &lt;code&gt;https://api.coil.com&lt;/code&gt;. You need to account for this in your proxy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create &lt;code&gt;./app/api/api_v1/endpoints/coil.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    from typing import Any
    from fastapi import APIRouter, HTTPException, Request, Response
    import httpx

    from app.core.config import settings


    router = APIRouter()
    # https://coil.com
    coil_uri = settings.COIL_URI
    # https://api.coil.com
    coil_api_uri = settings.COIL_API_URI


    # FOR POST
    @router.post("/proxy/{path:path}")
    async def proxy_post_request(*, path: str, request: Request) -&amp;gt; Any:
        # https://www.starlette.io/requests/
        # https://www.python-httpx.org/quickstart/
        # https://github.com/tiangolo/fastapi/issues/1788#issuecomment-698698884
        # https://fastapi.tiangolo.com/tutorial/path-params/#__code_13
        try:
            URI = coil_uri
            if path.startswith("user/"):
                URI = coil_api_uri
            data = await request.json()
            headers = {
                "Content-Type": request.headers["Content-Type"],
                "Authorization": request.headers.get("Authorization"),
            }
            async with httpx.AsyncClient() as client:
                proxy = await client.post(f"{URI}/{path}", headers=headers, data=data)
            response = Response(content=proxy.content, status_code=proxy.status_code)
            return response
        except Exception as e:
            raise HTTPException(status_code=403, detail=str(e))

    #FOR GET
    @router.get("/proxy/{path:path}")
    async def proxy_get_request(*, path: str, request: Request) -&amp;gt; Any:
        try:
            URI = coil_uri
            if path.startswith("user/"):
                URI = coil_api_uri
            headers = {
                "Content-Type": request.headers.get("Content-Type", "application/x-www-form-urlencoded"),
                "Authorization": request.headers["Authorization"],
            }
            async with httpx.AsyncClient() as client:
                proxy = await client.get(f"{URI}/{path}", headers=headers)
            response = Response(content=proxy.content, status_code=proxy.status_code)
            return response
        except Exception as e:
            raise HTTPException(status_code=403, detail=str(e))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all you need on the server, but let me give you a little context and explanation.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@router.post("/proxy/{path:path}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;FastAPI uses &lt;code&gt;types&lt;/code&gt; and Pydantic to annotate variables. What's going to happen here is that all the text after &lt;code&gt;path&lt;/code&gt; is going to be treated as a &lt;code&gt;path&lt;/code&gt; type and provided as a variable to the function. We then use &lt;code&gt;httpx&lt;/code&gt; to make a new &lt;code&gt;post&lt;/code&gt; to Coil. Note, though, how we have to account for the gotcha of shifting endpoints.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;URI = coil_uri
if path.startswith("user/"):
    URI = coil_api_uri
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then we regenerate our headers, get any data from the request in &lt;code&gt;json&lt;/code&gt;/&lt;code&gt;dict&lt;/code&gt; format, and proxy using &lt;code&gt;httpx&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data = await request.json()
async with httpx.AsyncClient() as client:
    proxy = await client.post(f"{URI}/{path}", headers=headers, data=data)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; Coil response codes are a little unclear. The only failure code is a &lt;code&gt;403&lt;/code&gt; request failure. You do get some text to explain the error, and you can send that back to the app, but I found it often quite obscure. It wasn't always clear what I had done wrong, or what wasn't working.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Your subscriber authorises your right to monetize your content
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/piiV_Cma8MvnZvqYqlcY2_60UCHDcfazxeyDVfEVnuU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzRlMXFjZzE2/b2p5b3NhM3R2aGZl/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/piiV_Cma8MvnZvqYqlcY2_60UCHDcfazxeyDVfEVnuU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzRlMXFjZzE2/b2p5b3NhM3R2aGZl/LnBuZw" alt="monetization-4-monetization" width="701" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;At the end of this process you will have:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorise your rights to monetize a subscriber,&lt;/li&gt;
&lt;li&gt;Initialise your web monetization stream, and specify a destination wallet,&lt;/li&gt;
&lt;li&gt;Refresh streaming tokens as required,&lt;/li&gt;
&lt;li&gt;Stop / Revoke streaming in response to your subscriber's wishes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Authorise your rights to monetize a subscriber
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;btpToken&lt;/code&gt; - which is a &lt;em&gt;promise&lt;/em&gt; to fulfill a payment request, not payment in and of itself - is only valid for 30 minutes. There is no point in generating one until your subscriber actually expresses their intention to stream money to your wallet.&lt;/p&gt;

&lt;p&gt;You'll need some sort of "start" / "stop" UX on your app, and the Web Monetization Foundation has a great set of docs that &lt;a href="https://webmonetization.org/docs/start-stop/" rel="noopener noreferrer"&gt;explain exactly this&lt;/a&gt;. You can review those after this section. First we use the subscriber's authentication token to request a BTP token.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;frontend&lt;/code&gt; store &lt;code&gt;./store/monetization/actions.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async getUserBTP({ commit, dispatch, state }) {
    try {
      const response: AxiosResponse = await coil.getUserBTP(state.coilToken)
      const data: ICoilUserBTP = response.data
      await commit("setBtpToken", data.btpToken)
    } catch (error) {
      await dispatch("checkCoilError", error)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Back in &lt;code&gt;frontend&lt;/code&gt; &lt;code&gt;./api/coil.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async getUserBTP(accessToken: string) {
    return await axios.post&amp;lt;ICoilUserBTP&amp;gt;(
      `${process.env.apiUrl}/api/v1/coil/proxy/user/btp`,
      {},
      requestUserHeaders(accessToken)
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There's no point in storing the token outside of the store. It is valid for only 30 minutes from the time of issue, whether it is used or not.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; the request to get the &lt;code&gt;btpToken&lt;/code&gt; is both in camel-case, and a &lt;code&gt;post&lt;/code&gt;. It also has no &lt;code&gt;data&lt;/code&gt; to send in post, so ensure you include an empty object: &lt;code&gt;{}&lt;/code&gt;. It also uses &lt;code&gt;https://api.coil.com&lt;/code&gt;. Both the request to get user info, and BTP token are the only requests to this root endpoint.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;response&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "btpToken": "eyJhbGciOiJCchQ8...SeOz98I2Sqyf1LrVM"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now you have a &lt;code&gt;btpToken&lt;/code&gt;, but possession of a token is not money. That still needs to happen in the next process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initialise your web monetization stream, and specify a destination wallet
&lt;/h3&gt;

&lt;p&gt;We'll start with the gotcha.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;GOTCHA:&lt;/strong&gt; The challenge of websites is also the purpose of JavaScript web frameworks, like Node. To manage, and &lt;strong&gt;persist&lt;/strong&gt;, state. The more you can persist between state changes (shifting between different web pages) the more a website can feel like a native application. A Progressive Web App is all about near native user-experience. Problem is, the current Coil OWM script does not seem to expect this. Their documentation expressly states "Add the following code to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; section of each page you are monetizing.":&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    document.coilMonetizationPolyfill.init({ btpToken: 'USERS_TOKEN_HERE' })
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Doing that in a Node app, though, will instantly crash your app during transitions since this is exactly the sort of state that Node will preserve. That means initialising the &lt;code&gt;coilMonetizationPolyfill&lt;/code&gt; needs to look like this on every page:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (
  process.client &amp;amp;&amp;amp;
  Object.prototype.hasOwnProperty.call(document, "coilMonetizationPolyfill")
)
  try {
    document.coilMonetizationPolyfill.init({ btpToken: this.btpToken })
  } catch (error) {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In words: check that your script will execute &lt;em&gt;only&lt;/em&gt; client-side, and that the &lt;code&gt;coilMonetizationPolyfill&lt;/code&gt; has been initialised, and then &lt;code&gt;try&lt;/code&gt;/&lt;code&gt;catch&lt;/code&gt; to initialise it with the &lt;code&gt;btpToken&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I created a single "start" / "stop" component to listen to subscriber instructions.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;frontend&lt;/code&gt; &lt;code&gt;./components/monetization/MenuToggle.vue&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public setMonetizationTag() {
    // https://github.com/Techgethr/webmonvuejs/blob/main/index.js
    // https://webmonetization.org/docs/start-stop/
    const monetizationTag = document.querySelector('meta[name="monetization"]')
    if (!monetizationTag &amp;amp;&amp;amp; this.coilPointer) {
      const meta = document.createElement("meta")
      meta.name = "monetization"
      meta.content = this.coilPointer
      document.getElementsByTagName("head")[0].appendChild(meta)
    }
    if (monetizationTag &amp;amp;&amp;amp; !this.coilPointer) {
      monetizationTag.remove()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ETHICS:&lt;/strong&gt; The way that the OWM script "listens" to whether or not a valid BTP token should be used to stream payments is whether or not there is a metatag pointing to a wallet. &lt;strong&gt;You end streaming payments by removing the pointer.&lt;/strong&gt; Everyone, from your subscriber, to Coil, to the Web Monetization Foundation and Grant for the Web are relying on you to honour your subscriber's intentions and expressed instructions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When your subscriber tells you to stream, you add the wallet pointer:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!monetizationTag &amp;amp;&amp;amp; this.coilPointer) {
  const meta = document.createElement("meta")
  meta.name = "monetization"
  meta.content = this.coilPointer
  document.getElementsByTagName("head")[0].appendChild(meta)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And when they tell you to stop, you remove it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (monetizationTag &amp;amp;&amp;amp; !this.coilPointer) {
  monetizationTag.remove()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That's the entirety of your subscriber's protection from abuse of their trust in you. That you &lt;strong&gt;MUST&lt;/strong&gt; honour their instructions and delete the wallet pointer. Please - and I know I'm whispering in a thunderstorm - please don't abuse this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refresh streaming tokens as required
&lt;/h3&gt;

&lt;p&gt;"Start" and "Stop" is equivalent to a call to check the validity of a token, and refresh them as required.&lt;/p&gt;

&lt;p&gt;In the same &lt;code&gt;frontend&lt;/code&gt; component &lt;code&gt;./components/monetization/MenuToggle.vue&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async toggleMonetizationStreaming() {
    if (!this.isStreaming) await this.startMonetizationStream()
    else await this.stopMonetizationStream()
    this.setMonetizationTag()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is in response to the subscriber toggling a button in the menu bar of the app.&lt;/p&gt;

&lt;p&gt;It is your responsibility to check that a BTP token is still valid. A JWT token intrinsically contains the time it was created, so you can easily check.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;frontend&lt;/code&gt; &lt;code&gt;./utilities/keys.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getTimeInSeconds(): number {
  // https://stackoverflow.com/a/3830279/295606
  return Math.floor(new Date().getTime() / 1000)
}

function tokenExpired(token: string) {
  // https://stackoverflow.com/a/60758392/295606
  const expiry = JSON.parse(atob(token.split(".")[1])).exp
  return getTimeInSeconds() &amp;gt;= expiry
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And then on to the &lt;code&gt;frontend&lt;/code&gt; store, where we manage monetization state &lt;code&gt;./store/monetization/actions.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  async startMonetizationStream({ commit, dispatch, state }) {
    await dispatch("refreshUserBTP")
    if (!state.requestError) {
      await commit("setIsStreaming", true)
      await commit("setCoilPointer", process.env.coilPointer as string)
      await commit(
        "main/addNotification",
        {
          content: "You have started web monetization.",
          color: "success",
        },
        { root: true }
      )
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refreshing the BTP token triggers a cascade of requests. I felt there was no point in worrying about the difference between an &lt;code&gt;authentication_token&lt;/code&gt; valid for 60 minutes, and a &lt;code&gt;btpToken&lt;/code&gt; valid for 30. Just refresh both of them.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;frontend&lt;/code&gt; &lt;code&gt;./store/monetization/actions.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async refreshUserBTP({ commit, dispatch, state }) {
    if (getCoilRefresh()) {
      try {
        const response: AxiosResponse = await coil.getUserRefresh(
          getCoilRefresh() as string
        )
        const data: ICoilUserTokenResponse = response.data
        // https://stackoverflow.com/a/32108184/295606
        if (
          data &amp;amp;&amp;amp;
          Object.keys(data).length !== 0 &amp;amp;&amp;amp;
          data.constructor === Object
        ) {
          await commit("setCoilToken", data.access_token)
          saveCoilRefresh(data.refresh_token)
          if (!state.requestError) {
            await dispatch("getUserBTP")
            await commit("setRequestError", false)
          }
        } else {
          await commit("setRequestError", true)
        }
      } catch (error) {
        await dispatch("checkCoilError", error)
      }
    } else {
      await commit("setRequestError", true)
      await commit(
        "main/addNotification",
        {
          content: "Error authorising web monetization.",
          color: "error",
        },
        { root: true }
      )
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You'll note that there are profoundly numerous points in the process where things can go wrong and &lt;code&gt;try&lt;/code&gt; / &lt;code&gt;catch&lt;/code&gt; is triggered.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;frontend&lt;/code&gt; api request in &lt;code&gt;./api/coil.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async getUserRefresh(refreshToken: string) {
    const data: ICoilUserTokenRefreshRequest = {
      refresh_token: refreshToken,
      grant_type: "refresh_token",
      scope: "simple_wm openid",
    }
    return await axios.post&amp;lt;ICoilUserTokenResponse&amp;gt;(
      `${process.env.apiUrl}/api/v1/coil/proxy/oauth/token`,
      data,
      requestHeaders()
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With that you can get your BTP token, as described above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stop / Revoke streaming in response to your subscriber's wishes
&lt;/h3&gt;

&lt;p&gt;Technically, all that's required to stop the process is to remove the wallet pointer in the metatag.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;frontend&lt;/code&gt; &lt;code&gt;./store/monetization/actions.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async stopMonetizationStream({ commit }) {
    await commit("setIsStreaming", false)
    await commit("setCoilPointer", "")
    await commit(
      "main/addNotification",
      {
        content: "You have stopped web monetization.",
        color: "success",
      },
      { root: true }
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Cascading back up the code, you call &lt;code&gt;this.setMonetizationTag()&lt;/code&gt; which removes the metatag pointer.&lt;/p&gt;

&lt;p&gt;From the &lt;code&gt;frontend&lt;/code&gt; component &lt;code&gt;./components/monetization/MenuToggle.vue&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (monetizationTag &amp;amp;&amp;amp; !this.coilPointer) {
  monetizationTag.remove()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Revoking&lt;/strong&gt; is slightly more convoluted. If a subscriber revokes, you need their &lt;code&gt;authentication_token&lt;/code&gt; to perform the action. However, their token may no longer be valid, so you may need to first refresh their token and only &lt;em&gt;then&lt;/em&gt; revoke your access to their keys.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;frontend&lt;/code&gt; &lt;code&gt;./store/monetization/actions.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async revokeMonetization({ commit, dispatch, state }) {
    await dispatch("stopMonetizationStream")
    if (state.coilToken) {
      await coil.revokeUserResource(state.coilToken)
      await commit("setCoilToken", "")
    } else if (getCoilRefresh()) {
      // Counterintuitively, we need a new user_token to request the revoke
      try {
        const response: AxiosResponse = await coil.getUserRefresh(
          getCoilRefresh() as string
        )
        const data: ICoilUserTokenResponse = response.data
        if (
          data &amp;amp;&amp;amp;
          Object.keys(data).length !== 0 &amp;amp;&amp;amp;
          data.constructor === Object
        )
          await coil.revokeUserResource(data.access_token)
      } catch (error) {
        await dispatch("checkCoilError", error)
      }
    }
    if (getCoilRefresh()) removeCoilRefresh()
    await commit(
      "main/addNotification",
      {
        content: "You have successfully revoked web monetization.",
        color: "success",
      },
      { root: true }
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With the &lt;code&gt;frontend&lt;/code&gt; api request in &lt;code&gt;./api/coil.ts&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async revokeUserResource(token: string) {
    const data: ICoilUserTokenRevokeRequest = {
      token,
    }
    return await axios.post(
      `${process.env.apiUrl}/api/v1/coil/proxy/oauth/token/revocation`,
      data,
      requestHeaders()
    )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There is no response to a revoke request. However, remember we adopted an ethical design approach. If our subscriber has deliberately cleared their cookies, we no longer have access to their &lt;code&gt;refresh_token&lt;/code&gt; anyway, and so no longer have access to their account.&lt;/p&gt;




&lt;p&gt;And that's it (so far). There's more we can do ... split payments (proportionally specifying which wallet pointer is monetized), opening up sections of your app to streaming subscribers, etc. All of that requires a rock-solid payment process, and I hope this guide helps save you at least some time and frustration in achieving that.&lt;/p&gt;

&lt;p&gt;Please let me know in the comments what you think, what I missed, and what can be fixed / improved.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;For both this guide, and for those of you wishing to go further and add things like scheduled token refresh, proportional payments, etc.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.coil.com/docs/dev/web-monetization" rel="noopener noreferrer"&gt;Coil Web Monetization documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webmonetization.org/docs/api" rel="noopener noreferrer"&gt;Web Monetization documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://javascript.info/settimeout-setinterval" rel="noopener noreferrer"&gt;Scheduling: setTimeout and setInterval&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>grantreports</category>
      <category>publishing</category>
      <category>ethics</category>
      <category>scifi</category>
    </item>
    <item>
      <title>Qwyre.com - What does a minimum lovable product even look like?</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Tue, 10 Aug 2021 16:20:12 +0000</pubDate>
      <link>https://community.interledger.org/qwyre/qwyre-com-what-does-a-minimum-loveable-product-even-look-like-22bm</link>
      <guid>https://community.interledger.org/qwyre/qwyre-com-what-does-a-minimum-loveable-product-even-look-like-22bm</guid>
      <description>&lt;p&gt;One of the management-speak fads of the last decade has been the &lt;a href="https://en.wikipedia.org/wiki/Minimum_viable_product" rel="noopener noreferrer"&gt;minimum viable product&lt;/a&gt; which, instinctively, we probably all know is wrong. It leaves us with scaffolding and dodgy wiring. Made things that are difficult to use, fail easily, and add to the noise and clutter of the world.&lt;/p&gt;

&lt;p&gt;They can be important early steps, but they're difficult to "love". And, if what we want is people to switch to wanting to use what we make, we need to offer at least a &lt;em&gt;modicum&lt;/em&gt; of enjoyment to these fledgling efforts.&lt;/p&gt;

&lt;p&gt;This approach is the &lt;strong&gt;minimum lovable product&lt;/strong&gt;. Unlike MVP, MLP doesn't seem to have an established parent, but the earliest visual representation of this I can find is from Jussi Pasanen:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-515301088660959233-135" src="https://platform.twitter.com/embed/Tweet.html?id=515301088660959233"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-515301088660959233-135');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=515301088660959233&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.crisp.se/2016/01/25/henrikkniberg/making-sense-of-mvp" rel="noopener noreferrer"&gt;Henrik Kniberg&lt;/a&gt;, writing in 2016, presented a more visual way or representing this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/7fL50Inbm-iZ7w5GquE_CvuHH5A-lk4alG-LmlGGdcU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzVnbTBhdHU3/YXdrN3JhNW1nNGp4/LnBuZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/7fL50Inbm-iZ7w5GquE_CvuHH5A-lk4alG-LmlGGdcU/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzVnbTBhdHU3/YXdrN3JhNW1nNGp4/LnBuZw" alt="Minimum lovable product, Henrik Kniberg" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Problem is, this implies you know what all of these intermediate things are when all you've got is this wheel you invented. Except ... none of us invented a wheel in a world of pedestrians. We're iterating on what exists.&lt;/p&gt;

&lt;p&gt;In the case of Qwyre, that means there are already many ways to convert a document to an epub, and multiple ways to publish them. I know what my iteration &lt;a href="https://community.webmonetization.org/qwyre/qwyre-com-a-framework-for-ethics-and-technical-development-in-fiction-publishing-3nln" rel="noopener noreferrer"&gt;looks like at the end&lt;/a&gt;, but I want an interim release, and that release needs to be useful.&lt;/p&gt;

&lt;p&gt;Useful to me so I can get feedback and test how webmonetization works in production, but definitely useful to people so that they want to give me that feedback in the first place.&lt;/p&gt;

&lt;p&gt;Originally, I thought I'd go as far as releasing an epub converter and call that &lt;em&gt;interim&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/kEUJGLB8d9Qn4Ge_Dn3DHzYMZ8JlEN-258eaUqF1Tsk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3k4Z2RuN2I1/ODd6ZTN1NXc0djZ0/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/kEUJGLB8d9Qn4Ge_Dn3DHzYMZ8JlEN-258eaUqF1Tsk/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL3k4Z2RuN2I1/ODd6ZTN1NXc0djZ0/LmpwZw" alt="Qwyre.com Epub conversion step" width="571" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The more I sat with that, though, the less enjoyable that became. The process of conversion is easy-enough, and the design - while interim - is inoffensive. I'm a professional writer, and this is also a tool I'd like to use. &lt;/p&gt;

&lt;p&gt;But ... my workflow, using my own app, was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate the epub,&lt;/li&gt;
&lt;li&gt;Download it,&lt;/li&gt;
&lt;li&gt;Import it into Calibre,&lt;/li&gt;
&lt;li&gt;Check that it looks ok.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot of effort, and distinctly displeasing. I can't ask anyone else to struggle that way.&lt;/p&gt;

&lt;p&gt;I decided that the interim app needs to have a functioning ereader. But you can't have a MVP ereader in this here 2021. It has to be an ereader. Which led me to the rabbit-hole of Epub.js, still the only mostly-functioning open-source browser-based ereader library:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/futurepress" rel="noopener noreferrer"&gt;
        futurepress
      &lt;/a&gt; / &lt;a href="https://github.com/futurepress/epub.js" rel="noopener noreferrer"&gt;
        epub.js
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Enhanced eBooks in the browser.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;There is &lt;em&gt;documentation&lt;/em&gt;, but no actual developer manual, and definitely no tutorials. The API appears to have changed without notice, so what works in other packages doesn't work anymore. It's a bit tedious. I'm also a little nervous as to how large it is, but I'll see how that goes once I get to the build point.&lt;/p&gt;

&lt;p&gt;A large-scale digression over the last five weeks means this is possible:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/L2kpkRSms9l2d2C6_2RDUWP2vClTfuyriPG0T20Ottg/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL20zdm1oMTdy/Y3JlcThjYTd6cDR0/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/L2kpkRSms9l2d2C6_2RDUWP2vClTfuyriPG0T20Ottg/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL20zdm1oMTdy/Y3JlcThjYTd6cDR0/LmpwZw" alt="Qwyre.com Epub reader, showing the menu page for 'Our Memory Like Dust'" width="574" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app is a &lt;a href="https://web.dev/progressive-web-apps/" rel="noopener noreferrer"&gt;Progressive Web App&lt;/a&gt;, and I'm using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API" rel="noopener noreferrer"&gt;IndexedDB&lt;/a&gt; to store data in the browser to make the user-experience truly offline. You can store all your epubs in the app, just like a real reader, and it is fully mobile responsive, meaning swipes and taps work as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/eDW90RJaUDa8C2WL34Svx0l6GZ2ibaHdHRb4XkauEJg/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL29lMnFlYnVy/ano5dGpmODdmZGgw/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/eDW90RJaUDa8C2WL34Svx0l6GZ2ibaHdHRb4XkauEJg/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzL29lMnFlYnVy/ano5dGpmODdmZGgw/LmpwZw" alt="Qwyre.com landing page showing epub and conversion libraries" width="574" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My workflow now is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload and convert a doc to epub,&lt;/li&gt;
&lt;li&gt;Read my epub...&lt;/li&gt;
&lt;li&gt;Get lost importing and reading other ebooks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which is the way it's supposed to be.&lt;/p&gt;

&lt;p&gt;The app itself works fine for release, but I still want to get basic webmonetization implemented, and then it should be ready to deploy for interim release. I've just picked up another project, so I'm going to be delayed for a few weeks, but hopefully by early September.&lt;/p&gt;

&lt;p&gt;One thing I'd like to know, though (for those who read this far) ... what is your favourite ereader app (not device), and what features makes it special for you?&lt;/p&gt;

</description>
      <category>grantreports</category>
      <category>publishing</category>
      <category>ethics</category>
      <category>scifi</category>
    </item>
    <item>
      <title>Qwyre.com - a framework for ethics and technical development in fiction publishing</title>
      <dc:creator>Gavin Chait</dc:creator>
      <pubDate>Wed, 30 Jun 2021 17:53:26 +0000</pubDate>
      <link>https://community.interledger.org/qwyre/qwyre-com-a-framework-for-ethics-and-technical-development-in-fiction-publishing-3nln</link>
      <guid>https://community.interledger.org/qwyre/qwyre-com-a-framework-for-ethics-and-technical-development-in-fiction-publishing-3nln</guid>
      <description>&lt;p&gt;Two months after my dad died, I was sitting on a beach in the Philippines writing my first novel. My dad's passing was expected. My writing trip long-planned. It's the sequence that wasn't the one I had hoped.&lt;/p&gt;

&lt;p&gt;I wrote 2,000 words a day for a month, and, when it was done, the orbital prison of Tartarus and the remote Nigerian village of Ewuru were real.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://gavinchait.com/w/u7sfZq9nL2yh/" rel="noopener noreferrer"&gt;&lt;strong&gt;Lament for the Fallen&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Isolated and with his orbital city hiding in the rubble of a devastating war, Samara falls 35,000km to escape from the space-based prison of Tartarus, smashing into the jungle near an isolated Nigerian village. Struggling to heal, and hunted by a brutal warlord in a ruthless land, Samara searches for a way home to the woman he loves.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;iframe src="https://open.spotify.com/embed/playlist/5gUE1sa6B18iE7jNm58kfP" width="100%" height="380px"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I never expected an agent or publishing contract, didn't want to rely on Amazon, and so &lt;a href="https://gavinchait.com" rel="noopener noreferrer"&gt;I rolled my own&lt;/a&gt;. Within a few months, my long-term software roadmap became moot when Penguin Random House picked up LAMENT for publication. The world of creative self-publishing stops at the border of formal publication.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/turukawa" rel="noopener noreferrer"&gt;
        turukawa
      &lt;/a&gt; / &lt;a href="https://github.com/turukawa/whyqd" rel="noopener noreferrer"&gt;
        whyqd
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Whyqd is an extensible object-based wiki bringing revision control, content presentation, forking and embedding to any type of digital object.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;A publishing house must have a list of titles, each within a set of genres conforming to the topics in the average bookshop. They make their money off their handful of giants, and do little to promote or develop the careers of their &lt;em&gt;mid-list&lt;/em&gt; writers. You are expected to promote your own work, but you can't sell your own work. Twitter ok. Experimental self-publishing definitely not.&lt;/p&gt;

&lt;p&gt;The challenge with &lt;em&gt;lists&lt;/em&gt; and &lt;em&gt;genres&lt;/em&gt; is that success is a game of whack-a-mole. Success leads to copycats, leads to perpetuation of specific norms. For African writers, this has meant we need to conform to an industry notion satirised by Binyavanga Wainaina in his frustrated guide on &lt;a href="https://granta.com/how-to-write-about-africa/" rel="noopener noreferrer"&gt;&lt;em&gt;How to Write About Africa&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Never have a picture of a well-adjusted African on the cover of your book, or in it, unless that African has won the Nobel Prize. An AK-47, prominent ribs, naked breasts: use these. If you must include an African, make sure you get one in Masai or Zulu or Dogon dress.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A continent of 54 countries, countless ethnicities, and over a billion people reduced to a cartoon cameo in the mid-list.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/c-jSQD5FVxE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  A framework for African collaborative publishing
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Umumtu ngumuntu ngabangtu&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The African philosophy of &lt;em&gt;Ubuntu&lt;/em&gt; can be summarised as "I am because we are." As I writer, I am both reader and writer because there are other writers. There is no line between writer and reader just as there is no line between genres, stories and influences.&lt;/p&gt;

&lt;p&gt;Qwyre starts with the following simple principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All Qwyre users are creators,&lt;/li&gt;
&lt;li&gt;A creator can perform any task of:

&lt;ul&gt;
&lt;li&gt;Reading,&lt;/li&gt;
&lt;li&gt;Writing,&lt;/li&gt;
&lt;li&gt;Editing,&lt;/li&gt;
&lt;li&gt;Curating.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The path to publication requires appointing an editor and letting them decide when a creative work is ready,&lt;/li&gt;

&lt;li&gt;Editors and curators are first-class citizens in the creative process and should be paid,&lt;/li&gt;

&lt;li&gt;An forum of peers is there to resolve disputes,&lt;/li&gt;

&lt;li&gt;Popularity can be gamed and is no guide to what a reader may enjoy,&lt;/li&gt;

&lt;li&gt;All writers should have an equal shot at being read.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In practice, this implies &lt;em&gt;moderated publication&lt;/em&gt;. There is no gatekeeping. We do not assign an editor and decide who gets published. All we require is that a writer recruit and appoint an editor, and that their editor gets to decide when their work is ready for publication. This will require an appeals process, and so we need an editors forum.&lt;/p&gt;

&lt;p&gt;This leads to the following structured components to support a Qwyre workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EPUB generator: convert a &lt;code&gt;.docx&lt;/code&gt; file to a standards-compliant &lt;code&gt;.epub&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Editor recruitment: manage the recruitment and contractual agreement between a writer and editor,&lt;/li&gt;
&lt;li&gt;eReader: an online standards-compliant EPUB reader,&lt;/li&gt;
&lt;li&gt;Web monetisation: books can be read by streaming and, once paid for via monetisation, can be downloaded as an epub,&lt;/li&gt;
&lt;li&gt;Editor's forum: a formal review process to permit dispute resolution between writers and their editors,&lt;/li&gt;
&lt;li&gt;Curation: create lists of works which can be shared and promoted,&lt;/li&gt;
&lt;li&gt;Search and discovery: a neutral, automated method to categorise and assess creative works to support reader discovery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this time, given the diverse needs of writers and their preferred ways of working, Qwyre will not offer any online editing or writing support. These are to take place offsite.&lt;/p&gt;

&lt;p&gt;Where possible, components will be released as free-standing apps as they are developed. The most immediately obvious of these are the EPUB generator, and the ereader app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nuxt.js and FastAPI as our technical stacks
&lt;/h2&gt;

&lt;p&gt;Qwyre will be a progressive web app (PWA) with offline support for the reader's library of ebooks. Books can be bought as epubs, or streamed via web monetisation. What a reader "buys" via web monetisation, they own. Once they have streamed an entire book, they are able to download an epub version of the work.&lt;/p&gt;

&lt;p&gt;I have developed on Django for years and enjoyed it, and its documentation. Recently, however, I've wanted to try something new and more in line with my current standards-based approach to data curation. Python's support for types has evolved, and the most modern and mature framework for working with Pydantic is &lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; which comes with a Docker base project with PostreSQL and Vue.js. I didn't want to faff too much with Vue, and set out to produce a version for &lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; (which is to Vue what Next is to React) as well as replacing Vuetify with &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; for the user interface design. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/whythawk" rel="noopener noreferrer"&gt;
        whythawk
      &lt;/a&gt; / &lt;a href="https://github.com/whythawk/nuxt-for-fastapi" rel="noopener noreferrer"&gt;
        nuxt-for-fastapi
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Nuxt.js frontend drop-in replacement for Vue.js FastAPI base project generator.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This provides a solid starting point for further development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current development: Chapisha and EPUB Generator PWA
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Chapisha&lt;/strong&gt; provides an intuitive method for converting a well-formatted Microsoft Word &lt;code&gt;.docx&lt;/code&gt; file into a standards-compliant EPUB3 ebook. I developed the standalone Python package to support a Nuxt.js progressive web application.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/whythawk" rel="noopener noreferrer"&gt;
        whythawk
      &lt;/a&gt; / &lt;a href="https://github.com/whythawk/chapisha" rel="noopener noreferrer"&gt;
        chapisha
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Python-based docx to standards-compliant epub3 conversion
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The core functionality for the EPUB convertor is almost complete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.interledger.org/images/eQ3gVOpGA4ufYRlVoQrhyaJzJw47WFhai1pYLpgZ5zM/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzk4ejJwZHd2/OW1iM3hhY2ZlYW15/LmpwZw" class="article-body-image-wrapper"&gt;&lt;img src="https://community.interledger.org/images/eQ3gVOpGA4ufYRlVoQrhyaJzJw47WFhai1pYLpgZ5zM/rt:fit/w:800/g:sm/q:0/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkuaW50ZXJs/ZWRnZXIub3JnL3Jl/bW90ZWltYWdlcy91/cGxvYWRzL2FydGlj/bGVzLzk4ejJwZHd2/OW1iM3hhY2ZlYW15/LmpwZw" alt="A screenshot of the current development landing page for Qwyre.com" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next up is to work with &lt;a href="https://help.coil.com/docs/dev/web-monetization/index.html" rel="noopener noreferrer"&gt;Coil's web monetisation API&lt;/a&gt; and test streaming payments to support EPUB conversion. The specific mechanism is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A creator provides all the content and metadata required to generate an EPUB in the PWA,&lt;/li&gt;
&lt;li&gt;Data only transfer from the creator to the Qwyre server during the generation process,&lt;/li&gt;
&lt;li&gt;During generation, they can stream payments to support the app,&lt;/li&gt;
&lt;li&gt;Streaming is monitored directly using websockets giving the creator feedback on the generation process, and costs,&lt;/li&gt;
&lt;li&gt;On completion, all creator data is deleted from the server and returned to the creator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm hoping to complete this in mid-July and deploy a test server with the EPUB generator for public testing and use by the end of July 2021. In my next update, I'll hopefully have a link for you to go test what I built so far.&lt;/p&gt;

&lt;p&gt;I'm keen on feedback and discussion, so please get in contact and let me know your thoughts or concerns.&lt;/p&gt;

</description>
      <category>grantreports</category>
      <category>publishing</category>
      <category>ethics</category>
      <category>scifi</category>
    </item>
  </channel>
</rss>
