Subscription Token

This token represents a subscription to a subscribable action, such as a conversation (CONV). Subscriptions control who receives messages and updates from group activities.

The subscription token must contain both a subject (sub) field referencing the subscribable action and an audience (aud) field specifying the owner of that action. For other constraints see the Action Tokens. Like every action token it is content-addressed — see Content Addressing.

Purpose

A SUBS token records that one profile is a participant in one subscribable action: it is the membership list, the delivery list and the role assignment all at once, and creating or deleting it is how a user joins or leaves.

Required Fields

Field Required Description
iss Yes The subscriber’s identity
aud Yes The owner of the subject action (e.g., CONV creator)
sub Yes The action_id being subscribed to (a1~...)
t Yes “SUBS”, “SUBS:UPD”, or “SUBS:DEL”
c Optional Subscription metadata (role, invitedBy, message)

Subtypes

Subtype Description
SUBS Create a new subscription (join)
SUBS:UPD Update subscription (change role)
SUBS:DEL Delete subscription (leave)

Content Structure

The optional content (c) field contains subscription metadata:

{
  "role": "member",
  "invitedBy": "alice.cloudillo.net",
  "message": "Joining the team discussion"
}
Property Type Required Description
role string No Participant role: “observer”, “member”, “moderator”, “admin”
invitedBy string No Identity of the user who invited this subscriber
message string No Optional message when joining
Note

The authoritative role is stored server-side as x.role (extensible metadata, not in the signed JWT). The subject owner assigns it — e.g. the CONV creator’s auto-subscription gets x.role: "admin", and an INVT acceptance creates a SUBS with x.role: "member". A role in the content payload is only used as a fallback. The default role for a plain subscription is member.

Database Key

The database key for a subscription token is {type}:{sub}:{iss}

This ensures only one active subscription per user per subject — see Database Keys. Example: SUBS:a1~conv123:alice.example.com.

Auto-Accept Logic

When a SUBS token is received, on_receive (crates/cloudillo-action/src/native_hooks/subs.rs:59) walks a fixed decision tree. The first branch that matches wins.

flowchart TD
    S[SUBS received] --> O{"Target has the 'O' (open) flag?"}
    O -- yes --> A["Accept, status 'A'"]
    O -- no --> I{"Invitation on record for this issuer?"}
    I -- yes --> A
    I -- no --> C{"Issuer is the target action's creator?"}
    C -- yes --> A
    C -- no --> P{"Arrived pre-approved (bundled under an owner APRV)?"}
    P -- yes --> A
    P -- no --> R["Reject, status 'D'"]

The pre-approved branch (subs.rs:117-124) is what lets a member node accept a subscription belonging to somebody it has never met. When a container owner vouches for an accepted child, the child travels bundled under the owner’s APRV as its related token; the receiving node has no local invitation and the issuer is not the creator, but the owner’s approval already answers the question. See Related Action Bundling and the worked example in Walkthrough — A Conversation in a Community.

Accepting also retires any outstanding invitation for that subject, so a consumed invitation no longer counts as pending. If no branch matches, the subscription rests at status 'D'.

Participant Roles

Each role is a superset of the one below it. Default role is member.

Role Additional permissions
observer Read only
member + write (send messages)
moderator + invite/remove members
admin + manage conversation settings
Warning

These are not community roles. The four roles above are subscription roles — they live on a single SUBS, in server-side x.role (crates/cloudillo-action/src/helpers.rs:222-243), and they say what you may do inside one conversation. Community membership uses a separate, longer hierarchy — public, follower, supporter, contributor, moderator, leader (crates/cloudillo-types/src/roles.rs:15-16) — stored in the profile’s roles column, saying what you may do in the community as a whole. The two overlap only in the word “moderator”, and neither implies the other: a community moderator with no SUBS is not in the conversation at all. See Communities.

Federation Flow

Subscribing to a Conversation

User creates SUBS token
         │
         ▼
┌─────────────────────────────┐
│ Local processing            │
│ - Validate subject exists   │
│ - Check for INVT or 'O'     │
└───────────┬─────────────────┘
            │
            ▼
┌─────────────────────────────┐
│ schedule_delivery()         │
│ - Send to subject owner     │
└───────────┬─────────────────┘
            │
            ▼
    Owner receives SUBS
         │
         ▼
┌─────────────────────────────┐
│ on_receive hook             │
│ - Check auto-accept logic   │
│ - Accept or reject          │
│ - Update subscription list  │
└─────────────────────────────┘

Message Fan-Out and Leaving

Once subscribed, messages are delivered via schedule_subscriber_fanout() which walks the parent chain to the CONV and delivers to all subscribers. See Subscriber Fan-Out for details.

To leave, the user creates a SUBS:DEL token. The subject owner marks the subscription as deleted and removes the user from the subscriber list.

Example

User @bob.cloudillo.net subscribes to a conversation owned by @alice.cloudillo.net:

Field Value
iss bob.cloudillo.net
aud alice.cloudillo.net
iat 2024-04-13T00:01:10.000Z
k 20240301
t SUBS
sub a1~NAado5PS4j5+abYtRpBELU0e5OQ+zGf/tuuWvUwQ6PA=
c {“role”: “member”, “invitedBy”: “alice.cloudillo.net”}

Status Values

Status Description
A (Active) Subscription is active, user receives messages
D (Deleted/Rejected) User left, was removed, or the subscription was rejected (no invitation, not open)

See Also