iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🐙

Platformless Application Development with Ceramic and IDX

に公開

The Trend toward Decentralization and De-platforming

Broadly speaking, I believe that decentralization and de-platforming across various fields and scales are currently mainstream trends.

To give a clear example, free-to-watch video content could previously only be broadcast by powerful television stations, but now individuals can distribute it using platforms like YouTube, gaining enough demand to even surpass television.

However, in the YouTube example, while the shift from TV to individuals succeeded in decentralizing content creators, from the perspective of YouTube as a giant platform, it is ultimately not decentralized. As mentioned in "What is happening in the entertainment content industry (Macro version)", by taking commissions through massive platformization, the people earning revenue have shifted from content creators to the platform side, and the power balance has tilted toward the platform.

Decentralized Finance (commonly known as DeFi) is a field attempting to break this cycle. Traditional finance requires central institutions such as governments, banks, and brokerage firms. Relying on these institutions for one's most important assets is considered risky (due to counterparty risk, lack of transparency, etc.), so the goal is to use decentralized technologies like blockchain to create finance that does not require such central authorities. (For details, see "Understanding the DeFi Mechanism in 10 Minutes: 8 Key Terms to Know")

There is also a trend toward decentralization in application development, which I would like to introduce here.

What is Application Decentralization?

Reflecting the trends of blockchain and Web 3.0, the decentralization of applications is a movement frequently seen recently. By developing what are called DApps (Decentralized Applications)—applications built on blockchain or decentralized networks—it is possible to realize applications that operate without a central administrator.

I believe the major characteristics of these are the absence of a centralized ID platform and the fact that they are not hosted on central platforms like the cloud. ID platforms can be disrupted by users managing their own identity and using key wallets to handle keys that prove their identity. The cloud is expected to be replaced by blockchain or decentralized networks.

Furthermore, wallets alone are insufficient to replace the Identity Layer. By introducing mechanisms like SSI (Self-Sovereign Identity) and DID (Decentralized Identifiers), which allow individuals to manage their own IDs, central management entities like ID platforms can be eliminated. (For details, see What do we want to solve with SSI and DID? (Beta) and Whose identity is it? SSI with Hyperledger Indy & Aries)

To get a sense of this decentralization trend, I would like to introduce and explain the implementation of Ceramic and IDX, which enable the development of applications where authentication is performed via DID and data can be written to and read from a decentralized network.

https://raw.githubusercontent.com/winor30/ceramic-idx-application-example/main/resources/overview-plattformless.jpg

Ceramic・IDX

Ceramic is a project developed by 3BoxLab that aims to decentralize the data layer currently managed by major platforms. They provide a decentralized and tamper-proof data infrastructure by utilizing DID, decentralized storage like IPFS, and their own proprietary decentralized network (currently in alpha). Roughly speaking, it is a document-oriented database where permissions for updating or reading data can be restricted to the DID owner.

IDX is a protocol based on DID for linking various data to realize data sharing across different applications. IDX is primarily developed by 3BoxLab, the same team behind Ceramic. While Ceramic is mainly used as the data storage for IDX, it is designed to be modular so that other decentralized data storage solutions (such as Ethereum contracts, IPFS, or Filecoin) can also be used.

These technologies enable an open internet where you can manage your own data and provide that data across various different applications (interoperability).

Implementation

Target for this implementation

In this instance, I implemented a web application that links a user's name and bio to a DID managed by an Ethereum wallet called MetaMask, using Ceramic and IDX. Although there is only one application, the name and bio are linked to this DID, meaning they can be used in other applications as well.

The web app is built with Next.js x Tailwind x TypeScript and hosted on Vercel (I considered decentralized hosting options as well, but chose the straightforward Vercel to avoid overcomplicating the scope).

https://raw.githubusercontent.com/winor30/ceramic-idx-application-example/main/resources/example-app-overview.jpg

Implementation Details

The details of the code can be found in the repository below.

https://github.com/winor30/ceramic-idx-application-example

Below, I will explain the main parts.

Ceramic and IDX Libraries

As you can see in src/libs/idx/index.ts, you can manage data associated with DIDs using libraries for Ceramic and IDX instances.

By passing an instance for HTTP communication with Ceramic (@ceramicnetwork/http-client) to the IDX instance, Ceramic can be used as the IDX backend.

Additionally, CERAMIC_CLAY_URL currently specifies the testnet URL.

import Ceramic from '@ceramicnetwork/http-client';
import { IDX } from '@ceramicstudio/idx';
import { CERAMIC_CLAY_URL } from './constant';

let ceramic: Ceramic;
const getCeramic = () => {
  if (ceramic) return ceramic;
  ceramic = new Ceramic(CERAMIC_CLAY_URL);
  return ceramic;
};

let idx: IDX;
const getIdx = () => {
  if (idx) return idx;
  const ceramic = getCeramic();
  idx = new IDX({ ceramic });
  return idx;
};

export { getIdx, getCeramic };

Authentication on Ceramic using DID

