Docs Menu

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 — a pool, a post, a member record — typed by its isA descriptor. What content types exist, and how each one is categorized for access control, is not 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. Useful to a receiver that wants to know how far content has traveled (originDOS minus remainingDOS is 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. poolCK on a member): 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 own dependency graph actually 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 it's 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, now extended to any field that references one. A domain MAY additionally declare its own content-type-specific fields as local-only (e.g. isSubscribed, fileSize) that get 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 their own side.

Registering a domain's content model

A domain's content model is its own to define, and different domains may define it completely differently.

Schema registration and validation

A domain registers a schema for each of its content types, 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 it — invalid content MUST NOT be persisted.

Labeling for access control

Separately, a domain registers how its content types map onto the subLabel categories defined in 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 a distinct concern from schema validation above: it exists specifically to drive access-control labeling, not to validate content shape.

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 are registered under are related but not identical: an implementation MUST derive the namespace by taking the final dot-separated segment of the domain identifier (bonafide.pools → pools). Registration — both the schema from Schema registration and validation above and the content validator from Labeling for access control above — is keyed by this derived namespace, not by 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 that isn't 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 by this ownership check), and a receiving party MUST NOT treat its own local edits to received content as an authoritative mutation the owner or anyone else needs to honor — only the domain's own locally-declared fields (the local-only descriptors above) may be freely changed locally.