I would like to shed some light on how we intend to implement peer-to-peer collaboration for shared entities like patches with event logs.
An example of collaboration (and our motivation) is users commenting on patches and updating their status (e.g. closed, reviewed, merged). Our intention is to implement this functionality using event logs.
I’ll give a brief overview in this post and will follow up with more details and updates in replies as things evolve.
Event logs
We take inspiration from Secure Scuttlebutt which uses feeds to implement a peer-to-peer social network and event sourcing.
Every action a peer takes on an entity will be represented by an event that is stored in the Radicle monorepo (more on the storage below). These events are replicated between peers and allows peers to construct a meaningful representation of the concerned entity by iterating over all relevant events.
For instance, if a maintainer wants to mark a patch as reviewed, they will publish an event conveying that intention and referencing the patch. Any peer that replicates that event can show that the patch has been reviewed by the maintainer.
The maintainer may also revoke their review by publishing another event referencing the previous event. Other peers will see this event, realize that it is more recent than the previous event that marked the patch as reviewed and display the patch as not reviewed.
Event ordering
In many cases it is necessary to order events to determine the state of entity. For example, in case of multiple events closing and reopening a patch we want to know which event is the most recent to determine whether a patch is open or closed.
Similarly, we want to put the comments on a patch in the order in which they were created.
We can always put a total order on events by including a timestamp in the event data. This works well for a lot of cases but the problem is that these timestamps are generated by the peers and cannot be verified by other peers. This may allow peers to manipulate the timestamps and ultimately how state on other peers is presented.
To defend against this events will references one or more previous events by hash.
Storage
The simplest way to represent hash-linked events in Git is through commits where each event corresponds to a commit. Whenever a peer publishes a new event they create a new commit that contains the event data and links to the commits for previous events. We’ll discuss how these commits are referenced in the next section.
We could store event data in trees associated with commits or in the commit message. The former may provide more flexibility but we don’t know of a concrete advantage now. The latter is a lot less complex.
For the encoding of events in commits we have the following requirements:
- Self-identifying: The commit should contain a hint that conveys that it is encoding an event.
- Upgradable: If we change the encoding, new code should be able to handle existing event logs with the legacy encoding.
- Compatibility with git tooling. The encoding in commit messages should not break existing git tools. Ideally we can use existing git tooling to debug event logs.
Our proposal for a commit message is the following
radicle upstream event: <event type>
content-type: radicle-upstream-event.v1
content: <JSON encoded event envelope>
The first line will be ignored when parsing. Its there to help with debugging with Git tools.
We’re using Git trailers to encode structured data in the commit message. We’re using the content-type
field to identify the encoding of content
. This allows us to change the encoding in the future (to base64 CBOR, for example).
References
Since events published by peers are stored as commits we need to make them accessible through Git references.
We propose the following scheme for references in the monorepo for events published by the local peer:
refs/namespace/<project-id>/refs/upstream-events/<topic>
For remote peers we use the following scheme:
refs/namespace/<project-id>/refs/remotes/<peer id>/upstream-events/<topic>
This means peers have different event logs scoped by project and a topic. We use scoping by project since replication is currently on a per-project basis so we can replicate events with few adjustments. Scoping by project and topic also allows peers to selectively replicate events they are interested in. For example, to get a peer’s comments on a patch a peer does not need to replicate all the events the peer ever published.
Authenticity
We probably want to sign individual events instead of relying on rad/signed_refs
. I’ll leave this for a follow-up post.