Image for post
Image for post

Building with Clay: Getting started with the Ceramic Network

An easy-to-follow tutorial for developers

Joel Thorstensson
Sep 10 · 7 min read

Summary

This tutorial will help you get started building with the Ceramic protocol within minutes. It covers everything from installing the software, to creating, reading, and updating your first documents. It uses the Ceramic CLI and JavaScript APIs.

Background

Ceramic is a protocol for creating smart documents, which are dynamic, mutable documents controlled by DIDs. Smart documents have a wide variety of use cases which are beyond the scope of this tutorial. Instead, this article provides you with the basics of how to build with Ceramic, specifically on the new Clay devnet. Clay is the final iteration of the protocol before it launches to mainnet later this year. If you’re building on Clay using this tutorial, we would love to hear about your experience in our Discord.

An overview of the Ceramic protocol can be found here, and the software architecture here.

Using the CLI

Installation and Setup

To get started, install the Ceramic CLI tool from npm.

$ npm i -g @ceramicnetwork/ceramic-cli

Once installed, you can launch the Ceramic daemon. This command spins up a node which runs the Ceramic protocol and exposes an http-api on port 7007 for interacting with the node. In the background an IPFS node is started which is used by Ceramic to route data and messages around the network.

$ ceramic daemon
Swarm listening on /ip4/127.0.0.1/tcp/4002/p2p/QmXoQz228wVdWxCgxZnKUWLFZX34yg4crpKRipXba8A2dB
Swarm listening on /ip4/192.168.188.22/tcp/4002/p2p/QmXoQz228wVdWxCgxZnKUWLFZX34yg4crpKRipXba8A2dB
Swarm listening on /ip4/10.66.178.21/tcp/4002/p2p/QmXoQz228wVdWxCgxZnKUWLFZX34yg4crpKRipXba8A2dB
Swarm listening on /ip4/127.0.0.1/tcp/4003/ws/p2p/QmXoQz228wVdWxCgxZnKUWLFZX34yg4crpKRipXba8A2dB
Ceramic API running on port 7007

Creating your first document

We can now start creating documents! There are different types of documents supported by Ceramic and these are called doctypes. The most common and general-purpose doctype is the tile doctype which will be the only one we concern ourselves with in this tutorial. To create your first document, simply use the ceramic create command as shown below.

$ ceramic create tile --content '{ "title": "My first Document" }'
ceramic://bafyreihv5qck5tl66c6v45w2vwki2aajzf2hmvgypgog2j5uozf53535qu
{
"title": "My first Document"
}

When the command is executed, a new document is created which has the content that we specified. The first thing that gets printed is its unique document id (DocID).

ceramic://bafyreihv5qck5tl66c6v45w2vwki2aajzf2hmvgypgog2j5uozf53535qu

The other output is simply the content of the newly created document.

Reading a document

