# XEP-XXXX: Subjective Delegation for Multi-User Chat

Status: Experimental  
Type: Standards Track  
Short Name: subjective-delegation  
Namespace: `urn:xmpp:subjective-delegation:0`  
Dependencies: XEP-0030, XEP-0045, XEP-0060, XEP-0359, XEP-0421  
Optional: XEP-0004, XEP-0128, XEP-0163, XEP-0313, XEP-0334  
Author: Joseph Turner

## 1. Abstract

This specification defines a PubSub-based protocol for subjective
delegation in XMPP Multi-User Chat rooms.

The generic protocol defines a room-scoped graph model in which
participants publish decisions, delegate decision evaluation to other
participants, and locally exclude participants from delegated
traversal.

```
A: What's your favorite line from Romeo and Juliet?
B: That's too OT.  The current topic is XEP design...
A: Alas!  There is no world without Verona walls.
B: I pray thee, leave me to myself.
   *B mutes A*
```

This specification also defines one initial capability: `mute`.

The `mute` capability affects only local client rendering. It does not
remove, ban, kick, moderate, retract, or silence any participant.

## 2. Introduction

Multi-User Chat moderation is objective and room-wide. A moderator’s
action generally affects the room state observed by all participants.

By contrast, each participant can use subjectively delegated `mute` to
compute their own local view of a room by combining their own `mute`
decisions with `mute` decisions imported from delegates.

For example:

- Alice mutes Bob.
- Alice delegates mute decisions to Carol.
- Carol mutes Dave.

Result:

- Alice’s client locally suppresses Bob’s and Dave’s messages.
- Carol’s client locally suppresses Dave’s messages.
- Other participants are unaffected.

Participants can recursively gather decisions from delegate's
delegates.  Participants can also prune delegates from the graph with
exclusion rules.

A richer example:

- Alice delegates mute decisions to Carol recursively.
- Carol mutes Bob.
- Carol delegates mute decisions to Dana.
- Carol also delegates mute decisions to Eve.
- Dana mutes Fred.
- Eve mutes Mallory.
- Alice excludes Dana as a delegate.

Result on Alice's client:

- Alice’s client locally suppresses Bob’s and Mallory's messages.
- Alice’s client does not suppress Dave’s messages through Dana.
- Dana and Dana’s delegates are not traversed for Alice.

This specification uses PubSub for public room graph state and update
notifications. This allows clients joining a room to fetch current
graph state without reconstructing it from MUC message history.

PEP may be used for private multi-device synchronization, but PEP is
not used as the authoritative source for public room graph state.

## 3. Generic Model

Subjective delegation is modeled as a directed graph of edges.

Each edge has:

- an actor,
- a type,
- a capability,
- a room,
- and, usually, a target.

The generic protocol defines edge types and graph traversal behavior.
Individual capabilities define the meaning of their decisions.

## 4. Concepts

### 4.1 Actor

The identity that creates an edge.

### 4.2 Target

The object affected by an edge.

For `delegate` and `exclude` edges, the target is an identity.

For decision edges, the target type is capability-specific. A decision
may target an identity, a message, a thread, or another object defined
by the capability.

### 4.3 Capability

A capability is a decision namespace.

This specification defines one capability:

- `mute`

Future specifications may define additional capabilities.

A capability defines:

- valid decision actions,
- valid target types,
- whether decisions may be public, private, or both,
- how imported decisions are evaluated,
- how local conflicts are resolved.

The generic `<edge/>`, `<actor/>`, and `<target/>` elements are defined
in the generic namespace:

```text
urn:xmpp:subjective-delegation:0
```

Capabilities MAY define additional semantics for actions and targets.

If a capability requires additional XML payload beyond the generic
attributes and elements, that payload SHOULD use a capability-specific
XML namespace.

### 4.4 Decision Edge

A decision edge expresses the actor’s decision within a capability.

Example: Alice publishes a `mute` decision suppressing Bob.

