Web3 Without Wallet

This article started as a bear market distraction for all the sad builders in the Web3 space that lost some money in the last months. Just playing around with some Web3 tech I found interesting.

%[INVALID_URL]

After I wrote it, I got the impression it's an excellent way for Web2 devs to dip their toes into decentralized technology without being distracted by the money that usually pulls people into Web3.

There are two exciting decentralized technologies out there, and you can use both of them without cryptocurrencies: Radicle & IPFS.

This tutorial will allow you to try Web3 tech without selling your soul to the crypto gods!

D_D Newsletter CTA

Deploying a React SPA

We will deploy the default app create-react-app emits right after initialization. You don't need to understand React for this; it should work with every other framework that can emit an SPA.

Here is the first nice thing: you can use many of your Web2 dev skills in Web3. A decentralized app (DApp) will still run in the browser like a regular web application; it will just use tech that isn't centralized.

Prerequisites

Debian/Ubuntu, Git and a current Node.js installation.

Also: No crypto wallet!

Radicle

We will start by setting up version control with Radicle, a decentralized alternative to centralized services like GitHub or GitLab.

Installing Radicle

The easiest way to install Radicle is via apt.

These commands will update your package repos and install the Radicle CLI:

$ curl https://europe-west6-apt.pkg.dev/doc/repo-signing-key.gpg | sudo apt-key add - $ echo deb https://europe-west6-apt.pkg.dev/projects/radicle-services radicle-cli main | sudo tee -a /etc/apt/sources.list.d/radicle-registry.list $ sudo apt update $ sudo apt install radicle-cli

After this, you'll have a rad command.

Creating a Radicle Identity

The idea behind Radicle is that you sign all your code with a private key. Your identity isn't bound to a central service like GitHub. You have the key; you have the identity.

Nodes make up the Radicle network. They host your repos. These nodes join and leave the Radicle network, so if you send a new update to the network, you might be talking to a different node than when you uploaded your repo for the first time.

Since you signed the code with your private key, you can tell any node that you are the actual owner of it. All a node has to do is check your signature against a public key.

So, as with any decentralized system: keep your private key safe!

This command creates your Radicle identity:

$ rad auth

Radicle differentiates between peer IDs and personal URNs because every dev might want to use different computers, which can end up with various states of the code base.

Initializing the React App

Now, we actually need a codebase we can put into Git and, in turn, onto the Radicle network. For this, we use create-react-app, a tool that will set up a bare SPA for us.

Use this command to get a fresh React app:

$ npx create-react-app my-first-dapp $ cd my-first-dapp

Initializing the Radicle Project

Now, we need to tell Radicle about the Git repository that create-react-app just created for us. This will start the signing process we talked about above. You can prove your identity to the Radicle network later when you want to push updates.

$ rad init

Pushing to the Radicle Network

Next, we need to upload our fresh repo to the network, to make it available to other devs or ourselves on different devices.

$ rad push

Radicle will ask you for a preferred network, and after that, it will upload your repo. You can choose the default, which should be willow.

If you get an ssh-agent error run val $(ssh-agent) > /dev/null and then rad auth again.

Updating Code

Now that the initial version of the codebase is on the Radicle network let's make a change and push it!

For this, replace the content of src/App.js with the following code:

import "./App.css"

function App() {
  return (
    <div className="App">
      <h1>My first DApp!</h1>
    </div>
  )
}

export default App

And run these commands:

$ git add src/App.js $ git commit -m "Change text" $ git push $ rad push

To understand how these commands work together, let's look at the following image:

Radicle Network

The first two commands add a new change to the stage and commit it with a message. Nothing special here. The change will end up in the "Local Git Repository."

The third command will push the local changes to a remote. But in the case of our Radicle setup, this isn't a remote server; it's a local server managed by Radicle. So, after this command, the change is still local to our computer.

The rad push command now will sync our "Radicle Git Repository" with the Radicle network. This is the equivalent of git push for centralized services like GitHub.

Cloning the Repo on Another Device

Finally, if someone wants to work on the code from another location, they can either use the Radicle web interface to get the project ID, or you can get it locally with this command:

$ rad .

It should look like this: rad:git:hnrkc...