To prove ownership of a DID, you need to pass that information to the Ceramic instance. This can be achieved with the following code:

The general flow is as follows:

  1. Make the Ethereum wallet (MetaMask, etc.) accessible from the browser.
  2. Obtain the wallet's Provider instance and wrap it to create an authProvider.
  3. Generate a didProvider that can prove DID ownership from the authProvider using 3id-connect.
  4. Set the didProvider to the ceramic instance (this allows the Ceramic instance to use DID features).
export const getBasicProfile = async (): Promise<Error | LoginData | null> => {
	const addresses: string[] | Error = await window.ethereum.enable().catch(returnErr);
  if (addresses instanceof Error) {
    return addresses;
  }

  const address = addresses[0];
  if (!address) {
    return new Error('address is undefined');
  }

  const authProvider = new EthereumAuthProvider(window.ethereum, address);
  const threeIdConnect = getThreeIdConnect();
  const connected = await threeIdConnect.connect(authProvider).catch(returnErr);
  if (connected instanceof Error) {
    return connected;
  }

  const idx = getIdx();
  const didProvider = threeIdConnect.getDidProvider();
  const result = await idx.ceramic.setDIDProvider(didProvider).catch(returnErr);
  if (result instanceof Error) {
    return result;
  }
...
};

The process of making the Ethereum wallet accessible from the browser is handled by window.ethereum.enable() in standard browsers like MetaMask or Trust, which essentially grants permission to use the wallet in the browser. (In terms of browser behavior, the wallet will pop up and ask the user for permission.)

The part wrapped in EthereumAuthProvider is to adapt to the specifications of the 3id-connect library so it can be used with Ceramic. 3id-connect is a library that allows a type of DID called 3ID DID (where the DID method is did:3:***) to be handled by blockchain wallets (currently Ethereum and EOS). An important concept here is that a DID can generally be managed in any way as long as it can perform signatures standardized by the W3C and those signatures can be verified. The idea behind 3id-connect is to use blockchain wallets for this management, though it doesn't necessarily have to be a blockchain wallet.

threeIdConnect.connect asks the user if this blockchain wallet can correctly manage the DID and whether it's okay for the user to manage the DID. If successful, you can then use a didProvider that can prove ownership of the DID through threeIdConnect.

Finally, by setting this didProvider into the ceramic instance, the ceramic instance can also utilize the DID proof.

Fetching Profile Information

Basically, no DID authentication is required for fetching profile information.

My research here is slightly incomplete, but based on the current IDX specifications, get (fetching data linked to a DID) seems possible without authentication. However, I expect that in the future, there will be more instances of fetching private information or data requiring specific permissions that will necessitate authentication.

The code is very simple: data can be fetched using idx.get(alias, did). Regarding the alias, a schema is determined beforehand, and the alias name is used to specify that schema. You can also add more schemas. Furthermore, there is generally only one piece of data that can be defined per schema for a single DID (e.g., there is only one piece of data for the basicProfile schema per DID).

export const getBasicProfile = async (): Promise<Error | LoginData | null> => {
...
	const did = didProvider.accountId;
  const basicProfile = await idx
    .get<LoginData | undefined>(constants.definitions.basicProfile, did)
    .catch(returnErr);
  if (!basicProfile) {
    return null;
  }
  return basicProfile;
};

Updating Profile Information

Updating profile information requires DID authentication.

Naturally, this is to prevent anyone from being able to change the information associated with you.

This is also done using an alias, and can be updated by setting an object that conforms to the schema for that alias.

export const useSignUp = () => {
...
  const signUp = async (data: { name: string; description: string }) => {
...
    const basicProfile = await getBasicProfile().catch(returnErr);
...
    // idx instance with DIDProvider already set
    const idx = getIdx();
    const docID = await idx
      .set(constants.definitions.basicProfile, {
        name: data.name,
        description: data.description,
      })
      .catch(returnErr);
...
  };
...
};

Operation Results

The demo app deployed on Vercel is here:

https://ceramic-idx-application-example.vercel.app

(Note: The wallet shown below is a test account newly generated for this project.)

https://raw.githubusercontent.com/winor30/ceramic-idx-application-example/main/resources/cermaic-app-screenshots.jpg

Summary and Discussion

In this article, I explained the background of decentralization and de-platforming, introduced Ceramic and IDX—projects aiming for the decentralization of application development currently in progress—and actually built an application using them.

My impression after trying them out was that they are built in a way that is easier to use than I expected, without significantly compromising the current development experience. However, regarding the UX, users will find the wallet and signature parts quite complex, so improvements are hoped for. (This could be somewhat alleviated by using wallets that can be embedded in web apps, such as Portis or Authereum.)

Also, as a characteristic of decentralized networks, performance tends to be worse than centralized alternatives in many cases, which also increases the difficulty of building systems and maintaining UX.
IDX's data storage can be chosen flexibly, and similarly, both decentralized and centralized systems have their own advantages. I felt that as engineers, we must consider these as requirements to be selected or rejected to realize a product and then proceed with system and application development.

References

Discussion