### 4.5 Delegate Edge

A `delegate` edge expresses that the actor imports another identity’s
public decisions for a capability.

A `delegate` edge may also permit recursive traversal through the
target’s public `delegate` edges. Recursion depth is controlled by the
`hops` attribute.

### 4.6 Exclude Edge

An `exclude` edge expresses that the actor excludes the target identity
from delegated traversal for a capability.

For conceptual and computational simplicity, only positive additions
to the delegation graph can be imported from delegates.  Therefore,
`exclude` edges are always local, and MUST NOT be imported through
delegation.

Example:

- Alice delegates mute decisions to Carol recursively.
- Carol delegates mute decisions to Dana.
- Alice excludes Dana.

Result:

- Alice imports Carol’s public mute decisions.
- Alice does not import Dana’s mute decisions through Carol.
- Alice's client does not traverse Dana’s delegates.

### 4.7 Public Edge

A public edge is stored in the room public graph node and visible
according to that node’s access model.

### 4.8 Private Edge

A private edge is stored locally or in private PEP storage visible
only to the actor’s clients.

Private edges are never imported through delegation by other users.

## 5. Edge Types

This specification defines three generic edge types:

- `decision`
- `delegate`
- `exclude`

Generic edge attributes:

| Attribute    | Meaning                              |
|--------------|--------------------------------------|
| `type`       | `decision`, `delegate`, or `exclude` |
| `capability` | Capability name                      |
| `room`       | Room JID                             |

Decision edges additionally have:

| Attribute | Meaning                    |
|-----------|----------------------------|
| `action`  | Capability-specific action |

`delegate` edges additionally have:

| Attribute | Meaning                             |
|-----------|-------------------------------------|
| `hops`    | Non-negative integer or `unbounded` |

## 6. Identity Model

This protocol supports two identity forms:

- bare JIDs,
- XEP-0421 stable occupant IDs.

It does not support:

- MUC nicknames,
- MUC occupant JIDs,
- full JIDs,
- temporary session identifiers,
- display names,
- avatars.

### 6.1 Non-anonymous MUCs

In non-anonymous MUCs, implementations MAY use bare JIDs or XEP-0421
occupant IDs to identify actors and identity targets.

### 6.2 Semi-anonymous MUCs

In semi-anonymous MUCs, implementations MUST use XEP-0421 occupant IDs
only.

Protocol payloads for semi-anonymous rooms MUST NOT contain actor or
target bare JIDs, even if the publishing client knows them.

### 6.3 Anonymous MUCs

In anonymous MUCs, implementations MUST use XEP-0421 occupant IDs
only.

If a participant has no identity appropriate for the room’s anonymity
mode, that participant is not addressable by identity through this
protocol.

### 6.4 Room Scoping

Occupant IDs are scoped to a room. The occupant ID alone is not
globally meaningful.

An implementation MUST interpret an occupant ID together with the
`room` attribute of the edge.

## 7. PubSub Model

There are two storage locations:

- room public graph storage,
- private user graph storage.

This specification uses XEP-0060 PubSub as the storage and
notification substrate for room public graph state.

Private graph storage may be implemented using:

- a private PEP node,
- local client storage.

PEP MUST NOT be used as the authoritative source for public room graph
edges. PEP nodes are discoverable by bare JID and are therefore not
suitable for authoritative public graph state for anonymous and
semi-anonymous rooms.

## 8. Room Public Graph Node

Each supported room SHOULD advertise a public graph node that stores
public edges relevant to that room.

Example node name:

```text
urn:xmpp:subjective-delegation:0/rooms/room@example.org/public
```

The exact node name is implementation-defined, but it MUST be
discoverable.

The public graph node stores only public edges. Capability
specifications define which edges may be public.

### 8.1 Node Requirements

The room public graph node MUST support:

- persistent items,
- item IDs,
- item retraction,
- payload delivery,
- event notifications,
- access control.

