Simplify event creation

The choice of JOSE for representing signatures in Ceramic was made in order to make it easy for sign and create events. However, due to an unfortunate decision made in the design process of dag-jose (an IPLD codec for JOSE) the creation of events requires specialized libraries that can encode data as dag-cbor, as well as producing CIDs. This means that off-the-shelf JWT libraries are not sufficient for creating events, which would be desirable because it would make it trivial for anyone to create custom clients for Ceramic.

Current event structure

DataEvents currently have the following layout,

type Event InitEvent | DataEvent | TimeEvent

type DataEventPayload struct {
  id &InitEvent
  prev &Event
  header optional struct {
    controllers optional [String]
  }
  data Any 
}

type DataEvent struct {
  payload String
  signatures [Signature]
  link: &DataEventPayload
}

In order to create an event a client needs to do the following:

  1. Create the DataEventPayload and encode it using dag-cbor
  2. Compute the CID of the data in (1)
  3. Encode the CID as base64url and sign over it using a JWS algorithm
  4. Create a wrapping object structured as a DagJWSResult, put the resulting jws in this object, encode the data from (1) as base64 and put it in the linkedBlock field

Note that for now I’m skipping the extra complexity of creating a commit that includes a CACAO object-capability.

As we can see above the process of creating an event is quite complicated and not something that any developer can do in a few minutes as a small part of a larger application.

Simplifying data events

What if the process of creating an event was as simple as signing a JWT? Well, not only is this possible but the ground work has already been started. Let’s have a look at how this could work.

First we would need to sign the event payload directly, removing the step of dag-cbor encoding. JWTs always sign JSON data, so we can easily structure our payload as dag-jose manually without any third party library (that is as long as we know the CID of id and prev, which we should have already from interacting with Ceramic).

{
  "id": { "/": "bafy2bzaced..." },
  "prev": { "/": "bagcqcerals..." },
  "data": "The data of my event"
}

This payload can then be signed as a JWT, or plainly a JWS. Ceramic’s apis could be updated to accept this JWT without any additional data.

IPLD representation

One problem remains however. How do we would represent this in IPLD? dag-jose can not be used since it requires the payload to always be a CID, which is not the case here. The resulting JWT will be encoded in the compact serialization, e.g. eyJhVCJ9.eyJzIyfQ.Sflsw5c, so we couldn’t just store this data using something like dag-cbor without loosing the ability to inspect it like any other IPLD object.

Varsig is a new standard being developed at CASA. It provides a way to represent any type of signature as a byte string that includes information about how to parse and verify it. What this means in practice is that we can store the payload from above, appending an additional field _sig that contains the varsig bytes, directly in IPLD using any encoding.

type Event InitEvent | DataEvent | TimeEvent
type Varsig Bytes

type DataEvent struct {
  id &InitEvent
  prev &Event
  header optional struct {
    controllers optional [String]
  }
  data Any
  _sig Varsig
}

For example, in our protocol implementation we might chose to store the above IPLD object using dag-cbor. This would mean that DataEvent’s can be easily observed in the IPLD explorer or through any IPFS gateway. While the explorer actually did get support for dag-jose merged, a single object DataEvent will greatly increase understandability of the Ceramic event log.

Additional event formats

While supporting plain JWTs as events is likely the biggest unlock for Ceramic in terms of what varsig provides, there are additional features that could be enabled. In particular we could support multiple new event formats. The ones that comes to mind are EIP-712, W3C Verifiable Credentials, and IETF Verifiable Credentials. Adding support for one or multiple of these could make Ceramic more accessible to more developers, and potentially make already existing data compatible with Ceramic without any extra effort of the data creators.
It’s however worth noting that the extra complexity of supporting various different event formats might prove to be more effort than the value it provides. Each new format we plan to add should be evaluated carefully.

5 Likes