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.
// Reserved collections:
// 1 -> Identity, 2 -> Feed, 3 -> Profile,
// 4 -> Interactions, 5 -> Social graph,
// 6 -> Reports, 7 -> Labels, 8 -> Verifications
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;
Report report = 10;
Labels labels = 11;
}
}
// 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;
repeated Link links = 5;
}
message PostReply {
// Initial post in the reply chain
EventKey root = 1;
// Post being replied to
EventKey parent = 2;
}
message Repost {
optional EventKey post = 1;
}
#### Link
```protobuf
message Link {
string title = 1;
string description = 2;
string image = 3;
string url = 4;
}
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;
}
Report
A Report flags another event for a category of policy violation. Reports are
themselves signed events, so a server can record who reported what and respond
according to its own moderation policy (moderation is per-server). additional_info
carries optional free-text context.
message Report {
// Event being reported
EventKey event_key = 1;
ReportCategory category = 2;
string additional_info = 3;
}
enum ReportCategory {
REPORT_CATEGORY_UNSPECIFIED = 0;
REPORT_CATEGORY_SPAM = 1;
REPORT_CATEGORY_ABUSE = 2;
REPORT_CATEGORY_CHILD_SAFETY = 3;
REPORT_CATEGORY_TERRORISM = 4;
REPORT_CATEGORY_ILLEGAL = 5;
REPORT_CATEGORY_COPYRIGHT = 6;
REPORT_CATEGORY_SERVER_POLICY = 7;
}
Labels
A Labels event records that a moderation service has classified content
against a set of label values. Labels are signed events in collection 7
(Labels). Like Report, labeling is per-server — a server indexes and
serves only labels from its configured trusted moderation service (set via
POLYCENTRIC_MODERATION_IDENTITY on the server side). Labels from any other
identity are stored and synced as normal events but are not indexed or served
when querying feeds.
The server returns matching Labels events as EventHint entries alongside
feed results; the client correlates each hint's event bundle to its target
by event key and renders it according to the user's preference
(Hide / Warn / Show). Because EventHint is a generic container, the client
differentiates label hints from identity/profile hints by checking whether the
event's collection is 7 (Labels).
message Labels {
// Event being labeled
EventKey event_key = 1;
// Label values applied, e.g. "sexual" or "hate".
repeated string label_values = 2;
}
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;
}