The node MUST be configured with:

- persistent items enabled,
- payload delivery enabled,
- event notifications enabled.

The public graph node’s access model SHOULD match the room’s effective access
policy. For example:

- open room: readable by anyone or by current occupants,
- members-only room: readable by members, admins, and owners,
- otherwise restricted room: readable only by entities authorized to access the room.

## 9. Private Graph Storage

Private graph storage contains edges used only by the actor’s own
clients.

Private edges MUST NOT be shared with the room public graph node.

Private storage MUST be readable and writable only by the owning user
or resources authorized by that user.

Private edges belonging to another actor MUST NOT be imported through
delegation.

If PEP is used for private synchronization, private edges are stored
as PubSub items using the same edge payload format.  If private graph
state is stored locally, there is no PubSub `<item/>` wrapper.

## 10. Discovery

A room supporting this protocol MUST advertise protocol support using XEP-0030 service discovery:

```text
urn:xmpp:subjective-delegation:0
```

A room supporting this protocol MUST advertise the PubSub service and
public graph node using XEP-0128 extended service discovery.  The data
form advertises the concrete PubSub location where public graph state
is stored.

Example disco response:

```xml
<iq xmlns="jabber:client"
    type="result"
    from="room@example.org"
    to="alice@example.com/laptop"
    id="disco1">
  <query xmlns="http://jabber.org/protocol/disco#info">
    <feature var="urn:xmpp:subjective-delegation:0"/>
    <feature var="urn:xmpp:occupant-id:0"/>
    <x xmlns="jabber:x:data" type="result">
      <field var="FORM_TYPE" type="hidden">
        <value>urn:xmpp:subjective-delegation:0#disco</value>
      </field>
      <field var="pubsub-service">
        <value>pubsub.example.org</value>
      </field>
      <field var="public-node">
        <value>urn:xmpp:subjective-delegation:0/rooms/room@example.org/public</value>
      </field>
    </x>
  </query>
</iq>
```

## 11. Edge Payload Format

Edges are XML payloads, usually carried inside PubSub `<item/>`
elements.

The edge element is qualified by:

```text
urn:xmpp:subjective-delegation:0
```

### 11.1 Identity Elements

Actors and identity targets are represented using either `jid` or
`occupant-id`:

```xml
<actor jid="alice@example.com"/>
<actor occupant-id="a1"/>
<target jid="bob@example.com"/>
<target occupant-id="b2"/>
```

`occupant-id` MUST be used in anonymous and semi-anonymous rooms.  An
identity element MUST NOT contain both `jid` and `occupant-id`.

### 11.2 Direct Decision Example

Alice publishes a public decision in a non-anonymous room:

```xml
<item xmlns="http://jabber.org/protocol/pubsub" id="edge-1">
  <edge xmlns="urn:xmpp:subjective-delegation:0"
        type="decision"
        capability="mute"
        action="suppress"
        room="room@example.org">
    <actor jid="alice@example.com"/>
    <target jid="bob@example.com"/>
  </edge>
</item>
```

### 11.3 Occupant-ID Decision Example

Occupant `a1` publishes a decision targeting occupant `b2`:

```xml
<item xmlns="http://jabber.org/protocol/pubsub" id="edge-2">
  <edge xmlns="urn:xmpp:subjective-delegation:0"
        type="decision"
        capability="mute"
        action="suppress"
        room="room@example.org">
    <actor occupant-id="a1"/>
    <target occupant-id="b2"/>
  </edge>
</item>
```

### 11.4 Delegate Edge Example

Alice delegates mute decisions to Carol, importing only Carol’s direct
public decisions as indicated by `hops="0"`:

```xml
<item xmlns="http://jabber.org/protocol/pubsub" id="edge-3">
  <edge xmlns="urn:xmpp:subjective-delegation:0"
        type="delegate"
        capability="mute"
        hops="0"
        room="room@example.org">
    <actor jid="alice@example.com"/>
    <target jid="carol@example.com"/>
  </edge>
</item>
```

