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:

  1. Membership Management: Track who is part of a conversation or group
  2. Message Delivery: Determine who receives messages sent to the group
  3. Role Assignment: Assign permissions (observer, member, moderator, admin)
  4. 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):

  1. Subject action has the ‘O’ flag (open - anyone can join)
  2. An INVT token exists for this user + subject combination
  3. 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 conversation

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
R (Rejected) Subscription was rejected (no invitation, not open)
D (Deleted) User left or was removed from the group

See Also