Now we can print the content of the document again by typing ceramic show <DocID>. However more interestingly we can show the state of the document instead by using the ceramic state command. This command shows us more metadata about the document, such as who the owner is, what the status of the anchor is (below it's pending because it was requested but not yet finalized), and what doctype the document has.

$ ceramic state ceramic://bafyreihv5qck5tl66c6v45w2vwki2aajzf2hmvgypgog2j5uozf53535qu
{
"doctype": "tile",
"content": {
"title": "My first Document"
},
"metadata": {
"owners": [
"did:3:bafyreibyqmzpkuavkte2vxwu6vpfnc4c62pdac5ycx2y56w342ahvpikle"
],
"schema": null
},
"next": {
"content": null
},
"signature": 2,
"anchorStatus": "PENDING",
"log": [
"bafyreihv5qck5tl66c6v45w2vwki2aajzf2hmvgypgog2j5uozf53535qu"
],
"anchorScheduledFor": "2020-09-07T10:00:00.000Z"
}

Updating a document

So we created a document which has a title but now we also want to have a description in there to give anyone observing the document a bit more context. We can update the document using the ceramic change command.

$ ceramic change --content '{ "title": "My first Document", "description": "This is an example of how a doc can be changed" }' ceramic://bafyreihv5qck5tl66c6v45w2vwki2aajzf2hmvgypgog2j5uozf53535qu

Creating a schema

Now what if we are creating many documents and want to ensure that they all have both a title and a description. Is there a way to enforce this? Yes of course! We can do so by creating a separate document which contains a json-schema. The schema document describes a specific json structure, and it can be used to validate the structure of the document containing our content. Let’s start by just simply creating a tile that contains the json-schema that we desire.

$ ceramic create tile --content '{
"$schema": "<http://json-schema.org/draft-07/schema#>",
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
}
},
"type": "object",
"additionalProperties": false,
"required": ["title", "description"]
}'
ceramic://bafyreidvpcrzllacsop5ihlpjzdg2rr37oej6ncs7c4tcsw3q3jgctzimi
{
"type": "object",
"$schema": "<http://json-schema.org/draft-07/schema#>",
"required": [
"title",
"description"
],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}

Creating a document that uses a schema

Now that we created the schema document, let’s try to apply it when we create a new document by using the --schema parameter.

$ ceramic create tile --schema ceramic://bafyreidvpcrzllacsop5ihlpjzdg2rr37oej6ncs7c4tcsw3q3jgctzimi --content '{ "title": "A doc with schema" }'
Validation Error: data should have required property 'description'

Whoops, in the example above we forgot to add the description property to the document. This means that our newly created document would not conform to the specified schema and the creation thus results in an error. Let’s try again by adding a description this time.

$ ceramic create tile --schema ceramic://bafyreidvpcrzllacsop5ihlpjzdg2rr37oej6ncs7c4tcsw3q3jgctzimi --content '{ "title": "A doc with schema", "description": "This document has to follow the given schema" }'
ceramic://bafyreiddvykwgrxvmznnast3ky62kejx4oo6nfltmtl5udeatkhapwvaaq
{
"title": "A doc with schema",
"description": "This document has to follow the given schema"
}

Cool! We managed to create a document which conforms to a schema defined within another document. Any further changes to the document will also be validated against that schema. That’s pretty neat.

Using the HTTP client

Installation and Setup

If you want to use Ceramic from within a browser or Node.js you can use either the @ceramicnetwork/ceramic-core or the @ceramicnetwork/ceramic-http-client package. The first is an implementation of the full protocol, the latter is an http client which connects to a remote Ceramic daemon, which is more lightweight. Both expose the same API, so you can use them interchangeably. In this tutorial we are going to use the HTTP client and connect it to our local daemon. First, just install the package.

$ npm i --save @ceramicnetwork/ceramic-http-client

In the examples below we use node for simplicity, however all of the example code snippets works just as well in a browser environment.

$ node --experimental-repl-await

First we import and create an instance of the Ceramic HTTP client. By default it will connect to localhost:7007. If you want to connect to a remote instance you can simply pass the url to the constructor like so: new CeramicClient('<https://my-domain.xyz:7007>').

> const CeramicClient = require('@ceramicnetwork/ceramic-http-client').default> const ceramic = new CeramicClient()

Reading a document

Now let’s try loading the document that we created in the CLI before.

> const doc1 = await ceramic.loadDocument('ceramic://bafyreiddvykwgrxvmznnast3ky62kejx4oo6nfltmtl5udeatkhapwvaaq')> doc1.content
{
title: 'A doc with schema',
description: 'This document has to follow the given schema'
}

Above we simply print the document content which is available in the doc1.content getter. When instantiated like this, the Ceramic client is a read-only client. In order to actually write new documents and update them we need to authenticate using a DID Provider. In the examples below we are using the alpha version of the package identity-wallet from npm.

Updating a document

First install IdentityWallet.

$ npm i --save identity-wallet@next