### 11.5 Recursive Delegate Edge Example

Alice delegates mute decisions to Carol and permits recursive
traversal.  In the following example, `hops="2"` means that Alice's
client will aggregate decisions from Carol, Carol's delegates, and
Carol's delegate's delegates:

```xml
<item xmlns="http://jabber.org/protocol/pubsub" id="edge-4">
  <edge xmlns="urn:xmpp:subjective-delegation:0"
        type="delegate"
        capability="mute"
        hops="2"
        room="room@example.org">
    <actor jid="alice@example.com"/>
    <target jid="carol@example.com"/>
  </edge>
</item>
```

Alternatively, use `hops="unbounded"` to indicate that recursion
continues indefinitely.

### 11.6 Private Exclude Edge Example

Alice excludes Dana from mute delegation traversal:

```xml
<item xmlns="http://jabber.org/protocol/pubsub" id="edge-6">
  <edge xmlns="urn:xmpp:subjective-delegation:0"
        type="exclude"
        capability="mute"
        room="room@example.org">
    <actor jid="alice@example.com"/>
    <target jid="dana@example.com"/>
  </edge>
</item>
```

This edge is private and MUST NOT be published to the room public graph
node.

### 11.7 Private Decision Example

The protocol for a particular capability may specify that some
decisions can be private.  For example, the `mute` capability
specifies that `"allow"` decisions are always private.  In the
following example, Alice privately allows Bob:

```xml
<item xmlns="http://jabber.org/protocol/pubsub" id="edge-5">
  <edge xmlns="urn:xmpp:subjective-delegation:0"
        type="decision"
        capability="mute"
        action="allow"
        room="room@example.org">
    <actor jid="alice@example.com"/>
    <target jid="bob@example.com"/>
  </edge>
</item>
```

This example assumes private storage uses PEP. If stored locally, the
`<edge/>` payload is stored without a PubSub `<item/>` wrapper.

## 12. Hops Semantics

A `delegate` edge imports the target’s direct public decisions for the
named capability.

The `hops` attribute controls whether the target’s public delegate
edges are also followed.

| `hops`      | Meaning                                                          |
|-------------|------------------------------------------------------------------|
| `0`         | Import target’s direct public decisions only                     |
| `1`         | Also import target’s delegates' decisions                        |
| `2`         | Also import target's delegates' delegates' decisions             |
| `unbounded` | Continue importing recursively, subject to implementation limits |

## 13. Publishing Public Edges

A client publishes a public edge using XEP-0060 publish.

```xml
<iq xmlns="jabber:client"
    type="set"
    from="alice@example.com/laptop"
    to="pubsub.example.org"
    id="pub1">
  <pubsub xmlns="http://jabber.org/protocol/pubsub">
    <publish node="urn:xmpp:subjective-delegation:0/rooms/room@example.org/public">
      <item id="edge-1">
        <edge xmlns="urn:xmpp:subjective-delegation:0"
              type="decision"
              capability="mute"
              action="suppress"
              room="room@example.org">
          <actor jid="alice@example.com"/>
          <target jid="bob@example.com"/>
        </edge>
      </item>
    </publish>
  </pubsub>
</iq>
```

The PubSub service then sends normal PubSub event notifications to
subscribers.

## 14. Revocation

Edges are stateful. Revocation is performed by retracting the PubSub
item.

```xml
<iq xmlns="jabber:client"
    type="set"
    from="alice@example.com/laptop"
    to="pubsub.example.org"
    id="retract1">
  <pubsub xmlns="http://jabber.org/protocol/pubsub">
    <retract node="urn:xmpp:subjective-delegation:0/rooms/room@example.org/public">
      <item id="edge-1"/>
    </retract>
  </pubsub>
</iq>
```

After retraction, clients MUST remove the edge from their local graph.

