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.
Content-Addressing
This token is content-addressed using SHA-256:
- The entire JWT token (header + payload + signature) is hashed
- Action ID format:
a1~{base64_hash} - Changing any field invalidates the action_id
- See Content-Addressing & Merkle Trees for details
Immutability: Once created, a SUBS token cannot be modified without changing its action ID.
Purpose
SUBS tokens serve several important purposes:
- Membership Management: Track who is part of a conversation or group
- Message Delivery: Determine who receives messages sent to the group
- Role Assignment: Assign permissions (observer, member, moderator, admin)
- Participation Control: Enable joining, leaving, and role changes
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 |
Database Key
The database key for a subscription token is {type}:{sub}:{iss}
Purpose: This key ensures that a user can only have one active subscription to a specific action. The key components are:
{type}: “SUBS” (base type){sub}: Subject ID (what they’re subscribing to){iss}: Issuer identity (who is subscribing)
Example:
- Alice subscribes to a CONV β Stored with key
SUBS:a1~conv123:alice.example.com - Alice updates her subscription β New SUBS:UPD with same key pattern
- Only ONE active subscription per user per subject
Auto-Accept Logic
When a SUBS token is received, the system determines whether to accept it automatically:
Subscription received
β
βΌ
βββββββββββββββββββββββββββββββ
β Check auto-accept conditionsβ
βββββββββββββ¬ββββββββββββββββββ
β
βββββββββ΄ββββββββ¬ββββββββββββββββ¬ββββββββββββββββ
βΌ βΌ βΌ βΌ
Subject has INVT exists Issuer is Otherwise
'O' flag? for user? subject creator?
β β β β
βΌ βΌ βΌ βΌ
Accept Accept Accept Reject
(Open) (Invited) (Creator) (status='R')Auto-accept conditions (any one is sufficient):
- Subject action has the ‘O’ flag (open - anyone can join)
- An INVT token exists for this user + subject combination
- The subscriber is the creator of the subject action
If none of these conditions are met, the subscription is rejected (status=‘R’).
Participant Roles
Subscriptions can specify different access levels:
| Role | Can Read | Can Write | Can Invite | Can Moderate | Can Admin |
|---|---|---|---|---|---|
| observer | Yes | No | No | No | No |
| member | Yes | Yes | No | No | No |
| moderator | Yes | Yes | Yes | Yes | No |
| admin | Yes | Yes | Yes | Yes | Yes |
Default role: member (if not specified)
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 β
βββββββββββββββββββββββββββββββReceiving Messages After Subscription
Once subscribed, messages sent to the conversation are delivered via fan-out:
Message sent to CONV
β
βΌ
βββββββββββββββββββββββββββββββ
β schedule_subscriber_fanout()β
β - Walk parent chain to CONV β
β - Get all SUBS for CONV β
β - Deliver to each subscriberβ
βββββββββββββββββββββββββββββββLeaving a Conversation
User creates SUBS:DEL token
β
βΌ
βββββββββββββββββββββββββββββββ
β Delivered to subject owner β
βββββββββββββ¬ββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββ
β on_receive hook β
β - Mark SUBS as deleted β
β - Remove from subscriber β
β list β
βββββββββββββββββββββββββββββββ
β
βΌ
User no longer receives
messages from conversationExample
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 |
| R (Rejected) | Subscription was rejected (no invitation, not open) |
| D (Deleted) | User left or was removed from the group |
See Also
- Conversation Token - Subscribable group conversations
- Invitation Token - Inviting users to subscribe
- Message Token - Messages delivered to subscribers
- Federation Architecture - How messages are distributed to subscribers