Everyone you give this ID can clone the repo with the Radicle CLI:

$ rad clone --seed willow.radicle.garden

The seed can differ depending on how you initialize your repo, but the one in the example is the default.

IPFS

Now that our development setup is ready and all our fellow devs can access the code, we want to deploy the app so our users can do something with it. For this, we will deploy the code to IPFS.

Some Background on IPFS

IPFS is a protocol, just like HTTP. The difference is that IPFS URLs are hashes of the content they address; data is content addressed. Changing content also changes the address.

This allows for decentralized storage of data worldwide in the IPFS network. If someone messes with your data, the URL changes too. So, if you keep using your old URL, your data will be the same, independently of its location on the network.

But this doesn't solve the problem of persisting that data. If you have a hash to information that isn't on the network, you don't get the wrong data, but that's because you don't get any data.

To make sure the network persists your data, IPFS uses pinning services. You upload your data to a pinning service and pay it some money, so it keeps it up. There are also some free pinning services, like the decentralized one called Filecoin.

As the name implies, this service uses a cryptocurrency, but at least right now, it comes with a free storage contingent, so, again, no crypto wallet is needed.

Also, if users think your DApp is pretty cool, they might pin it on their own IPFS nodes, and it will stay online even if you aren't around anymore to maintain it.

Signup to Web3.Storage

In this tutorial, we will use Web3.Storage, a tool & service that uploads our DApp to Filecoin to make it available via IPFS.

To get started, you need to create a Web3.Storage account. While IPFS and Filecoin are decentralized, Web3.Storage itself isn't.

Creating an API Token

Next, you have to create an API token, so your deploy script can access the Web3.Storage service.

Creating the Deployment Script

Create a deploy.mjs script inside your project directory and add the following code:

Don't forget to add the correct API key!

import { URL } from "url"
import * as path from "path"
import { getFilesFromPath, Web3Storage } from "web3.storage"

const API_TOKEN = "YOUR_API_TOKEN"

async function deploy() {
  const client = new Web3Storage({ token: API_TOKEN })

  const files = await getFilesFromPath(
    path.join(new URL(".", import.meta.url).pathname, "build")
  )
  const cid = await client.put(files, { wrapWithDirectory: false })

  console.log(`https://${cid}.ipfs.dweb.link/index.html`)
}

deploy()

This will connect to the Web3.Storage service, gather all files in the build directory and then put them (through Web3.Storage) onto the Filecoin network.

The wrapWithDirectory: false flag ensures we find our files in the root of the URL and not wrapped with a build directory.

In the end, the script will log an URL to an IPFS gateway that includes the CID, which is the hash of our deployed files.

But since it's decentralized, you could also use a different IPFS gateway with the same CID.

IPFS Network

You could even run your own IPFS node locally and tell it to pin your content there. Your computer would become part of the IPFS network, and other IPFS nodes in your local network could fetch their data from there.

IPFS Network

Building & Deploying to IPFS

Finally, the big moment. First, we have to build the React app, which will end up in the build directory, then we have to call our script to deploy it.

$ npm run build $ node deploy.mjs

Everything from the build directory will be accessible via the URL logged in at the end.

The URL will look something like this:

https://.ipfs.dweb.link/index.html

The CID will change every time we do a new deployment.

Bonus: Setting a Custom Domain

Now, requiring your users to remember these big CIDs isn't good UX, nor will they know when there is a new CID after every deployment.

This is why we put a human-readable domain in front of our deployments.

You can do so by creating two records for the custom domain you own.

An A or Alias record that points to a gateway IP or domain:

209.94.90.1

A TXT record called _dnslink that has the following content:

dnslink=/ipfs/

Your domain resolves to a gateway that can use the _dnslink record/subdomain to resolve the data stored on IPFS. Users can open the deployed page in their browser like every other website.

D_D Newsletter CTA

Summary

Using decentralized technology isn't hard and doesn't require getting a wallet and cryptocurrency to start, not for yourself or your users.

With Radicle and IPFS, you get some decentralization benefits, like censorship resistance, without the need to go all-in.

You can run Radicle and IPFS nodes on your own hardware, in your own local networks, if you like, and other people can host your content if they think it's worth it. No single entity can delete your data from those networks.