dapps today still rely on centrally hosted endpoints to do the bulk of their work, so dapp users are not fully independent: they cannot take the front end, run with it and still use the app living on the blockchain.
See: https://moxie.org/2022/01/07/web3-first-impressions.html
See the notion of "right to exit": https://medium.com/@danfinlay/what-moxie-missed-on-web3-wallets-8dc572e7f39b
Standardize the ways backend services are declared, and provide end-users with a Single-URL entrypoint passed to decentralized apps. Include a discovery mechanisms ultimately providing all necessary backend services for the app to function. Provide what is necessary for someone to quickly spin up a business (get paid and cover his costs) by participating in such an economy.
This would be an actual implementation of the "right to exit": provide the ability to hop providers, but keeping the backend services specs unaltered.
This provides censorship-resistance, anti-fragility and independance to users of the decentralized web, along with a business for anyone with the competencies to run such services.
Let's say a social app needs 5 backend services to function properly:
- Ethereum blockchain RPC endpoint, with means to read and write
- a GraphQL endpoint to a Subgraph, indexing the dapp's data on chain
- an image resizer, for profile pictures and uploads
- an IPFS pinning service (for writing)
- an IPFS gateway (for reading)
Two optional services are supported by the dapp, which gracefully degrades the experience if they are not available (as they are most probably much pricier):
- a full-text search on your social feed
- a algorithmic feed organizer (like what Facebook and Twitter do today)
Application specify their requirements in such a form:
name: my_social_app
version: 0.0.1
services:
- name: eth_node
optional: false
spec:
kind: blockchains.w3g/v1/Ethereum
archive: true
client: geth
version: v1.10.15+
rpc: true
endpoints:
- getBlock
- getLogs
- sendRawTransaction
- name: resizer # https://imgproxy.net/
routing:
kind: FromRoot # request that the provided endpoint handles requests at root, and forward to the right service, never a subpath
spec:
kind: graphics.w3g/v1/imgproxy
version: v3.1.0
minimum_patch_version: true
minimum_minor_version: false # Implicit, as with `minimum_major_version` ?
- name: ipfs
spec:
kind: storage.w3g/v1/IPFS
features:
- read
- write
- pin
- name: uniswapv2subgraph
routing:
kind: MapPath
from_path: /subgraph/id/Qm1230918230192830129380192
to_path: /subgraph/id/Qm1230918230192830129380192
spec:
kind: subgraphs.w3g/v1/Subgraph
id: Qm1230918230192830129380192
#or name: uniswap/exchange-v2
only_read_entities:
- Entity1
- Entity2
- name: full_text
optional: true
spec:
kind: socialdapp.com/v1beta/FullTextIndex
short_queries_only: true
- name: sorter
optional: true
spec:
kind: socialdapp.com/v1beta/FeedSorter
ad_revenue_optimized_algo: false
happiness_optimized_algo: true
All the kind
are predefined service types, HTTPS/2.0 based, and imply their respective method of communications (JSON-RPC, or REST, or gRPC, or whatever).
The specs for each kind
element specify what is being negotiated between the client (which could be a flat HTML page on a USB key) and the gateway service.
For example, if the gateway service doesn't have a geth v1.9.10+
Ethereum node, it can't provide the service named eth_node
. On the flip side, when a W3G endpoint is called for the eth_node
service, the provider promises to fulfill all the declared specs.
An application can be designed with degraded backends in mind (feature depth), meaning that an end-user might not want to pay for optional
features, or would get to choose one of many options, potentially priced differently.
In the example above, it seems that the application could use a FeedSorter
that is either ads-revenue-optimized (think Facebook), or happiness-optimized (a brave new world) algorithms. Users could expect the prices to be different here, and could choose, and the app would adjust accordingly.
Along with some sampled usage of users of that bundle of services, the specification itself provides rich information to allow a W3G Service Provider to make informed cost and pricing decisions.
A W3G Provider can offer metered or fixed price access. It can offer bundle of dapps (when the same services are proxied for different dapps). It can use a wide variety of marketing techniques to reach and promote its service. It can use a wide variety of methods to collect payments from end users, and pay backend services (like The Graph subgraphs). It can use various login processes to authenticate its users, or no login at all (say payment was done on-chain), and there's really no reason to collect any other info in order to provide the service.
Some dapps like to subsidize the costs for their users. By dealing with Providers, and offering coupons, dapps can pay providers yet let end users choose which Provider they prefer.
The W3G software stack is there to help those service providers manage costs and pricing, handling payments from clients, and payments to third-party backend providers (if they themselves do not or cannot run certain services, but still want to act as a unique gateway), management of payment channels, credit card payments, metering and quotas, coupon codes, etc..
Once a W3G has accepted a spec and agreed with an end-user on pricing, billing and depth of features, it provides the end user with a single URL, that:
- allows for the discovery of the endpoints providing each service, along with their authentication methods
- can keep track of consumption, usage, depletion of one's metered account, etc..
A URL could look like:
or
That endpoint could respond with a JSON file similar to:
services:
- name: eth_node
spec:
url: https://my_social_app_0_0_1_qmdd7a812f3d29192.myw3g.io/eth_node # Here, queries sent to `/eth_node` would strip `/eth_node` before forwarding to the backend service. Might not be cool for some services.
auth_query_param: token=ABCDEF1234567886431
- name: resizer
spec:
url: https://my_social_app_0_0_1_qmdd7a812f3d29192.myw3g.io # Notice how the gateway will forward requests at the ROOT of the server, directly to the `resizer` backend.
auth_bearer: ABCDEF1234567886431
headers:
X-Routing: "resizer"
- name: uniswapv2subgraph
spec:
url: https://my_social_app_0_0_1_qmdd7a812f3d29192.myw3g.io/subgraph/id/Qm1230918230192830129380192
auth_query_param: token=ABCDEF1234567886431
...
or
services:
- name: eth_node
spec:
url: https://gateway1.myw3g.io
auth_query_param: token=ABCDEF1234567886431
headers:
X-Spec: "my_social_app_0_0_1_qmdd7a812f3d29192" # Some info is needed by the W3G to understand where to route the requests
X-Routing: "eth_node" # for both authentication, and knowing which service of which spec is requested here.
- name: resizer
spec:
url: https://gateway3.myw3g.io
auth_bearer: ABCDEF1234567886431
headers:
X-Routing: "resizer"
...
Once this is received, the application can initialize the different libraries that are going to make use of those backend services, along with the authentication mechanisms.
From a W3G Provider's perspective, this design allows them to have a business and be quickly on the market. It provides great optimizations opportunities (in terms of optimizations of scale, unit economics, etc.).
From a dapp's perspective, it provides a clean way to declare what your app needs, and quickly reach out to providers for them to run those services, offering a basket of users. It allows them to subsidize providers to invert who pays (end-users or dapps).
From an end-user's perspective, you get freedom by choosing who your provider will be, anti-fragility in the apps you use and someone to talk to when backends have issues.