Then, simply create an instance of the IdentityWallet package and use it’s DID provider with the ceramic client. We won’t go into details of how IdentityWallet works, but note that we are passing a seed which is used to generate the DID. If we use the same seed again we will always get the same DID.

> const IdentityWallet = require('identity-wallet').default> const idw = await IdentityWallet.create({
getPermission: async () => [],
seed: '0x8e641c0dc77f6916cc7f743dad774cdf9f6f7bcb880b11395149dd878377cd398650bbfd4607962b49953c87da4d7f3ff247ed734b06f96bdd69479377bc612b'
})
> await ceramic.setDIDProvider(idw.getDidProvider())

Alright, so let’s try to change a document!

> await doc1.change({
content: {
title: 'A doc with schema',
description: 'This should not be possible'
}
})
Signature was made with wrong DID. Expected: did:3:bafyreibyqmzpkuavkte2vxwu6vpfnc4c62pdac5ycx2y56w342ahvpikle, got: did:3:bafyreiel4qbm5feio2bt5wp4pun2idwbh6oce22njp7l2utzbxyxpakzw4

What, an error? Yes, we tried to update doc1 using our DID from IdentityWallet, but this document was previously created using a different DID on the CLI. Since doc1 is controlled by a different DID from the one we just created above, we won’t be able to change it from here. Let’s instead create a new document.

> const doc2 = await ceramic.createDocument('tile', { metadata: { schema: 'ceramic://bafyreidvpcrzllacsop5ihlpjzdg2rr37oej6ncs7c4tcsw3q3jgctzimi' }, content: { "title": "Client Document" } })
Uncaught Error: Validation Error: data should have required property 'description'

As you can see, we tried to create a document that uses the schema that we defined using the CLI, however in the above example we failed to provide the description property. Now, let’s actually create doc2.

> const doc2 = await ceramic.createDocument('tile', {
metadata: {
schema: 'ceramic://bafyreidvpcrzllacsop5ihlpjzdg2rr37oej6ncs7c4tcsw3q3jgctzimi'
},
content: {
title: "Client Document",
description: "A Ceramic Document created from the http client."
}
})
> doc2.id
'ceramic://bafyreicznfwk3k3rstchigj4p5yqjanb6kioeoyui3er4bw7mcv7jfjchm'
> doc2.content
{ title: 'Client Document', description: 'A Ceramic Document created from the http client.' }

We have now created a new document owned by the DID from our IdentityWallet instance. The schema of this document is however controlled by the DID from the CLI. This illustrates how we can make complex graphs of interconnected documents where different documents are controlled by different identities.

As in the CLI we can also change a document from the javascript API.

> await doc2.change({
content: {
title: 'Client Document',
description: 'This document is now also updated from the http client'
}
})
> doc2.content
{ title: 'Client Document', description: 'This document is now also updated from the http client' }

Listening for updates

Sometimes we need to listen for updates of a document that may occur on the network. This is especially important for documents which are created by DIDs we don’t control. In the CLI we can use the ceramic watch command. Below we see how to listen for changes using the javascript API.

doc1.on('change', () => console.log('doc1 was updated:', doc1.content))

Since we are listening for changes on doc1, we need to make an update to this document from the CLI.

$ ceramic change --content '{ "title": "A doc with schema", "description": "Testing changed event" }' ceramic://bafyreiddvykwgrxvmznnast3ky62kejx4oo6nfltmtl5udeatkhapwvaaq                                                              14:30
{
"title": "A doc with schema",
"description": "Testing changed event"
}

Once we execute the above command, we will see the following in the Node.js console.

>
doc1 was updated: { title: 'A doc with schema', description: 'Testing changed event' }

That’s it for this tutorial! Hope you found it useful and thanks for reading. If you want to dive deeper have a look at the js-ceramic Github repo, and jump into our Discord.

Ceramic

Ceramic is a permissionless protocol for creating…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store