Skip to content

Commit 3f9e37c

Browse files
sanityclaude
andcommitted
docs: add article on River's message-based member lifecycle
Explains the automatic pruning mechanism, invite chain preservation, and ban persistence through pruning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3c1d907 commit 3f9e37c

File tree

3 files changed

+308
-0
lines changed

3 files changed

+308
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
+++
2+
title = "Self-Managing Communities: How River Handles Inactive Members"
3+
date = 2026-02-15
4+
author = "Ian Clarke"
5+
author_link = "https://x.com/sanity"
6+
tags = ["front-page"]
7+
+++
8+
9+
Membership management is a surprisingly interesting problem in decentralized systems. In a centralized
10+
chat app, an admin can remove inactive users from a group. But in a decentralized system without
11+
servers, who decides when someone is no longer active? And how do you enforce that decision across
12+
every peer?
13+
14+
River answers this with a mechanism we call **message-based member lifecycle**: your
15+
presence in a room's member list is tied to whether you have recent messages. No messages, no
16+
membership entry. Send a message, and you're back automatically.
17+
18+
#### The Problem
19+
20+
Previously, River's room member list only grew. The only way to remove someone was to ban them, which
21+
is a hostile action that isn't appropriate for someone who simply stopped chatting. Over time, rooms
22+
would accumulate dozens of inactive members, making the member list meaningless and wasting bandwidth
23+
synchronizing their membership data across the network.
24+
25+
This is a common problem in decentralized systems. Without a central authority to curate membership,
26+
most protocols either ignore the problem (letting lists grow unboundedly) or require manual
27+
intervention from room administrators.
28+
29+
#### The Solution
30+
31+
River takes a different approach. The room contract — the WebAssembly code that every peer runs to
32+
validate state changes — now enforces a simple rule: **members exist in the list only while they have
33+
at least one message in the room's recent message window** (100 messages by default, configurable by
34+
the room owner).
35+
36+
When your last message ages out of the recent messages buffer, you're automatically pruned from the
37+
member list. When you send a new message, your original invitation is bundled with the message, and
38+
you reappear. From the user's perspective, nothing changes — they can always participate in rooms
39+
they've been invited to. But the member list now reflects who's actually active.
40+
41+
This is conceptually similar to how a conference room works in real life: if you leave and come back
42+
later, you don't need a new invitation — but no one would list you as "present" while you're away.
43+
44+
<img src="/img/member-lifecycle.svg" alt="Member lifecycle diagram showing how members are pruned when inactive and automatically re-added when they send a message" style="width: 100%; max-width: 800px; margin: 20px 0;">
45+
46+
#### Preserving Invite Chains
47+
48+
One subtlety: River's permission model uses invite chains. The room owner invites Bob and Dave, Bob
49+
invites Carol. If Bob goes inactive and gets pruned, Carol's invitation would become unverifiable —
50+
her invite chain back to the room owner would be broken.
51+
52+
The pruning algorithm handles this by keeping members who are in the invite chain of anyone with
53+
recent messages. Bob stays in the list as long as Carol (or anyone Bob invited) is active, even if
54+
Bob himself hasn't sent a message recently. Dave, having no active members in his invite chain, gets
55+
pruned.
56+
57+
<img src="/img/invite-chain-pruning.svg" alt="Invite chain preservation diagram showing how inactive members in an active member's invite chain are kept" style="width: 100%; max-width: 800px; margin: 20px 0;">
58+
59+
#### Bans Survive Pruning
60+
61+
Getting the interaction between pruning and bans right required careful thought. If Alice bans
62+
Charlie and then Alice goes inactive, what happens to the ban?
63+
64+
The old logic removed bans when the banning member left the member list — which would mean that
65+
inactive members' bans would silently disappear, allowing banned users to rejoin. The new logic
66+
distinguishes between members who were *pruned* (just inactive) and members who were *banned*
67+
(explicitly removed). Bans issued by pruned members persist. Only bans from members who were
68+
themselves banned are treated as orphaned and removed.
69+
70+
#### Enforced by the Contract
71+
72+
All of this runs inside the room contract's WebAssembly, which means every peer enforces the same
73+
rules. Any delta that arrives — whether from a current or outdated peer — is validated and then cleaned up
74+
by the contract's `post_apply_cleanup` function, which runs after every state change and
75+
deterministically prunes members who shouldn't be there.
76+
77+
This is the power of Freenet's contract model: the rules of the application are embedded in code that
78+
every participant executes, not policies that a server enforces on your behalf.
79+
80+
#### What It Looks Like
81+
82+
For users, the change is minimal. The member list sidebar now shows "Active Members" — the people
83+
actually participating. If you've been invited to a room but haven't chatted recently, you won't
84+
appear in the list, but you can send a message at any time and you'll reappear instantly. No
85+
re-invitation needed, no admin action required.
86+
87+
#### Current Limitations
88+
89+
Private rooms are temporarily disabled while we work through the implications of pruning for
90+
encrypted room key distribution, where the room owner needs to be online to distribute secrets to
91+
re-joining members.
92+
93+
#### A Pattern for Decentralized Systems
94+
95+
The broader lesson here is that decentralized applications need self-managing data structures.
96+
Without a server to run maintenance tasks, the data itself has to define its own lifecycle rules. By
97+
embedding pruning logic in the contract, River rooms stay clean without any human intervention — a
98+
small example of the kind of autonomous behavior that makes decentralized applications practical.
Lines changed: 130 additions & 0 deletions
Loading
Lines changed: 80 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)