Implementations SHOULD use stable item IDs. An item ID SHOULD identify
exactly one active edge.

## 15. Notifications and Synchronization

Subscribers receive updates via normal PubSub event notifications.

Clients SHOULD fetch current node state:

- on login,
- on reconnect,
- on room join,
- after missed notifications are suspected.

PubSub notifications are not authoritative. The PubSub item set is
authoritative.

Services MAY include XEP-0334 `no-store` hints on PubSub notification
messages because durable state is stored in PubSub items rather than
notification messages.

## 16. Generic Delegation Evaluation

The following Scheme-like pseudocode defines behavior, not implementation language.

For local actor `U`, room `R`, and capability `C`, evaluation uses:

- decisions authored by `U`,
- `delegate` edges authored by `U`,
- private `exclude` edges authored by `U`,
- public `delegate` edges reachable through delegation.
- public decisions reachable through delegation.

Private edges authored by other actors are never used. Exclude edges are never imported.

```scheme
(define (delegated-decisions U R C)
  (append-map
   (lambda (edge)
     (let ((T (edge-target edge)))
       (if (excluded? U R C T)
           '()
           (traverse U R C T (edge-hops edge) '()))))
   (delegate-edges-authored-by U R C)))
```

```scheme
(define (traverse U R C X hops visited)
  (cond
   ((member X visited) '())
   ((excluded? U R C X) '())
   (else
    (append
     (public-importable-decisions X R C)
     (if (zero? hops)
         '()
         (append-map
          (lambda (edge)
            (traverse U R C
                      (edge-target edge)
                      (hops-min (hops-dec hops) (edge-hops edge))
                      (cons X visited)))
          (public-delegate-edges X R C)))))))
```

```scheme
(define (hops-dec hops)
  (if (eq? hops 'unbounded)
      'unbounded
      (- hops 1)))

(define (hops-min a b)
  (cond
   ((eq? a 'unbounded) b)
   ((eq? b 'unbounded) a)
   (else (min a b))))
```

Helper meanings:

- `delegate-edges-authored-by` returns public or private delegate
  edges authored by `U` for capability `C`.
- `excluded?` checks whether identity `X` is privately excluded from
  delegation by `U`.
- `public-importable-decisions` returns public decisions that
  capability `C` allows to be imported.
- `public-delegate-edges` returns public delegate edges.
- `edge-hops` is a non-negative integer or `unbounded`.

Note that in the above `traverse` function, the `visited` argument only
detects cycles within the current recursive branch. Implementations
SHOULD deduplicate returned decisions, for example by stable PubSub
item ID. Implementations MAY use caching or a global visited structure
only if doing so preserves the semantics of `hops` and local
exclusions.

Implementations MUST detect cycles and SHOULD impose limits on
recursion depth, edge count, node count, and computation time.

# Mute Capability

## 17. Capability Name

The mute capability name is:

```text
mute
```

The mute capability controls local rendering of ordinary MUC messages.

It does not affect room state and does not grant moderation power.

## 18. Capability namespace

The `mute` capability uses only the generic edge payload format
defined by this specification. It does not define a separate
capability-specific XML namespace.

## 19. Mute Decision Actions

The mute capability defines two decision actions:

| Action     | Meaning                                       |
|------------|-----------------------------------------------|
| `suppress` | Locally suppress matching messages            |
| `allow`    | Locally override delegated suppress decisions |

### 19.1 Suppress

A `suppress` decision means the actor does not want matching ordinary
MUC messages shown in the main conversation view.

A client SHOULD suppress notifications for suppressed messages.

A client MAY provide a way to reveal suppressed messages.

### 19.2 Allow

An `allow` decision is a private local override. It causes matching
messages to remain visible to the local actor even if they are
suppressed by delegated decisions or by the actor’s own active
`suppress` decisions.

An `allow` decision does not retract, modify, or hide any public
`suppress` decision from other users. Users who delegate to the actor
may still import the actor’s public `suppress` decisions.

