diff --git a/docs/composedb/examples/index.mdx b/docs/composedb/examples/index.mdx
new file mode 100644
index 00000000..8d6ffea0
--- /dev/null
+++ b/docs/composedb/examples/index.mdx
@@ -0,0 +1,21 @@
+# Starter Applications and Tutorials
+Looking for code samples, starter applications, and tutorials to kickstart your development process? Check out some of the examples below for inspiration.
+
+## Starter Applications
+- [**Official ComposeDB Example App**](https://github.com/ceramicstudio/ComposeDbExampleApp) - A starter application built around a social media platform use-case (this application utilizes the same data models used by the [ComposeDB Sandbox](../../sandbox)). A great first step if you haven't done anything with ComposeDB yet.
+- [**Lit Protocol with ComposeDB**](https://github.com/ceramicstudio/lit-composedb) - Encrypt and decrypt data based on on-chain condition logic using Lit Protocol while storing on ComposeDB
+- [**Ethereum Attestation Service on ComposeDB**](https://github.com/ceramicstudio/ceramic-eas) - Save attestations generated using the Ethereum Attestation Service to the Ceramic Network using ComposeDB.
+- [**OpenAI Realtime Chat with ComposeDB**](https://github.com/ceramicstudio/ceramic-ai) - Interact with an OpenAI API endpoint in the form of a realtime chat application with storage on ComposeDB.
+
+## Tutorials
+- [**Getting Started with ComposeDB (video)**](https://www.youtube.com/watch?v=r68FXBTCBZ4) - Follow an instructional video on getting started and set up on ComposeDB.
+- [**Lit Protocol with ComposeDB**](https://developer.litprotocol.com/v3/integrations/ceramicExample) - A tutorial that walks the reader through how the `Lit Protocol with ComposeDB` repository (linked above) works under-the-hood.
+- [**Decentralized Databases: ComposeDB**](https://dev.to/fllstck/decentralized-databases-composedb-49m3) - A tutorial that walks the reader through how to set up a decentralized blog on ComposeDB.
+- [**Build an AI Chatbot on ComposeDB**](https://learnweb3.io/lessons/build-an-ai-chatbot-on-compose-db-and-the-ceramic-network) - Read a LearnWeb3.io tutorial on how the `OpenAI Realtime Chat with ComposeDB` repository (linked above) works and how to set it up.
+- [**Query Filtering and Ordering in ComposeDB**](https://blog.ceramic.network/tutorial-query-filtering-and-ordering-in-composedb/) - Learn the ins and outs of filtering and ordering based on schema subfields in this tutorial blog post.
+- [**Creating `MetIRL` Attestations with EAS**](https://docs.attest.sh/docs/tutorials/ceramic-storage) - Learn how the `Ethereum Attestation Service on ComposeDB` repository (linked above) works, how to generate attestations, and how to create confirmations.
+- [**Encrypted Data on ComposeDB**](https://blog.ceramic.network/tutorial-encrypted-data-on-composedb/) - Learn yet another way to encrypt and decrypt data on ComposeDB by generating an `encryptionDid` instance.
+
+### **Want your open-source examples featured here?**
+Get in touch with us on the [Ceramic Discord](https://chat.ceramic.network) - we'd love to see what you're building!
+
diff --git a/docs/composedb/examples/verifiable-credentials.mdx b/docs/composedb/examples/verifiable-credentials.mdx
new file mode 100644
index 00000000..20b374c5
--- /dev/null
+++ b/docs/composedb/examples/verifiable-credentials.mdx
@@ -0,0 +1,646 @@
+# Verifiable Credentials
+Verifiable Credentials are a W3C standard used by development teams to fulfill a need for tamper-evident claims that can be cryptographically proven to reliably reveal who issued the claim, who (if anyone) is the recipient, the content of the claim itself, and more.
+
+While there are multiple libraries and data model implementations available to developers who want to generate and validate Verifiable Credentials for their applications, this guide outlines one example to help illustrate these general concepts.
+
+## But First - What are Verifiable Credentials?
+[Verifiable Credentials](https://www.w3.org/TR/vc-data-model/) offer a digital credentialing format that follows specific [World Wide Web Consortium](https://www.w3.org/) open standards. This format relies on several key characteristics that help ensure its reliability as a tamper-proof credential such as including a digital signature that cannot be inauthentically synthesized, the use of Decentralized Identifiers (or DIDs) to represent unique individual identities, and a predictable core data model that allows credential instances to be reconstructed as presentations.
+
+Generally speaking, these components break down into three primary categories that make up a Verifiable Credential:
+
+- **Metadata**: This data is often cryptographically signed by the issuer and contains information about the credential itself (such as who the issuer is, when it expires, and so on) in addition to the credential identifier
+- **Claim**: A set of claims about the credential subject (these must be tamper-proof)
+- **Proof**: A set of properties that allows people to cryptographically verify the source of the data and whether the data has been tampered with
+
+A common use case to point to would be an online education platform that issues credentials when students have completed courses. A Verifiable Credential could be used to reliably show that an issuer (the education platform) attests that a given student (the eventual credential holder) has completed a specific course (included in the metadata). If a graduate student program needed to verify a given credential to accept students, those programs (playing the role of a verifier) would request proofs from the holder and be able to know whether those proofs were valid, invalid, or tampered with.
+
+
+### Where do Verifiable Credentials and Decentralized Storage Converge?
+Given the data model Verifiable Credentials use, individual credential instances can be stored anywhere - from offline storage on a hard drive, to traditional databases controlled by companies who rely on Verifiable Credentials, to smart contracts on a blockchain, to more performant peer-to-peer storage, and everything in between. Why, then, would developers choose to store verifiable credentials on Ceramic?
+
+**Data Interoperability**
+
+Since Verifiable Credentials are flexible enough to describe a seemingly limitless set of circumstances, yet standardized enough to be able to easily verify the proofs included therein, developers who build on Ceramic not only benefit from the performance and querying capabilities offered by ComposeDB but can also consume verifiable credentials from other issuers and communities built on Ceramic.
+
+**Self-Sovereign Identity (SSI)**
+
+Verifiable Credentials contribute to self-sovereign identity in a major way by allowing users to easily prove their identity and share credentials without delegating factors of their identity to some central authority. Developers who allow their users to store their credentials on Ceramic enable their users to retain control and ownership of their data. Since streams in the Ceramic Network can only be modified with the permission of the controller, each credential saved to Ceramic can only be edited or changed in the future by the users themselves.
+
+**Ease-Of-Querying**
+
+While storing raw JSON files to other decentralized storage options like IPFS is always an option, developers who require any layer of storage at scale with the ability to filter, sort, and query data based on more precise qualities will need a storage option that provides functional similarities to a traditional database. This is where ComposeDB comes into play, offering a familiar database experience with native support for GraphQL and automatic performance gains by splitting read/write operations.
+
+While the list goes on, let's hop into the guide.
+
+## Getting Started - Key Components
+
+To support the functionality we need for our application that uses Verifiable Credentials, we will rely on the following tools:
+
+**Verifiable Data Registry**
+
+One of the key components outlined in the [Verifiable Credential Ecosystem](https://www.w3.org/TR/vc-data-model-2.0/#dfn-verifiable-data-registries) is a Verifiable Data Registry. This component is responsible for maintaining things like Verifiable Credential schemas. To fulfill this function, we will be using [Serto](https://schemas.serto.id/), a shared repository of schemas.
+
+**Verifiable Credential Library**
+
+Our application will also require an open-source library that makes it easy to generate and verify credentials across the various formats needed by our application. For our library, we will use [Veramo](https://veramo.io/).
+
+**Decentralized Storage**
+
+Finally, we will be using ComposeDB to both store credentials for our user, as well as retrieve credentials to verify.
+
+### Defining Our Schema
+
+If you [sign up](https://schemas.serto.id/) for a free account on Serto you'll have the ability to define credential schemas for your uses and applications. However, for our application, we will be using an existing schema definition called `Vetted Reviewer` (see the definition [here](https://schemas.serto.id/schema/vetted-reviewer)).
+
+
+
+![serto schema](/img/schema_serto.png)
+
+
+
+The basic idea here is that an entity (such as a workplace or group of collaborators) might want to issue `Vetted Reviewer` instances to code reviewers they trust. As you'll notice, the data model is quite simple - we have an identifier for the credential, issuer, and recipient, as well as a date and a simple boolean field indicating whether the subject is or is not trusted.
+
+Serto also supplies a peek view into what the schema would look like in JSON format (though we will later create full instances that we can also inspect):
+
+
+
+![serto schema](/img/serto-example.png)
+
+
+
+## Setting Up Our Veramo Agent
+
+Now that we have our schema defined that we'll use for our credentials, we can start setting up the section of our application that uses Veramo.
+
+As outlined in the [Veramo Docs](https://veramo.io/docs/veramo_agent/introduction), our Veramo Agent will act as our application's interface for issuing and verifying credentials, as well as managing a DID that represents the application itself.
+
+We've already done most of this work for you, which we're about to walk through below.
+
+To get started, open a new terminal and clone the following repository:
+
+```bash
+git clone https://github.com/ceramicstudio/verifiable-credentials && cd verifiable-credentials
+```
+
+Once you have the repository opened in your text editor of choice, you'll see two sub-directories - one named `client` and the other named `express-veramo`. Let's first explore our `express-veramo` directory.
+
+This section of our application is meant to serve our Veramo Agent on an Express.js server that the `client` section of our application will use to generate and verify credentials.
+
+**Veramo Agent**
+
+If you open `/express-veramo/src/veramo/setup.ts` in your code editor, you can observe how we're instantiating our agent with all of the relevant plugin settings we'll need. For example, we've set our `defaultProvider` (on line 98 within our `DIDManager` instantiation) to "did:key". We've also enabled the ability to resolve "did:key" DIDs by adding a call to `keyDidResolver()` to our `DIDResolverPlugin` instantiation. Finally, we will be using a local SQLite instance (with TypeORM) to manage our Veramo Agent's DIDs and private keys.
+
+Our agent (with all our custom configurations) will then be available to import and use within each of our methods used to generate DIDs, create credentials, and verify credentials. For example, if you take a look at `/express-veramo/src/create-identifier.ts` you'll see how `main` uses our agent to access the `didManagerCreate` method on the agent's prototype chain.
+
+Finally, our `/express-veramo/index.ts` file exposes these agent methods as endpoints for our Express server.
+
+### Install Your Dependencies
+
+Install your dependencies from within the `/express-veramo` directory:
+
+```bash
+npm install
+```
+
+### Environment Variables
+
+You will notice that our Express application will need two environment variables.
+
+1. `INFURA_PROJECT_ID`: Simply go to [infura.io](https://www.infura.io/) and set up a new Web3 API key. Once set up, you will only need to copy the key itself (it should look something like "b45j76facf05112f9664778z1bf6bd50").
+2. `KMS_SECRET_KEY`: You can generate a KMS Secret Key using the Veramo CLI:
+
+```bash
+npx @veramo/cli config create-secret-key
+```
+
+### Generate Veramo DIDs
+
+We will also need to generate an admin DID that our Veramo Agent will use when generating credentials. This can be thought of as the admin seed representing our application:
+
+```bash
+npm run generate-id
+```
+We are now ready to start up our Express server! To begin, run the following in your terminal:
+
+```bash
+npm start
+```
+
+If all is successful so far, you should see the following in your terminal logs:
+
+`server started at http://localhost:8080`
+
+### Generate EIP-712 Signed Verifiable Credentials
+
+The [EIP712](https://eips.ethereum.org/EIPS/eip-712) standard allows wallets to display data in signing prompts in a readable and highly structured format and also happens to be a standard supported by Veramo's modular plugins. Unlike other formats, EIP712 requires `TypedData` (a JSON object containing type information, as well as domain separator parameters and the message).
+
+If you observe `/express-veramo/src/create-credential-712.ts` you'll notice that it looks almost identical to `/express-veramo/src/create-credential-jws.ts`. However, one major factor that will yield a very different output is the value for the "proofFormat" field (we use `EthereumEip712Signature2021` for our EIP712 instance).
+
+You'll also notice that the body of our credential references the schema we defined in Serto earlier, as well as the actual subject of the credential (indicating the recipient's identifier and whether they are trusted).
+
+In `/express-veramo/index.ts` you'll see that we're exposing this method on our /create endpoint.
+
+If you have Postman, you can open up a window and send a POST request to "http://localhost:8080/create" using a dummy input DID:
+
+
+
+![postman query](/img/postman.png)
+
+
+
+You'll be able to view the output thereafter, which should look similar to this:
+
+```json
+{
+ "issuer": "did:key:zQ3shjSvqxWu82TG8ARw6yZYvRhnAxi3MrDS7MoghVJLrUh1h",
+ "@context": [
+ "https://www.w3.org/2018/credentials/v1",
+ "https://beta.api.schemas.serto.id/v1/public/vetted-reviewer/1.0/ld-context.json"
+ ],
+ "type": [
+ "VerifiableCredential",
+ "VettedReviewer"
+ ],
+ "credentialSchema": {
+ "id": "https://beta.api.schemas.serto.id/v1/public/vetted-reviewer/1.0/json-schema.json",
+ "type": "JsonSchemaValidator2018"
+ },
+ "issuanceDate": "2023-10-24T22:10:31.906Z",
+ "credentialSubject": {
+ "isTrusted": true,
+ "id": "did:pkh:eip155:1:0xc362c16a0dcbea78fb03a8f97f56deea905617bb"
+ },
+ "proof": {
+ "verificationMethod": "did:key:zQ3shjSvqxWu82TG8ARw6yZYvRhnAxi3MrDS7MoghVJLrUh1h#zQ3shjSvqxWu82TG8ARw6yZYvRhnAxi3MrDS7MoghVJLrUh1h",
+ "created": "2023-10-24T22:10:31.906Z",
+ "proofPurpose": "assertionMethod",
+ "type": "EthereumEip712Signature2021",
+ "proofValue": "0xa090c41ba3a768ddf2695000ddc98009bf7dcddf9778e9d54cefcd3adbd7faaf08d4f2e9b31038112221a2d8dddbc1b0024488ea3b926400b767d1fc1ea4309b1b",
+ "eip712": {
+ "domain": {
+ "chainId": 1,
+ "name": "VerifiableCredential",
+ "version": "1"
+ },
+ "types": {
+ "EIP712Domain": [
+ {
+ "name": "name",
+ "type": "string"
+ },
+ {
+ "name": "version",
+ "type": "string"
+ },
+ {
+ "name": "chainId",
+ "type": "uint256"
+ }
+ ],
+ "CredentialSchema": [
+ {
+ "name": "id",
+ "type": "string"
+ },
+ {
+ "name": "type",
+ "type": "string"
+ }
+ ],
+ "CredentialSubject": [
+ {
+ "name": "id",
+ "type": "string"
+ },
+ {
+ "name": "isTrusted",
+ "type": "bool"
+ }
+ ],
+ "Proof": [
+ {
+ "name": "created",
+ "type": "string"
+ },
+ {
+ "name": "proofPurpose",
+ "type": "string"
+ },
+ {
+ "name": "type",
+ "type": "string"
+ },
+ {
+ "name": "verificationMethod",
+ "type": "string"
+ }
+ ],
+ "VerifiableCredential": [
+ {
+ "name": "@context",
+ "type": "string[]"
+ },
+ {
+ "name": "credentialSchema",
+ "type": "CredentialSchema"
+ },
+ {
+ "name": "credentialSubject",
+ "type": "CredentialSubject"
+ },
+ {
+ "name": "issuanceDate",
+ "type": "string"
+ },
+ {
+ "name": "issuer",
+ "type": "string"
+ },
+ {
+ "name": "proof",
+ "type": "Proof"
+ },
+ {
+ "name": "type",
+ "type": "string[]"
+ }
+ ]
+ },
+ "primaryType": "VerifiableCredential"
+ }
+ }
+}
+```
+
+Notice how the `proof` key in our JSON output includes both a `proofValue` as well as all of the `TypedData` details required for the EIP712 format.
+
+If you send the same post request to the /create-jws endpoint, you will notice how the JSON web token output differs significantly:
+
+```json
+{
+ "credentialSubject": {
+ "isTrusted": true,
+ "id": "did:pkh:eip155:1:0xc362c16a0dcbea78fb03a8f97f56deea905617bb"
+ },
+ "issuer": {
+ "id": "did:key:z6MkrmuQWoiVynAchQiuVwrv8nt6dU3equSFJ3ZnuSnjhnkp"
+ },
+ "type": [
+ "VerifiableCredential",
+ "VettedReviewer"
+ ],
+ "credentialSchema": {
+ "id": "https://beta.api.schemas.serto.id/v1/public/vetted-reviewer/1.0/json-schema.json",
+ "type": "JsonSchemaValidator2018"
+ },
+ "@context": [
+ "https://www.w3.org/2018/credentials/v1",
+ "https://beta.api.schemas.serto.id/v1/public/vetted-reviewer/1.0/ld-context.json"
+ ],
+ "issuanceDate": "2023-10-24T22:14:09.000Z",
+ "proof": {
+ "type": "JwtProof2020",
+ "jwt": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vYmV0YS5hcGkuc2NoZW1hcy5zZXJ0by5pZC92MS9wdWJsaWMvdmV0dGVkLXJldmlld2VyLzEuMC9sZC1jb250ZXh0Lmpzb24iXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlZldHRlZFJldmlld2VyIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlzVHJ1c3RlZCI6dHJ1ZX0sImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL2JldGEuYXBpLnNjaGVtYXMuc2VydG8uaWQvdjEvcHVibGljL3ZldHRlZC1yZXZpZXdlci8xLjAvanNvbi1zY2hlbWEuanNvbiIsInR5cGUiOiJKc29uU2NoZW1hVmFsaWRhdG9yMjAxOCJ9fSwic3ViIjoiZGlkOnBraDplaXAxNTU6MToweGMzNjJjMTZhMGRjYmVhNzhmYjAzYThmOTdmNTZkZWVhOTA1NjE3YmIiLCJuYmYiOjE2OTgxODU2NDksImlzcyI6ImRpZDprZXk6ejZNa3JtdVFXb2lWeW5BY2hRaXVWd3J2OG50NmRVM2VxdVNGSjNabnVTbmpobmtwIn0.rAhjw1_bkvY9QNSTJsoWHnsYU4ccYHngJ36x6gv567DEp85QGpz3zcKbrJAIBEdvR76C5-FcF6tSKk6TnhiADQ"
+ }
+}
+```
+
+While both versions can be reliably stored and later verified, the JWT implementation compacts all of the necessary credential data and signatures into a single field.
+
+For this guide, we will show you how to store both JWT and EIP712 Verifiable Credentials on ComposeDB, and later how to reconstruct and verify them using our Veramo agent.
+
+## ComposeDB Server and Client Setup
+
+You can leave your Express server running as we begin exploring the `client` section of the application, starting with the data models we'll need for storing our Verifiable Credentials.
+
+If you open your `/client/composites` directory in your text editor, you'll find a GraphQL file that encompasses the interfaces and types we'll need for our VC data class:
+
+```graphql
+# 00-verifiableCredential.graphql
+
+## our overarching VC interface that acts agnostic of our proof type
+interface VerifiableCredential
+ @createModel(description: "A verifiable credential interface")
+{
+ controller: DID! @documentAccount
+ issuer: Issuer!
+ context: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ type: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ credentialSchema: CredentialSchema!
+ issuanceDate: DateTime!
+}
+
+type Issuer {
+ id: String! @string(maxLength: 1000)
+ name: String @string(maxLength: 1000)
+}
+
+type CredentialSchema {
+ id: String! @string(maxLength: 1000)
+ type: String! @string(maxLength: 1000)
+}
+
+## we'll use interfaces for our proof types to generalize them as well - this one's for EIP712
+interface VCEIP712Proof implements VerifiableCredential
+ @createModel(description: "A verifiable credential interface of type EIP712")
+{
+ controller: DID! @documentAccount
+ issuer: Issuer!
+ context: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ type: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ credentialSchema: CredentialSchema!
+ issuanceDate: DateTime!
+ proof: ProofEIP712!
+}
+
+## generalized JWT proof type
+interface VCJWTProof implements VerifiableCredential
+ @createModel(description: "A verifiable credential interface of type JWT")
+{
+ controller: DID! @documentAccount
+ issuer: Issuer!
+ context: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ type: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ credentialSchema: CredentialSchema!
+ issuanceDate: DateTime!
+ proof: ProofJWT!
+}
+
+type ProofEIP712 {
+ verificationMethod: String! @string(maxLength: 1000)
+ created: DateTime!
+ proofPurpose: String! @string(maxLength: 1000)
+ type: String! @string(maxLength: 1000)
+ proofValue: String! @string(maxLength: 1000)
+ eip712: EIP712!
+}
+
+type ProofJWT {
+ type: String! @string(maxLength: 1000)
+ jwt: String! @string(maxLength: 100000)
+}
+
+type EIP712 {
+ domain: Domain!
+ types: ProofTypes!
+ primaryType: String! @string(maxLength: 100)
+}
+
+type Types {
+ name: String! @string(maxLength: 100)
+ type: String! @string(maxLength: 100)
+}
+
+type ProofTypes {
+ EIP712Domain: [Types!]! @list(maxLength: 100)
+ CredentialSchema: [Types!]! @list(maxLength: 100)
+ CredentialSubject: [Types!]! @list(maxLength: 100)
+ Proof: [Types!]! @list(maxLength: 100)
+ VerifiableCredential: [Types!]! @list(maxLength: 100)
+}
+
+type Domain {
+ chainId: Int!
+ name: String! @string(maxLength: 100)
+ version: String! @string(maxLength: 100)
+}
+
+type CredentialSubject
+{
+ id: DID! @accountReference
+ isTrusted: Boolean!
+}
+
+## define our EIP712 type that uses a hard-coded credentialSubject specific to our use case
+type VerifiableCredentialEIP712 implements VerifiableCredential & VCEIP712Proof
+ @createModel(accountRelation: LIST, description: "A verifiable credential of type EIP712")
+ @createIndex(fields: [{ path: "issuanceDate" }])
+ @createIndex(fields: [{ path: "issuer" }]) {
+ controller: DID! @documentAccount
+ issuer: Issuer!
+ context: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ type: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ credentialSchema: CredentialSchema!
+ issuanceDate: DateTime!
+ proof: ProofEIP712!
+ credentialSubject: CredentialSubject!
+}
+
+## define our JWT type that uses a hard-coded credentialSubject specific to our use case
+type VerifiableCredentialJWT implements VerifiableCredential & VCJWTProof
+ @createModel(accountRelation: LIST, description: "A verifiable credential of type JWT")
+ @createIndex(fields: [{ path: "issuanceDate" }])
+ @createIndex(fields: [{ path: "issuer" }]) {
+ controller: DID! @documentAccount
+ issuer: Issuer!
+ context: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ type: [String!]! @string(maxLength: 1000) @list(maxLength: 100)
+ credentialSchema: CredentialSchema!
+ issuanceDate: DateTime!
+ proof: ProofJWT!
+ credentialSubject: CredentialSubject!
+}
+```
+
+What we've done here is generalized both our `VerifiableCredential` schema by using an interface, as well as their corresponding proof types. This allows us to query as general as we want (with our entrypoint on the proof-agnostic `VerifiableCredential` interface), as well as looking specifically for the `VerifiableCredentialEIP712` and `VerifiableCredentialJWT` types that contain `credentialSubject` fields specifically tuned for our use-case.
+
+### Model Instance Controllers
+
+This example application was also intentionally set up to display two different ways a development team might choose to implement model instance ownership. More specifically, it may make sense (under certain situations) for the application itself to have the exclusive ability to change model instance documents after they are created. Conversely, as mentioned toward the beginning of this guide, developers may instead allow their users to retain control of each credential instance (since they know that it would be easy to tell whether a credential has been tampered with anyway).
+
+The dummy UI entails two pages found at `/client/src/pages/index.tsx` and `/client/src/pages/jwt.tsx` which respectively use the components found at `/client/src/components/VC712.tsx` and `/client/src/components/VCJwt.tsx`. You'll notice how the VC712 component calls an API within the `createCredential` method found at `/api/create` (after obtaining an EIP712 Verifiable Credential from our Express server).
+
+If you take a look into `/client/src/pages/api/create.ts`, you'll find the corresponding route definition. Notice how we call an `authenticateDID` method before running a mutation query to authenticate a static seed environment variable (which represents our application's DID).
+
+Conversely, you'll notice how the `createCredential` method within our `/client/src/components/VCJwt.tsx` component executes a mutation query on our `compose` instance. If you dig a bit deeper, you'll notice that we (as the individual user) are already authenticated on the `ComposeClient` instance (imported from `/client/src/fragments/index.tsx`).
+
+### Getting Started
+
+To get started in the client section, we'll first have to install our dependencies (be sure to cd into your `client` directory):
+
+```bash
+npm install
+```
+
+Next, we will need to generate an admin seed and ComposeDB configuration our application will use. This example repository contains a script found at `/client/scripts/commands/mjs` that generates one for you (preset to run "inmemory" which is ideal for testing).
+
+To generate your necessary credentials, run the following in your terminal:
+
+```bash
+npm run generate
+```
+
+Finally, you will need to create a .env file with a SECRET_KEY - this is what our `/client/src/pages/api/create` route will use to authenticate us as the developer on our ComposeClient instance (this must be 32 bytes and must be different from our admin_seed.txt).
+
+Feel free to copy-paste this dummy seed into your .env file:
+
+```bash
+SECRET_KEY="11b574d316903ced6cc3f4787bbcc3047d9c72d1da4d83e36fe714ef7891jb50"
+```
+
+Finally, go ahead and start your application in development mode (switch to node v16 first):
+
+```bash
+nvm use 16
+npm run dev
+```
+
+### Interacting with the UI
+
+If you have been following along up until this point, you should be able to access the UI in your browser on port 3000. Go ahead and connect your wallet using the Web3Modal:
+
+
+
+![verifiable credential playground](/img/vc-playground.png)
+
+
+
+While our UI in this context does not illustrate a setup that would be used in production, readers following this guide should imagine an application's flow whereby users log in, exhibit behavior by performing tasks, and receive verifiable credentials signed by the application.
+
+In our case, we're mimicking this behavior with the simple push of a button, which you'll see after logging in:
+
+
+
+![generate 712 credentials](/img/generate-712.png)
+
+
+
+Jumping back to your text editor, you'll see how the `Generate Verifiable Credential` button click is tied to the `createCredential` method found in `/client/src/components/VC712.tsx`. This method then sends a fetch request to our Express server running on port 8080 with the `/create` route, thus invoking a response from the corresponding `createCredential` method at `/express-veramo/src/create-credential-712.ts`. You'll also notice how we're sending over our user's "did:pkh" DID (which we saved in our local storage for easy access) to be used for the `id` field in our `credentialSubject` key within our credential.
+
+Finally, back in our React component, you'll see how we use the result of this fetch request to hit our endpoint at `/api/create`, which generates a model instance document using our application as the controller DID (which we discussed above).
+
+Go ahead and click `Generate Verifiable Credential` to see this in action.
+
+
+
+![eip712 generate](/img/eip-712-generate.png)
+
+
+
+If you generated a few dummy credentials using Postman in the previous section, you'll notice that the output looks the same. You should also now see your own "did:pkh" appear within the `credentialSubject` key.
+
+### Creating JWT Credentials
+
+If you navigate to `localhost:3000/jwt` in your browser, you will be able to generate credentials using JWTs (and with yourself, the user, as the model instance controller in ComposeDB):
+
+
+
+![jwt generate](/img/jwt-generate.png)
+
+
+
+### Verifying Credentials
+
+One item we haven't yet discussed is the process of verifying credentials. Given how this process entails querying ComposeDB and reconstructing a credential instance for verification, we should also discuss what changes we had to make between the original credential output from Veramo and the saved result in ComposeDB.
+
+You'll notice that both of our ComposeDB schema definitions save the entirety of the Veramo credential output except for changing the name of the "@context" key to "context" (given field naming constraints in GraphQL). We therefore replace the name of this key with "context" when running our mutation query, but must also reconstruct it when querying ComposeDB and reconstructing our credential.
+
+Given that you're already on the `localhost:3000/jwt` page, we'll jump into its corresponding component at `/client/src/components/VCJwt.tsx`. You'll notice how our `verifyCredential` method (tied to the `Verify JWT Credential` button) adds an `@context` key-value pair to our `final` object (and deleting the `context` pair) before hitting our Express server at `/verify-jws`.
+
+If you take a look at `/express-veramo/src/verify-credential-jws.ts` you'll see how we deconstruct our payload during the verification process (you can also see how this is done for our EIP712 verification route at `/express-veramo/src/verify-credential-712.ts`).
+
+Go ahead and click "Verify JWT Credential" if you still have `localhost:3000/jwt` open in your browser:
+
+
+
+![jwt verify](/img/verify-jwt.png)
+
+
+
+Back in our `/client/src/components/VCJwt` component you'll notice that we first make a fetch request to `/api/query-jws` route (found at `/client/src/pages/api/query-jws`) to grab the most recent `VerifiableCredentialJWS` instance from our ComposeClient. Note that in a production setting when dealing with multiple users and many instance documents, your query might instead filter based on the user's DID.
+
+Finally, our `VCJwt` component then hits our `verify-jws` Express endpoint we just discussed to retrieve a response from our Veramo agent.
+
+### Querying interfaces
+
+Back in your browser window you can access an integrated instance of GraphiQL by visiting `http://localhost:3000/query`:
+
+
+
+![query interfaces](/img/query-interfaces.png)
+
+
+
+If you look at the query that's been generated for you, you'll see that we're using the general `VerifiableCredential` interface as the entrypoint, while still being able to grab corresponding proofs that are of EIP712 and JWT types:
+
+```graphql
+query VerifiableCredentialsAll {
+ verifiableCredentialIndex(first: 10) {
+ edges {
+ node {
+ controller {
+ id
+ }
+ issuer {
+ id
+ }
+ context
+ type
+ credentialSchema {
+ id
+ type
+ }
+ issuanceDate
+ ... on VCEIP712Proof {
+ proof {
+ verificationMethod
+ created
+ proofPurpose
+ type
+ proofValue
+ eip712 {
+ domain {
+ chainId
+ name
+ version
+ }
+ types {
+ EIP712Domain {
+ name
+ type
+ }
+ CredentialSchema {
+ name
+ type
+ }
+ CredentialSubject {
+ name
+ type
+ }
+ Proof {
+ name
+ type
+ }
+ VerifiableCredential {
+ name
+ type
+ }
+ }
+ primaryType
+ }
+ }
+ }
+ ... on VCJWTProof {
+ proof {
+ type
+ jwt
+ }
+ }
+ }
+ }
+ }
+}
+```
+
+Feel free to experiment further with GraphiQL and test out other interface entrypoints, or by specific known StreamIDs or DIDs.
+
+### Next Steps
+
+We hope you've enjoyed this example implementation of using ComposeDB to save and retrieve Verifiable Credentials. However, you may be wondering what else is possible in the realm of verifiable claims, or you may have a need to allow your users to encrypt their claims when saving to Ceramic.
+
+Here are a few resources you might find useful as you continue to discover what's possible with ComposeDB:
+
+- [**Creating Attestations with EAS**](https://docs.attest.sh/docs/tutorials/ceramic-storage) - Learn how to use the Ethereum Attestation Service to generate a different class of verifiable claims called "Attestations".
+- [**Lit Protocol with ComposeDB**](https://developer.litprotocol.com/v3/integrations/storage/ceramic-example) - Follow a tutorial that shows how to use Lit Protocol for access control together with ComposeDB.
+- [**Encrypted Data on ComposeDB**](https://blog.ceramic.network/tutorial-encrypted-data-on-composedb/) - Learn yet another way to encrypt and decrypt data on ComposeDB by generating an `encryptionDid` instance.
+
diff --git a/sidebars.ts b/sidebars.ts
index a458e582..5f4b2980 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -217,7 +217,22 @@ const sidebars: SidebarsConfig = {
{ type: 'doc', id: 'composedb/core-concepts', label: 'Core concepts' },
],
},
- { type: 'doc', id: 'composedb/examples', label: 'Tutorials and Examples' },
+ {
+ type: 'category',
+ collapsed: true,
+ label: 'Tutorials and Examples',
+ link: {
+ type: 'doc',
+ id: 'composedb/examples/index',
+ },
+ items: [
+ {
+ type: 'doc',
+ id: 'composedb/examples/verifiable-credentials',
+ label: 'Verifiable Credentials',
+ },
+ ],
+ },
{
type: 'category',
collapsed: true,
diff --git a/static/img/eip-712-generate.png b/static/img/eip-712-generate.png
new file mode 100644
index 00000000..360cf31d
Binary files /dev/null and b/static/img/eip-712-generate.png differ
diff --git a/static/img/generate-712.png b/static/img/generate-712.png
new file mode 100644
index 00000000..64d3342a
Binary files /dev/null and b/static/img/generate-712.png differ
diff --git a/static/img/jwt-generate.png b/static/img/jwt-generate.png
new file mode 100644
index 00000000..5da22803
Binary files /dev/null and b/static/img/jwt-generate.png differ
diff --git a/static/img/postman.png b/static/img/postman.png
new file mode 100644
index 00000000..626316a3
Binary files /dev/null and b/static/img/postman.png differ
diff --git a/static/img/query-interfaces.png b/static/img/query-interfaces.png
new file mode 100644
index 00000000..65e94e43
Binary files /dev/null and b/static/img/query-interfaces.png differ
diff --git a/static/img/schema_serto.png b/static/img/schema_serto.png
new file mode 100644
index 00000000..d766d522
Binary files /dev/null and b/static/img/schema_serto.png differ
diff --git a/static/img/serto-example.png b/static/img/serto-example.png
new file mode 100644
index 00000000..f501b8ae
Binary files /dev/null and b/static/img/serto-example.png differ
diff --git a/static/img/vc-playground.png b/static/img/vc-playground.png
new file mode 100644
index 00000000..611b9bc3
Binary files /dev/null and b/static/img/vc-playground.png differ
diff --git a/static/img/verify-jwt.png b/static/img/verify-jwt.png
new file mode 100644
index 00000000..99929a8e
Binary files /dev/null and b/static/img/verify-jwt.png differ