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 matchingfromrecord on their side does that. A domain MAY additionally verify incoming content against its owntorecord before accepting it, as a second check layered on top of the sender'sfromgate — the protocol does not require this, since thefromcheck 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
ckis thesubLabel. Subscribing to it grants access to the thing itself. - Subbable-related content (e.g. a membership record) — the
subLabelis its parent'sck, 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
subLabelis its parent'sckwithContentappended, 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.