Clients SHOULD warn when a local `allow` conflicts with an active
public or private `suppress` authored by the same actor.

## 20. Mute Edge Visibility Rules

`suppress` decisions may be public or private.  Public `suppress`
decisions may be imported through delegations.  

For conceptual and computational simplicity, only `suppress` decisions
can be imported from delegates.  Therefore, `allow` edges are always
local, and MUST NOT be imported through delegation.

## 21. Mute Targets

Mute decision edges may target:

- identities,
- messages,
- threads.

Mute `delegate` and `exclude` edges target identities only.

### 21.1 Identity Target

An identity target uses `jid` or `occupant-id`:

```xml
<target jid="bob@example.com"/>
```

or:

```xml
<target occupant-id="b2"/>
```

### 21.2 Message Target

A message target uses a XEP-0359 stanza ID.

```xml
<target stanza-id="msg-123" by="room@example.org"/>
```

The `stanza-id` attribute contains the XEP-0359 stable stanza ID. The
`by` attribute identifies the assigning entity.

### 21.3 Thread Target

Mute decision edges MAY target RFC 6121 message thread
identifiers. Clients that do not support or preserve `<thread/>` MAY
ignore thread targets.
 
```xml
<target thread="thread-123"/>
```

Thread identifiers are room-scoped by the edge’s `room` attribute.

## 22. Effective Mute Evaluation

For local actor `U` in room `R`, effective mute targets are:

```scheme
(define (effective-mute U R)
  (difference
   (union
    (direct-suppress U R)
    (delegated-suppress U R))
   (local-allow U R)))
```

Where:

- `direct-suppress` returns `suppress` decisions authored by `U`,
  whether public or private.
- `delegated-suppress` returns imported public `suppress` decisions.
- `local-allow` returns private `allow` decisions authored by `U`.

A local `allow` decision cancels matching `suppress` decisions for the
local actor’s own view, including `suppress` decisions authored by the
local actor.

A local `allow` decision does not retract or modify any public
`suppress` decision. Therefore, if Alice publicly suppresses Bob and
privately allows Bob, Alice’s client shows Bob, but users who delegate
mute decisions to Alice can still import Alice’s public suppress
decision for Bob.

Clients SHOULD warn when a local `allow` conflicts with an active
public or private `suppress` authored by the same actor.

## 23. Mute Message Filtering

When rendering a MUC message, the client determines whether the
message matches any target in `effective-mute`.

A message is suppressed if any of the following match:

- sender identity,
- message stanza ID,
- thread identifier.

## 24. Mute Examples

The following examples show `<edge/>` payloads only. Public examples
are published as PubSub `<item/>` payloads in the room public graph
node.  Private examples are stored in private PEP or local storage.

### 24.1 Direct Public Mute

Carol mutes Dave publicly in a non-anonymous room:

```xml
<edge xmlns="urn:xmpp:subjective-delegation:0"
      type="decision"
      capability="mute"
      action="suppress"
      room="room@example.org">
  <actor jid="carol@example.com"/>
  <target jid="dave@example.com"/>
</edge>
```

### 24.2 Local Allow

Alice allows Dave, overriding any potential `mute` decisions:

```xml
<edge xmlns="urn:xmpp:subjective-delegation:0"
      type="decision"
      capability="mute"
      action="allow"
      room="room@example.org">
  <actor jid="alice@example.com"/>
  <target jid="dave@example.com"/>
</edge>
```

Dave is visible to Alice.  If Alice also has an active public
`suppress` decision for Dave, users who delegate mute decisions to
Alice may still suppress Dave.

### 24.3 Message-Specific Mute

Alice suppresses a particular message:

```xml
<edge xmlns="urn:xmpp:subjective-delegation:0"
      type="decision"
      capability="mute"
      action="suppress"
      room="room@example.org">
  <actor jid="alice@example.com"/>
  <target stanza-id="msg-123" by="room@example.org"/>
</edge>
```

