Skip to main content

Data Model

Polycentric is a binary protocol using Protocol Buffers v3. The messages below are the v2 definitions from the protos/polycentric/v2 directory of the Polycentric code repository. RPC request/response messages are covered in gRPC.

Keys and identity

KeyType

The only supported key type is Ed25519.

enum KeyType {
KEY_TYPE_UNSPECIFIED = 0;
KEY_TYPE_ED25519 = 1;
}

PublicKey

There is usually one public key per account, per device.

message PublicKey {
// Type of key used. Defaults to ED25519.
KeyType key_type = 1;
// Value of the key
bytes key = 2;
}

Identity

The identity document. rotation_keys control the identity and can issue new keys; signing_keys may sign events but not change the document; revocation_bounds preserve the verifiability of events from a revoked key.

message Identity {
repeated PublicKey rotation_keys = 1;
repeated PublicKey signing_keys = 2;
repeated RevocationBound revocation_bounds = 3;
}

message RevocationBound {
// Key that is revoked
PublicKey revoked_key = 1;
// One target per collection `revoked_key` wrote in, anchoring proof
// verification at that collection's head event.
repeated EventProofTarget targets = 2;
}

// Per-collection target for verifying an EventProof.
message EventProofTarget {
int32 collection = 1;
// Event's signature; matched by `EventProof.target_signature`.
bytes signature = 2;
// Event's `previous_root` — the Merkle root proofs verify against.
bytes root = 3;
// Leaf count of the tree at `root`.
uint64 leaf_count = 4;
}

EventKey

The unique identifier of an event.

message EventKey {
// Reserved collections:
// 1 -> Identity, 2 -> Feed, 3 -> Profile,
// 4 -> Interactions, 5 -> Social graph
int32 collection = 1;

// Identity key (sha256 hash of the initial Identity content)
string identity = 2;

// Public key that signed the event
PublicKey signed_by = 3;

// Sequence number of the event in the collection (logical clock)
uint64 sequence = 4;
}

Events

VectorClock

The sequence numbers (of the same collection) the signing key is aware of.

message VectorClock {
repeated uint64 sequence = 1;
}

Event

References, but does not contain, its content.

message Event {
// Key for the event
EventKey key = 1;

// Reference to the sequence, of the Identity Collection (1), that holds
// the identity document
uint64 identity_sequence = 2;

VectorClock vector_clock = 3;

// Signature of the previous event signed by the same key.
bytes previous_signature = 4;

// Digest of the content
ContentDigest content_digest = 6;

// Creation time, in milliseconds
uint64 created_at = 7;

// RFC 6962 Merkle root over this signer's prior signatures in this
// collection (leaf_count = key.sequence - 1).
bytes previous_root = 8;
}

SignedEvent

The signature is computed over event_bytes, not over a re-encoded Event. The bytes are stored as-is so the signature stays verifiable.

message SignedEvent {
// Signature of event_bytes below
bytes signature = 1;
// Serialized representation of the Event message that is signed against
bytes event_bytes = 2;
}

EventBundle

Bundles a SignedEvent with its serialized content and any inclusion proofs. The content is carried as SerializedContent so its checksum can be verified against the event's ContentDigest.

message EventBundle {
SignedEvent signed_event = 1;
optional SerializedContent serialized_content = 2;
repeated EventProof event_proofs = 3;
}

EventProof

An RFC 6962 Merkle inclusion proof: the bundle's SignedEvent is the leaf at leaf_index in the tree rooted at the target identified by target_signature.

message EventProof {
bytes target_signature = 1;
uint64 leaf_index = 2;
// RFC 6962 audit path: sibling hashes from leaf toward root.
repeated bytes audit_path = 3;
}

EventHint

Extra events a server may return to save the client a round trip — for example, the root and parent posts of a reply, plus the author's latest profile event.

message EventHint {
EventBundle event_bundle = 1;
}

Content

ContentDigest

enum ContentDigestType {
CONTENT_DIGEST_TYPE_UNSPECIFIED = 0;
CONTENT_DIGEST_TYPE_SHA256 = 1;
}

message ContentDigest {
ContentDigestType type = 1;
// Hash of the serialized content bytes
bytes value = 2;
}

Content

The body of an event. Exactly one variant is set.

message Content {
oneof content_body {
Post post = 2;
Delete delete = 3;
Follow follow = 4;
Block block = 5;
Reaction reaction = 6;
ProfileUpdate profile_update = 7;
Identity identity = 8;
Repost repost = 9;
}
}

// What the ContentDigest is computed over.
message SerializedContent {
bytes content_bytes = 1;
}

Post, PostReply, Repost

message Post {
string text = 1;
optional PostReply reply = 2;
repeated ImageSet images = 3;
optional EventKey quote = 4;
}

message PostReply {
// Initial post in the reply chain
EventKey root = 1;
// Post being replied to
EventKey parent = 2;
}

message Repost {
optional EventKey post = 1;
}

Delete

A Delete tells a server to forget the contents of a previous event. The server keeps the data only if it is still referenced by another event.

message Delete {
EventKey event_key = 1;
}

Follow, Block

message Follow {
string identity = 1;
}

message Block {
string identity = 1;
}

Reaction

message Reaction {
// Key of the event being reacted to
EventKey event_key = 1;
optional string emoji = 2;
// Upvote = true, downvote = false
bool positive = 3;
}

ProfileUpdate

message ProfileUpdate {
optional string name = 1;
optional ImageSet avatar = 2;
optional ImageSet banner = 3;
optional string description = 4;
}

Blob, Image, ImageSet

Media is stored as blobs in object storage and referenced by digest. Images may carry multiple size variants.

message Blob {
ContentDigest digest = 1;
string mime_type = 2;
int64 size = 3;
}

message Image {
Blob blob = 1;
int32 width = 2;
int32 height = 3;
}

message ImageSet {
repeated Image images = 1;
}