Aero Cast is a demo of a decentralized π and interactive π web feed.
Personal feeds are an integral part of the web. However, centralized social media platforms define how the user's feed is curated and threaten their users' privacy. At the same time, decentralized systems like RSS/Atom are not curated and don't have a feedback channel.
Put more generally, the fabric of the web is decentralized protocols but its applications are often centralized.
Aero Cast uses the Ethereum blockchain to build a decentralized feed on the application level, where users can subscribe to websites and choose their feed aggregator freely.
Aero Cast consists of the following components:
- Publishers create content. This could be any website such as a blog or news site.
- Aggregators are services that collect and curate published content on behalf of a subscriber.
- Subscribers are users who subscribe to publishers. They consume the content using an aggregator of their choice.
- Ethereum Blockchain provides a decentralized database to store subscriptions.
Of course, there can be many publishers, subscribers and aggregators.
At first, the publisher and subscriber must each deploy a smart contract on the blockchain (omitted from the figure for the sake of simplicity). When a user subscribes to a publisher, they add the publisher's address to the list of subscriptions stored in their contract. Additionally, the subscriber allows an aggregator of their choice to read the list of subscriptions from their contract.
When a publisher creates new content, an Ethereum event is emitted. Once the aggregator receives the event, he fetches the content from the publisher and provides it to the user as part of a curated feed.
The figure below illustrates the whole workflow on a technical level.
- A publisher creates a new instance of the Publisher contract on the blockchain. The contract contains the URL of the publisher's RSS feed. It is also used to emit an event when new content is created.
- The new Publisher contract is registered with a Registry contract, which serves as a lookup table for all contracts.
- The publisher stores the address of his contract on his publishing website (e.g. his blog).
- A subscriber creates a new instance of the Subscriber contract. The contract is later used to manage subscriptions.
- The new Subscriber contract is registered with the Registry Contract so that its address can be looked up by others.
- The subscriber registers with an aggregator of his choice. An aggregator is a web service, that collects and curates published content on behalf of the subscriber.
- During the registration, the aggregator fetches the subscriber's contract from the registry.
- The subscriber subscribes to the publisher's website.
- Upon subscribing, client-side Javascript requests the address of the subscriber's contract from the Registry contract.
- The publisher's contract address is added to the list of subscriptions of the subscriber's contract using a client-side Javascript request. The publisher is now subscribed.
- Adding to the list of subscriptions emits an Ethereum event. This enables aggregators to become aware of changes to subscriptions.
- The publisher creates new content on his website.
- This adds a reference to the new content to the publisher's contract.
- A new Ethereum event is emitted to notify listeners of the new content.
- When the aggregator's listener captures the event, he fetches the new content from the publisher's RSS feed.
- Next, the aggregator provides the content to the user as part of a curated feed.
The easiest way to set up Aero Cast is via Docker.
The following command spins up a preconfigured local Ganache blockchain, one aggregator and two publishers with sample data:
Linux / MacOS:
./example-run.sh
Windows (Powershell):
.\example-run.cmd
After startup, the following services are available:
URL(s) | Description | |
---|---|---|
Aggregator | http://127.0.0.1:8081 | Generates a feed of subscribed |
Publisher 1 | http://127.0.0.1:8082 http://127.0.0.1:8082/admin |
Displays all published articles Create a new article |
Publisher 2 | http://127.0.0.1:8083 http://127.0.0.1:8083/admin |
Displays all published articles Create a new article |
To interact with the local Ganache blockchain and its contracts, you need to set up Metamask accordingly.
Add a new network to Metamask using the following data:
Type | Value |
---|---|
Network name | Ganache localhost |
RPC URL | http://127.0.0.1:8545 |
Chain-ID | 31337 |
Currency Symbol | ETH |
One of the advantages of a local blockchain is that accounts don't change. This example has several accounts preconfigured. This means you can import these accounts to your Metamask. To interact as a subscriber with publishers and aggregators, use the following private key to import the account to your Metamask:
Type | Value |
---|---|
Account Address | 0x90F79bf6EB2c4f870365E785982E1f101E93b906 |
Private Key | 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6 |
Overview of all accounts and contracts (click to expand).
- Publisher 1: (1)
- Account: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
- PK: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
- Publisher 2: (2)
- Account: 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC
- PK: 0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
- Subscriber 1: (3)
- Account: 0x90F79bf6EB2c4f870365E785982E1f101E93b906
- PK: 0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6
- Subscriber 2: (4)
- Account: 0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65
- PK: 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a
- Registry: 0x5FbDB2315678afecb367f032d93F642f64180aa3
- Publisher 1: 0xa16E02E87b7454126E5E10d957A927A7F5B5d2be
- Publisher 2: 0xB7A5bd0345EF1Cc5E66bf61BdeC17D2461fBd968
- Subscriber 1: 0xeEBe00Ac0756308ac4AaBfD76c05c4F3088B8883
- Subscriber 2: 0x10C6E9530F1C1AF873a391030a1D9E8ed0630D26
First, we want the feed aggregator to fetch the content to which the subscriber has subscribed. Go to http://127.0.0.1:8081 and use the Metamask subscriber account to connect your wallet and enlist.
Now we want to subscribe to the two publishers. Open http://127.0.0.1:8082 and use the Metamask subscriber account to subscribe. If everything works, the aggregator will now receive an Ethereum event every time the publisher creates a new article. Do the same with the second publisher at http://127.0.0.1:8083.
Now the aggregator should already show you sample content from both publishers in your personal feed.
Let's have some fun and publish some content own content. Open the first publisher's admin interface at http://127.0.0.1:8082/admin and create a new article. This emits an event that the Aggregator picks up after a few seconds.
Open the aggregator at http://127.0.0.1:8082 and you should see your new article.
That's it π!
If you don't want to use the Docker-based quickstart, you can set up the project yourself. Note that the project requires recent versions of Java, Maven, Node, and NPM.
This project uses Ganache (a local Ethereum blockchain). Follow the steps below to set up Ganache.
Run the following commands within the contracts
folder to install all modules and Ganache:
npm install
npm install --global ganache
If the installation was successful, execute the following command within the contracts
folder to
start Ganache:
npm run ganache
If you want the blockchain to be persistent, use the following command:
npm run ganache-persistent
Now Ganache should be up and running!
Now we can deploy the various contracts on the Ganache blockchain.
The registry contract serves as a lookup table for all other contracts. Deploy the registry contract via Truffle migrations.
truffle migrate
Next, deploy the publisher via a Truffle script.
truffle exec scripts/create_publisher.js
Finally, deploy the subscriber contract.
truffle exec scripts/create_subscriber.js
Click to show contract address details.
- Registry: 0x5FbDB2315678afecb367f032d93F642f64180aa3
- Publisher: 0xa16E02E87b7454126E5E10d957A927A7F5B5d2be
- Subscriber: 0xB7A5bd0345EF1Cc5E66bf61BdeC17D2461fBd968
Now that the contracts are deployed, let's set up the Publisher App and Aggregator App. These are Java-based applications that provide a web-based frontend to interact with. On the backend, they interact with the deployed contracts.
To build the Apps, run the following command within the apps
folder:
mvn clean package
To start, run the following command within the apps
folder:
java -jar feed-publisher\target\publisher.jar \
--server.port=8082 \
--h2.server.port=9092 \
--spring.datasource.url="jdbc:h2:file:./pubdb;DATABASE_TO_LOWER=TRUE" \
--spring.datasource.username="sa" \
--spring.datasource.password="password" \
--eth.api.url="http://127.0.0.1:8345" \
--eth.registry.contractaddress="0x5FbDB2315678afecb367f032d93F642f64180aa3" \
--eth.publisher.privateKey="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" \
--eth.publisher.contractAddress="0xa16E02E87b7454126E5E10d957A927A7F5B5d2be" \
--app.baseURL="http://127.0.0.1:8082" \
--app.title="Publisher 1" \
--app.subTitle="A demo publisher" \
--app.iconPath="pub.ico"
If everything works, you should be able to access the web interfaces:
Public | Admin | |
---|---|---|
URL | http://127.0.0.1:8082 | http://127.0.0.1:8082/admin |
Description | Overview of published articles | Publish a new article |
To start, run the following command within the apps
folder:
java -jar feed-aggregator\target\aggregator.jar \
--server.port=8081 \
--h2.server.port=9091 \
--spring.datasource.url="jdbc:h2:file:./aggdb;DATABASE_TO_LOWER=TRUE" \
--spring.datasource.username="sa" \
--spring.datasource.password="password" \
--eth.api.url="http://127.0.0.1:8545" \
--eth.registry.contractaddress="0x5FbDB2315678afecb367f032d93F642f64180aa3"
If everything works, you should be able to access the web interface using this URL: http://127.0.0.1:8081
That's it. You have successfully set up the project. π
ActivityPub pursues similar goals using a different technical approach.
Please use the GitHub issue tracker to report any bugs.
If you would like to contribute code, fork the repository and send a pull request. When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible.
This project is distributed under the Apache License, Version 2.0 (see LICENSE file).
By submitting a pull request to this project, you agree to license your contribution under the Apache License, Version 2.0.