In message targets, the `by` attribute MUST be present and MUST match
the `by` attribute of the XEP-0359 `<stanza-id/>` being referenced. In
typical MUC usage, this value is the room JID and therefore matches
the edge’s `room` attribute.

### 24.4 Thread Mute

Alice suppresses a thread:

```xml
<edge xmlns="urn:xmpp:subjective-delegation:0"
      type="decision"
      capability="mute"
      action="suppress"
      room="room@example.org">
  <actor jid="alice@example.com"/>
  <target thread="thread-123"/>
</edge>
```

## 25. Actor Authenticity

For bare-JID edges, the PubSub service MUST ensure that the publisher
is authorized to publish edges for the claimed actor JID.

For occupant-ID edges, the PubSub service MUST ensure that the
publisher is authorized to publish edges for the claimed XEP-0421
occupant ID in the specified room.

This may require integration between:

- the MUC service,
- the PubSub service,
- the occupant-ID provider.

A client MUST NOT trust a self-asserted actor occupant ID in a public
PubSub item unless the service enforces this binding.

## 26. Access Control

Access control is required for both reading and writing.

The service MUST prevent unauthorized publication of edges for another
actor.

The service MUST prevent unauthorized retraction of another actor’s
edges.

The room public graph node SHOULD use an access model consistent with
the room’s access policy.

Private graph storage MUST be readable and writable only by the owning
user or authorized resources.

In semi-anonymous and anonymous rooms, the PubSub service MUST NOT
expose bare JIDs through:

- payloads,
- node names,
- notifications,
- access-control side effects visible to unauthorized entities.

## 27. Privacy Rules

In semi-anonymous and anonymous rooms, stable occupant-IDs are used to
identify actors and identity targets.  Bare JIDs MUST NOT appear.

In non-anonymous rooms, bare JIDs are permitted because the room
already exposes them.

Public delegation graphs may reveal social information. For example,
they may reveal who mutes whom and whose mute decisions are delegated
to by others.  Implementations SHOULD make this clear to users.

## 28. Interaction with Existing XEPs

### 28.1 XEP-0045: Multi-User Chat

This protocol does not alter MUC roles, affiliations, bans, kicks,
voice, or moderation.  A delegate is not a MUC moderator.

### 28.2 XEP-0060: Publish-Subscribe

XEP-0060 is the storage and notification substrate for public room
graph state.

### 28.3 XEP-0163: Personal Eventing Protocol

PEP MAY be used for private multi-device synchronization.

PEP MUST NOT be used as an authoritative source for public room graph
edges.

### 28.4 XEP-0359: Unique and Stable Stanza IDs

The mute capability may use XEP-0359 stanza IDs to target individual
messages.

### 28.5 XEP-0421: Occupant IDs

XEP-0421 is required for semi-anonymous and anonymous room support.

### 28.6 XEP-0313: Message Archive Management

Clients MAY apply the same capability-specific filtering to archived
messages.

### 28.7 XEP-0334: Message Processing Hints

Message Processing Hints are not required for protocol correctness.

Services MAY include `no-store` hints on PubSub notification messages.

## 29. Security Considerations

Implementations MUST consider:

- actor spoofing,
- occupant-ID forgery,
- unauthorized PubSub publication,
- unauthorized PubSub subscription,
- unauthorized item retraction,
- deanonymization through bare JID leakage,
- cycles in delegation graphs,
- denial of service through large graphs,
- stale or rotated occupant IDs.

## 30. Client UX Recommendations

Clients SHOULD distinguish:

- direct local decisions,
- delegated decisions,
- local allow rules,
- local exclude rules,
- delegates with limited hops,
- delegates with recursive traversal.

For the mute capability, suggested UI actions include:

- private mute,
- public mute,
- unmute
- allow despite delegated mute,
- remove allow rule,
- delegate mute decisions,
- set delegate hops,
- exclude delegate,
- remove exclude rule.

