Docs Menu

Specs

spec/04-access-control.md · shown exactly as it appears in the repository

04 — Access Control

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

A subscription is the mechanism that implements Layer 1 access control (see 00-overview.md): a connection receives nothing it has not been granted, and a subscription record is the only thing that grants it. This layer itself has no idea what content looks like — it only tracks, per connection, which labels it may receive.

The subscription record

A subscription record holds: the connection (or bulk invitation) it binds to, a subLabel, the domain it applies to, a direction, and a subStatus.

subStatus Meaning
pending Requested but not yet granted.
subscribed Active — content under this label is shared.
declined Explicitly refused.
left Previously subscribed, since withdrawn.

direction is relative to the local party:

  • from — this connection receives content under this label from me. An implementation MUST check this record — and only this record — before sending any content under this label; it is the protocol's enforcement point for access control.
  • to — I have asked to receive content under this label from this connection. This drives what I expect to receive and what I display, but it does not, by itself, cause my peer to send me anything — only a matching from record on their side does that. A domain MAY additionally verify incoming content against its own to record before accepting it, as a second check layered on top of the sender's from gate — the protocol does not require this, since the from check on the sending side is already the enforcement point. The Pools domain does not do this; its handler accepts whatever is sent, trusting that only subscribed content was sent in the first place.

subLabel: what access control actually grants access to

A domain does not grant access per piece of content — it grants access per subLabel, a grouping key derived from a content item's type:

  • Subbable content (e.g. a pool, a thread) — its own ck is the subLabel. Subscribing to it grants access to the thing itself.
  • Subbable-related content (e.g. a membership record) — the subLabel is its parent's ck, derived by a domain-supplied function. It travels with the same access grant as its parent.
  • Unshareable or subscription-unrelated content — never assigned a subLabel; it is never shared under any subscription.
  • Anything else — its subLabel is its parent's ck with Content appended, a distinct label from the parent's own — separating "may see this container exists" from "may see what's inside it."

Which content types fall into which category is declared by the domain (see 05-content-model.md), not by this layer.

Subscribing

A party that wants to receive a labelled content sends a subscription / subscribe message to the connection it expects to receive it from, carrying the subLabel and domain. An implementation MUST record its own to subscription locally before sending — the same before-you-act ordering as registering a key before being addressed (see 03-cross-domain.md).

A domain MAY grant either direction, or both, in response to a subscribe message — the protocol only defines the message itself, not what granting it means. In the Pools domain, joining a pool's content grants both directions at once: the joiner is subscribed to receive the pool's content (to), and their own activity in the pool is subscribed to be shared back out to it (from) — this is what makes them a visible member. A domain with a simpler, one-directional content model MAY instead grant only the from record the message actually asked for.

Granting access MAY also fulfill the subscription: backfilling any existing content already matching the label — that the requester doesn't already have and still has propagation hops remaining (see 06-propagation.md) — immediately, bundled in batches of 10, is a domain policy choice, not a protocol requirement. A domain may instead grant access without backfilling anything, leaving the requester to receive only what's shared from that point forward.

Planned extension (non-normative preview): a future version is expected to support granting access scoped to a date range, rather than an all-or-nothing backfill.

Unsubscribing

A party withdraws by sending subscription / unsubscribe. The receiving side updates its own subscription pair to left (or declined, if this was a refusal rather than a withdrawal) and, for domains with a visible membership count, updates that count accordingly.

Refreshing

After a connection is established or completed (see 02-connections.md), or a cross-domain extension is accepted (see 03-cross-domain.md), the completing party MUST send subscription / refreshSubscriptions. This re-sends the party's alias (below) and re-fulfills every label it has currently granted (from, status subscribed) to that connection. It MUST use the recreate operation rather than create — the peer may already hold some of this content from before the resync, and recreate avoids resending it as if it were new.

Alias

An alias is a token (cxnAliasCode) a party shares with its connections so that content propagated through multiple hops (see 06-propagation.md) can be recognized as having come from the same original party, even when it arrives by different paths. Without it, the same person's content or membership could appear as unrelated, duplicate entries to someone several hops away.

A domain MAY use the same alias for every connection, or MAY generate a fresh one per connection — it trades off recognizability for unlinkability. The Pools domain uses one alias for all of a party's connections, because its trust model depends on recognizing the same person's re-shared content and membership correctly across degree-of-separation propagation; this means two of a party's own connections could, if they compared notes directly with each other (outside the protocol — neither the router nor any observer ever sees an alias in the clear), recognize they are both connected to the same person. A domain with a stronger unlinkability requirement MAY instead generate a distinct alias per connection, at the cost of losing that cross-hop recognition.