Introducing Epoxy, Monetizable WebSocket proxies
What if there is no line, no boundary between tor and clearnet (the ‘normal’ web)? What if a Damus user on iPhone can talk to their friend’s private TOR relay? Let’s find out together what can happen by using this new tool which I call Nostr Epoxy! And it’s scalable, too!
TL;DR Demo video: naddr1qq…zpll
Phone can reach clearweb relay, but cannot connect to TOR relay | Phone can reach TOR relay |
Proxies have been around forever and can be a powerful tool in getting around network restrictions. In the context of nostr, currently everyone connects to a relay directly, which you may not always want. The relay can keep track of your IP address and build a profile, or your device might be behind a very restrictive firewall which prevents you from reaching a relay.
From this problem, the idea for proxy relays was born. We wanted to reach a TOR relay, without having to install any TOR stuff on our devices. Which on iPhones is especially challenging. So the idea is that we want to extend a relay’s functionality so that it can instead of processing requests for itself, it can pass on the requests to another relay that they themselves can reach.
The easiest solution to get this working is in the form of a Reverse Proxy, which you can put in front of any relay, which by default tunnels the traffic to the operator’s own relay, for example a Strfry instance. That way the relay implementation doesn’t have to change and the user doesn’t have to be aware that this relay endpoint can also proxy to other relays. The example implementation is called NERP - Nostr Epoxy Reverse Proxy.
Reverse proxy default behaviour | Reverse proxy after proxy request |
As shown above the default is to connect to the operator’s own relay. So how do we create the proxied connection? Well we send the following request over our websocket:
["PROXY", "wss://relay.com"]
Which asks the proxy to change the target from it’s own back-end to whatever we specify. In this case wss://relay.com
. However, it turns out this relay doesn’t live in a fiat world, it responds with:
["PROXY", "PAYMENT_REQURIED", <payment_request>]
where <payment_request>
is an object:
{
"price": "1",
"unit": "sat",
"mint": "https://some.mint.cash/",
}
Which brings us to…
💰 Monetizing proxies
Inevitably we’re going to hit the “Who’s gonna run the proxies!?” question. Well you are, because you want to stack some sats. Therefore we added monetization to these proxies, which is what sets this project apart from current-day proxies on the internet. When the client is hit with the PAYMENT_REQUIRED
message above, it can retry the first message, but with an additional field, a cashu payment.
["PROXY", "wss://relay.com", "cashuAeyJ0..."]
We’re now paying a couple sats, (the price above is in sats per minute). If the proxy is happy with our payment, it will start a timer and set up the connection to the target relay. Indicated by a ["PROXY", "CONNECTED"]
message, after which we can go about our relay business until the bought time runs out. After which the proxy will close our connection with code 1000
, reason: PROXY: Connection Bankrupted
.
The nice thing here is that introducing payments can reduce spam significantly.
Why Cashu?
Because Cashu eCash allows us to finally make micro-payments viable and scalable, it can help us to bring an honest monetary reward to the people that do the hard work of maintaining the free and open internet. If you provide a proxy, you get paid and therefore you’re incentivised to keep the infrastructure up and running.
Cashu tokens are a very efficient way of sending payments directly. Unlike Lightning, it does not require a back and forth between the two transacting parties. Because cashu tokens can be sent as a string of text, you can attach it to any request/data you’re sending around. Even at rest, the money will still be ‘physically’ there. That means there’s no back and forth required to send a payment, like we do on lightning, and people don’t need perfect connectivity.
💭 Some philosophy
Until now, all web applications have been strongly coupled to the transportation methods they use, mainly http(s). Nostr changes this, because the data that feeds an application can now arrive over any transportation mechanism. WebSockets, Thumb-drives, QR, LoRa (radio) and Heck! even FM and pigeons will work!
Although the above point is not necessarily tied to the use of websocket proxies, it does underline the point I want to make: Because Nostr completely decouples applications from the transportation layer, We can get VERY creative in the ways in which we transport application data… Therefore, the boundaries of the current clearnet network become irrelevant.
Using the websocket proxies, we can now hop from clearnet to tor, to i2p, to hyper and back to clearnet. This significantly reduces the burden on the client by not having to ‘know’ how any of these other networks work, they won’t have to install anything, yet they can interact with relays on these lesser known networks.
⚙ Under the hood
Here’s a UMl example of a proxy (that does not have a default relay behind it) and the lifecycle of a proxied connection.
NIP-XX draft
I wrote a draft NIP on how to support proxying here.
🛝 Play with it!
I created a Dashboard to discover proxies and play with connecting to relays (multiple hops are possible).
Check out the route-builder tab, add a couple sats to the wallet and route some request through the proxies.