Instead of offering an option to set delegate hops, a simple
delegation UI may provide a choice between zero-hop "delegates" and
unbounded "superdelegates".

If a user selects “unmute” for a participant muted only through
delegation, the client SHOULD create a private `allow` decision.

If a user selects “unmute” for a participant directly muted by the
user, the client SHOULD retract the direct `suppress` decision instead
of creating a private `allow` decision.

If a user excludes a directly appointed delegate, the client SHOULD
normally retract the direct `delegate` edge instead of creating an
`exclude` edge.

### 30.1 Delegation graph

Clients MAY provide a navigable interface for viewing the graph of
delegates for a particular room and capability.  See the "Prior Art"
section for examples.

## 31. Delegation Seeds

A future version of this specification may define delegation seeds for
bootstrapping a participant’s private delegation graph when joining a
room.

A delegation seed is a recommended set of initial private edges
associated with a room. For example, an invitation link or
out-of-band invitation mechanism could recommend that a joining
participant delegate the `mute` capability to one or more existing
room participants.

A seed does not affect any participant unless that participant’s
client accepts it.  If accepted, seeded edges are created as private
edges authored by the joining participant.

Seed child `<edge/>` elements are templates, not graph edges. They
therefore omit the actor. The actor is filled in by the accepting
client.

Example seed:

```xml
<seed xmlns="urn:xmpp:subjective-delegation:0"
      room="room@example.org">
  <edge type="delegate" capability="mute" hops="0">
    <target jid="carol@example.com"/>
  </edge>
  <edge type="delegate" capability="mute" hops="unbounded">
    <target jid="dana@example.com"/>
  </edge>
</seed>
```

If Alice accepts this seed, her client creates private edges like the
following:

```xml
<edge xmlns="urn:xmpp:subjective-delegation:0"
      type="delegate"
      capability="mute"
      hops="0"
      room="room@example.org">
  <actor jid="alice@example.com"/>
  <target jid="carol@example.com"/>
</edge>
```

Clients MAY also use the existing set of room owners, admins, and
moderators as seed delegates.

Future work:

- how seeds are encoded in XMPP URIs,
- how seed data is fetched from PubSub or another source,
- whether seeds are signed,
- how clients verify seed authenticity,
- how invitation links or seed access tokens interact with XMPP access control,
- how seeds represent occupant-ID targets in anonymous or
  semi-anonymous rooms.

## 32. Open Issues

- Exact PubSub node naming convention.
- Whether room public graph nodes must be hosted by the MUC service.
- How edge IDs are generated, e.g., using deterministic hashes.
- Handling occupant-ID rotation policies.
- Cache expiration after users leave a room.
- Fine-grained muting, such as muting a participant within a
  particular thread,

## 33. Prior Art and Related Work

Credit for inspiring this XEP goes to:

- [Alexander Cobleigh’s TrustNet](https://cblgh.org/trustnet/), which
  describes a subjective trust graph model in which users can delegate
  moderation-like decisions to trusted peers, producing different
  local views for different participants.

- [The Cable moderation protocol](https://cabal.chat/moderation.html),
  used in Cabal, which explores moderation as graph-based,
  user-centered social state rather than solely centralized server
  authority.

- [USHIN’s hyperdrive.el EmacsConf 2024
  presentation](https://emacsconf.org/2024/talks/hyperdrive/), which
  demonstrates a navigable UI for viewing the graph of sources/delegates.

## 34. Summary

This specification defines subjective delegation for MUC using PubSub.

The generic model is:

- `decision`: express a decision within a capability,
- `delegate`: import another identity’s public decisions for a
  capability,
- `exclude`: locally remove an identity from delegated traversal,
- `capability`: a named decision area with capability-specific semantics.

This specification also defines the `mute` capability:

- `suppress`: locally suppress matching messages,
- `allow`: locally override delegated suppress decisions.
