Specs
spec/05-content-model.md · shown exactly as it appears in the repository
05: Content Model
Status: Draft, v0.1.
License: This text is CC-BY-4.0; the field names, enum values, and other machine-readable schema elements defined here are also MPL-2.0. See LICENSE-DOCS and LICENSE.
Overview
Content is any object shared between connections (e.g. a photo, a message, a membership record) typed by its isA descriptor. What content types exist, and how each is categorized for access control, isn't fixed by the protocol: each domain declares its own content model, including which of its types are subbable, subbable-related, unshareable, or subscription-unrelated (see 04-access-control.md's subLabel section). Two domains may categorize structurally similar content completely differently. This chapter defines the fields every content item carries regardless of domain, and the mechanism a domain uses to declare its own content model, not any specific domain's actual content types.
The content envelope
Every content item, regardless of domain, carries a baseline shape:
| Field | Purpose |
|---|---|
ck |
The content's own unique key. |
isA |
Its content type. |
version |
Incremented on every update; used to discard stale/duplicate updates (see 06-propagation.md). |
receivedFromCxnId |
Which connection this content came from, or 'me' if it's the party's own. Determines ownership (below). |
originDOS |
How many hops this content was originally allowed to propagate; originDOS minus remainingDOS gives hops traveled so far. |
remainingDOS |
Hops still remaining; decremented on every further share. Content with none remaining is never shared again (see 06-propagation.md). |
created |
When the content item was first created. |
updated |
When the content item was most recently updated. |
Relationship descriptors
A content item declares its relationships to other content and to connections explicitly, rather than requiring those relationships to be derived by traversing the data graph at the moment they're needed:
- Parent pointer:
{parentIsA}CK(e.g.groupCKon amember): a direct, single-slot "this belongs to that" reference. - Content-reliance:
reliesOn{IsA}CK(e.g.reliesOnMemberCK): a denormalized reference to another content item this one depends on, used so a cascading delete can match directly instead of walking a multi-hop chain to re-derive the relationship. A content type MAY declare as many of these as its dependency graph needs; there's no protocol-imposed limit. - Connection-reliance:
reliesOnCxnId(and as many further slots as needed, e.g.reliesOnSecondaryCxnId): the same denormalization applied to a connection instead of another content item, so that deleting a connection can efficiently find everything that depends on it.
Shareable vs. local descriptors
Not every field on a content item travels with it when shared. An implementation MUST strip, before transmission: receivedFromCxnId, any database-internal identifier (e.g. _id), and every connection-reliance descriptor (reliesOnCxnId and its further slots). A cxnId MUST NOT leave the party that holds it, the same non-linkability guarantee from 00-overview.md, extended to any field referencing one. A domain MAY additionally declare its own fields as local-only (e.g. isSubscribed, fileSize), stripped the same way. Content-reliance descriptors (reliesOn{IsA}CK) are not stripped: they reference another content item's ck, which the receiver needs to reconstruct the same dependency graph on its own side.
Registering a domain's content model
A domain's content model is its own to define; different domains may define it completely differently.
Schema registration and validation
A domain registers a schema for each content type, declaring required fields, mutually-exclusive field groups, at-least-one-of field groups, and allowed values for specific fields. An implementation MUST validate content against its registered schema before saving; invalid content MUST NOT be persisted.
Labeling for access control
Separately, a domain registers how its content types map onto the subLabel categories from 04-access-control.md: which types are subbable, subbable-related, unshareable, or subscription-unrelated, and how to derive a piece of content's parent for the subbable-related case. This is distinct from schema validation above: it drives access-control labeling, not content-shape validation.
Domain identifier and namespace
A domain's wire identifier (the domain field carried in every envelope, e.g. bonafide.pools) and the internal namespace its schema and content validator register under are related but not identical: an implementation MUST derive the namespace from the identifier's final dot-separated segment (bonafide.pools → pools). Both the schema and the content validator (above) register under this derived namespace, not the full dotted identifier.
Ownership and mutation
A content item has exactly one owner: the party whose receivedFromCxnId is 'me'. An implementation MUST reject any attempt to locally create, update, or delete content not owned by the local party; only the owner may originate a mutation. Content received from a peer is written through a separate path (governed by the shareSequence/version ordering rules in 06-propagation.md, not this ownership check). A receiving party MUST NOT treat its own local edits to received content as an authoritative mutation anyone else needs to honor: only the domain's local-only descriptors (above) may be freely changed locally.