Core System Architecture Part 86 — Anonymous Network Group Directory, Communities, Channels, Membership Discovery, Moderation Boundaries & Privacy-Preserving Social Spaces Architecture
SIAR — Secure, Local-First, Multi-Transport Communication Platform
Series: Core System Architecture
Part: 86
Status: Architecture specification
Primary language: Rust
Builds on: Core System Architecture Parts 43, 45–46, 48, 56, 58–59, 68–69, 74–85
Primary purpose: define SIAR's group directory, community, channel, membership-discovery, join/leave, moderation-boundary, announcement, large-group, public/private social-space, federation, abuse-resistance, and privacy-preserving social architecture without constructing a centralized map of group membership or user social participation.
1. Purpose
Social spaces create powerful community features, but also major privacy risks.
A centralized group system can reveal:
which communities users join
which topics users follow
which organizations overlap
which channels people read
which members moderate whom
The governing principle is:
SIAR social spaces must support discovery, communities, channels, and moderation without turning membership or participation into a globally queryable identity graph.
2. Architectural Position
Directory / Invite / Capability
│
▼
Social Space
│
┌───────┼────────┐
│ │ │
Community Group Channel
│ │ │
└───────┼────────┘
▼
Membership State
│
▼
Roles / Moderation Scope
3. Core Separation
Keep distinct:
group
community
channel
directory entry
membership
role
moderation authority
announcement subscription
federation membership
4. Non-Goals
Part 86 does not create:
a global membership directory
a global list of who joined what
a universal moderator identity
automatic contact creation from group membership
public participant history by default
5. Social Space Types
#![allow(unused)] fn main() { pub enum SocialSpaceType { Group, Community, Channel, AnnouncementChannel, ManagedWorkspaceSpace, } }
6. Group
Interactive many-to-many space.
7. Community
Container for multiple related spaces.
8. Channel
Topic/room within community or standalone.
9. Announcement Channel
One-to-many or limited-publisher broadcast.
10. Managed Workspace Space
Org-controlled social space.
11. Social Space ID
#![allow(unused)] fn main() { pub struct SocialSpaceId(pub [u8; 32]); }
12. ID Scope
Opaque logical identifier.
13. Hard Rule
SocialSpaceId is not user identity and does not encode owner/member data.
14. Community ID
#![allow(unused)] fn main() { pub struct CommunityId(pub [u8; 32]); }
15. Channel ID
#![allow(unused)] fn main() { pub struct ChannelId(pub [u8; 32]); }
16. Directory Entry ID
Separate.
#![allow(unused)] fn main() { pub struct DirectoryEntryId(pub [u8; 32]); }
17. Hard Rule
Directory entry identity and social-space identity remain separate.
18. Social Space Visibility
#![allow(unused)] fn main() { pub enum SocialSpaceVisibility { Private, InviteOnly, ExactHandle, Listed, Public, } }
19. Private
No directory entry.
20. InviteOnly
Capability required.
21. ExactHandle
Exact lookup only.
22. Listed
Appears in scoped directory/category.
23. Public
Broadly discoverable.
24. Default
Private or InviteOnly.
25. Hard Rule
Creating a group does not automatically publish it.
26. Directory Model
Directory is metadata projection, not authority.
27. Directory Record
#![allow(unused)] fn main() { pub struct SocialDirectoryRecord { pub entry_id: DirectoryEntryId, pub social_space: SocialSpaceRef, pub title: DirectoryTitle, pub category: Option<DirectoryCategory>, pub visibility: SocialSpaceVisibility, pub capabilities: DirectoryCapabilities, pub signatures: DirectorySignatureBundle, } }
28. Directory Record Must Not Include
full member list
member account IDs
device IDs
moderation history
29. Hard Rule
Directory discovery does not disclose membership.
30. Directory Scope
#![allow(unused)] fn main() { pub enum DirectoryScope { Local, Community, Tenant, FederationDomain, PublicNetwork, } }
31. No One Global Directory Required
Hard rule.
32. Public Network Directory
Optional.
33. Federation Directory
Domain-scoped.
34. Tenant Directory
Organization-scoped.
35. Local Directory
Device/local curated list.
36. Directory Authority
Signs directory projection.
37. Does Not Own Membership Authority
Hard rule.
38. Membership Authority
Social-space-specific.
39. Membership State
#![allow(unused)] fn main() { pub enum MembershipState { NotMember, Invited, Requested, Active, Muted, Suspended, Left, Removed, Banned, } }
40. Invited
Capability offered.
41. Requested
Join request pending.
42. Active
Member.
43. Muted
Local notification state, not membership authority.
44. Suspended
Temporary participation restriction.
45. Left
Voluntary.
46. Removed
Moderator/admin removal.
47. Banned
Membership capability denied.
48. Hard Rule
Muted is never treated as suspension/removal.
49. Membership ID
#![allow(unused)] fn main() { pub struct MembershipId(pub [u8; 32]); }
50. Relationship To User Identity
Membership may be pseudonymous.
51. Membership Identity Mode
#![allow(unused)] fn main() { pub enum MembershipIdentityMode { AccountLinked, ScopedPseudonym, AnonymousCapability, ManagedIdentity, } }
52. AccountLinked
Explicitly identifiable.
53. ScopedPseudonym
Stable only within social space.
54. AnonymousCapability
No durable public identifier where protocol allows.
55. ManagedIdentity
Org-scoped identity.
56. Hard Rule
Public social spaces do not require global AccountId exposure.
57. Scoped Membership Pseudonym
#![allow(unused)] fn main() { pub struct MembershipPseudonym(pub [u8; 32]); }
58. Domain Separation
Derived/scoped separately per social space.
59. No Cross-Group Correlation
Hard rule.
60. Membership Discovery
Different from space discovery.
61. Default
Member list private/restricted.
62. Membership Visibility
#![allow(unused)] fn main() { pub enum MembershipVisibility { Hidden, MembersOnly, ModeratorsOnly, PublicOptIn, } }
63. Hidden
No member list exposed.
64. MembersOnly
Only authenticated members.
65. ModeratorsOnly
Administrative use.
66. PublicOptIn
Only members who opted in.
67. Hard Rule
Public group does not imply public membership list.
68. Join Policy
#![allow(unused)] fn main() { pub enum JoinPolicy { InvitationOnly, ApprovalRequired, OpenCapability, ManagedMembership, } }
69. InvitationOnly
Scoped invite token.
70. ApprovalRequired
Join request + moderation approval.
71. OpenCapability
Anyone with directory capability can join.
72. ManagedMembership
Org directory controls.
73. Join Capability
#![allow(unused)] fn main() { pub struct JoinCapability { pub space: SocialSpaceId, pub allowed_role: InitialRole, pub expires_at: Timestamp, pub use_policy: JoinUsePolicy, } }
74. Join Capability Must Not Grant
moderator authority
admin authority
cross-space access
unless explicitly encoded.
75. Hard Rule
Join capability is not generic social-space authority.
76. Join Use Policy
#![allow(unused)] fn main() { pub enum JoinUsePolicy { SingleUse, MultiUseBounded(u32), OpenUntilExpiry, } }
77. No Unlimited Permanent Join Link By Default
Hard rule.
78. Join Request
#![allow(unused)] fn main() { pub struct JoinRequest { pub request_id: JoinRequestId, pub space: SocialSpaceId, pub proposed_identity: MembershipIdentityProposal, pub proof: JoinProof, } }
79. Join Request Data Minimization
Only required metadata.
80. No Account Email/Phone By Default
Hard rule.
81. Join Flow
discover/invite
→ validate capability
→ request/approve if required
→ create membership identity
→ distribute group keys/capabilities
→ activate membership
82. Membership Activation
Atomic local authority update + durable event.
83. No Message Decryption Before Membership Activation
Hard rule.
84. Leave Flow
request leave
→ stop new send authority
→ update membership epoch
→ rotate/rekey if required
→ stop future state delivery
85. Leave Is Not Historical Erasure
Hard truth.
86. Removed/Banned Flow
Moderator authority required.
87. Rekey
Depending group E2EE model.
88. Group Epoch
#![allow(unused)] fn main() { pub struct GroupEpoch(pub u64); }
89. Membership changes may advance epoch.
90. Old member
Cannot derive future keys.
91. Hard Rule
Removed/banned member cannot continue future group decryption through stale group state.
92. Role Model
#![allow(unused)] fn main() { pub enum SocialRole { Member, Publisher, Moderator, Administrator, Owner, } }
93. Role ≠ Authority
Compile to scoped capabilities.
94. Hard Rule
Domain code checks capabilities/actions, not role name alone.
95. Role Scope
#![allow(unused)] fn main() { pub enum RoleScope { Space(SocialSpaceId), Channel(ChannelId), Community(CommunityId), } }
96. No Global Moderator Role
Hard rule.
97. Moderator Authority
Scoped.
98. Moderator Actions
#![allow(unused)] fn main() { pub enum ModerationAction { RemoveMessage, RestrictMember, RemoveMember, BanMember, PinMessage, LockChannel, ApproveJoin, } }
99. Admin Actions
change policy
assign roles
rotate ownership
delete space
100. Owner
High-risk role.
101. No One Moderator Automatically Gets Owner/Admin Actions
Hard rule.
102. Moderation Boundary
Moderation only applies inside the social space.
103. Hard Rule
Group moderator cannot affect personal account, contacts, unrelated groups, or transport identity.
104. Managed Workspace
Org admin may control managed spaces.
105. Personal spaces remain separate.
106. Hard rule.
107. Moderation Capability
#![allow(unused)] fn main() { pub struct ModerationCapability { pub moderator: MembershipPseudonym, pub scope: RoleScope, pub actions: BTreeSet<ModerationAction>, pub expires_at: Option<Timestamp>, } }
108. Delegation
Attenuation only.
109. No role escalation through delegation.
110. Hard rule.
111. Moderation Evidence
Scoped.
112. Can include:
message reference
rule ID
reason class
113. Should not include unrelated conversation history.
114. Hard rule.
115. Moderation Audit
Local/social-space scoped.
116. No global user moderation dossier by default.
117. Hard rule.
118. Public Moderation Transparency
Optional.
119. Can publish:
policy
aggregate actions
appeal process
120. Not full user identity history.
121. Channel Types
#![allow(unused)] fn main() { pub enum ChannelType { Discussion, Announcement, Media, Support, ReadOnly, } }
122. Discussion
Many senders.
123. Announcement
Limited publishers.
124. Media
Attachment-heavy.
125. Support
Threaded support/help.
126. ReadOnly
Archive/reference.
127. Channel Membership
Can inherit from community or be independent.
128. Inheritance
Explicit.
129. No Accidental Cross-Channel Access
Hard rule.
130. Channel Capability
#![allow(unused)] fn main() { pub struct ChannelCapabilities { pub read: CapabilityState, pub publish: CapabilityState, pub upload: CapabilityState, pub moderate: CapabilityState, } }
131. Announcement Channel
Publisher capability restricted.
132. Subscribers need not reveal identity publicly.
133. Hard rule.
134. Subscription State
#![allow(unused)] fn main() { pub enum SubscriptionState { Subscribed, Muted, Unsubscribed, } }
135. Subscription ≠ Membership
Hard rule.
136. Public Channel Subscription
Can be pseudonymous/private.
137. Directory Discovery
Public listing metadata.
138. Search Categories
Optional.
139. No behavioral ranking by default.
140. Hard rule.
141. Search Model
#![allow(unused)] fn main() { pub struct DirectorySearchQuery { pub text: Option<DirectoryQueryText>, pub category: Option<DirectoryCategory>, pub scope: DirectoryScope, } }
142. Search Privacy
No account identity required for public directory search.
143. Search Logs
Minimized.
144. No long-lived query profile.
145. Hard rule.
146. Exact Handle Search
Part 59.
147. Listed Directory Search
Can support text/category.
148. Public Directory
Must not expose member list.
149. Search Result
#![allow(unused)] fn main() { pub struct DirectorySearchResult { pub entry: DirectoryEntryId, pub title: DirectoryTitle, pub summary: Option<DirectorySummary>, pub join_mode: JoinPolicy, } }
150. No member identities.
151. Search Ranking
Use:
text relevance
category
coarse popularity bucket
if enabled.
152. Popularity
Privacy-sensitive.
153. Prefer coarse bucket.
154. No exact member count in strict privacy mode.
155. Hard rule.
156. Popularity Bucket
#![allow(unused)] fn main() { pub enum PopularityClass { Small, Medium, Large, VeryLarge, } }
157. No per-user personalization required.
158. No "people you know joined this group."
159. Hard rule.
160. Community Structure
Community can contain channels/groups.
161. Community Graph
#![allow(unused)] fn main() { pub struct CommunityStructure { pub community: CommunityId, pub children: Vec<SocialSpaceRef>, } }
162. Bounded.
163. No arbitrary recursive cycles.
164. Hard rule.
165. Hierarchy Depth
Limited.
166. Why
Policy/UX/performance.
167. Community Role Inheritance
Explicit.
168. Example
CommunityAdmin may manage channels.
169. But
No implicit authority outside community.
170. Hard rule.
171. Large Group Architecture
Needs scalable fanout.
172. Avoid O(n) sender upload where possible.
173. Possible models:
tree fanout
provider pub/sub
encrypted distribution service
174. Still E2EE where required.
175. Server sees minimal metadata.
176. Large Group Sender Keys
Part 43.
177. Membership epochs.
178. Rekey strategy.
179. No Central Plaintext Group Message Store
Hard rule.
180. Channel Message Storage
Ciphertext.
181. Public announcement content
May be public plaintext if intentionally public.
182. Distinguish public content from private membership metadata.
183. Hard rule.
184. Group Membership Privacy
Provider should not need full canonical roster where hidden-roster mode is used.
185. Membership modes:
#![allow(unused)] fn main() { pub enum RosterMode { FullRoster, MembersOnlyRoster, HiddenRoster, ModeratorOnlyRoster, } }
186. HiddenRoster
Recommended for sensitive groups.
187. Public group
May still use MembersOnlyRoster.
188. No assumption public=public roster.
189. Hard rule.
190. Hidden Roster Tradeoff
Moderation/distribution more complex.
191. Membership Proof
Capability/proof.
192. Server can validate membership without exposing full roster where design supports.
193. No custom anonymous-credential crypto initially.
194. Hard rule.
195. Use reviewed primitives.
196. Join Approval
Moderators can approve request.
197. Request metadata minimized.
198. No applicant contact graph.
199. Ban Model
Scoped.
200. Ban ID
#![allow(unused)] fn main() { pub struct BanRecord { pub space: SocialSpaceId, pub subject: BanSubjectRef, pub reason: BanReasonClass, pub expires_at: Option<Timestamp>, } }
201. Ban Subject
Space-scoped identity/capability.
202. Not global AccountId if avoidable.
203. Hard rule.
204. Temporary Ban
Supported.
205. Permanent Ban
Space-scoped.
206. No Automatic Cross-Group Ban Propagation
Hard rule.
207. Abuse Reporting
Part 46.
208. Reports scoped to social space.
209. Moderator sees only evidence necessary.
210. Platform abuse response
Separate.
211. No universal moderator backdoor.
212. Hard rule.
213. Appeals
Optional but recommended for managed/public spaces.
214. Appeal Scope
Only moderation action.
215. No account-wide authority.
216. Social Space Policy
#![allow(unused)] fn main() { pub struct SocialSpacePolicy { pub visibility: SocialSpaceVisibility, pub join_policy: JoinPolicy, pub roster_mode: RosterMode, pub moderation_policy: ModerationPolicy, pub retention: RetentionPolicyId, } }
217. Versioned.
218. Signed where authoritative.
219. Anti-Rollback.
220. No Silent Policy Weakening
Hard rule.
221. Public/Private Transition
High-risk.
222. Private → Public
Requires explicit admin/owner action.
223. Existing members should be informed if membership exposure changes.
224. Hard rule.
225. Public → Private
Allowed.
226. Directory entry removed.
227. Cached public metadata expires.
228. No immediate guarantee all external caches disappear.
229. Hard truth.
230. Space Ownership
Owner capability.
231. Ownership Transfer
Explicit two-party or multi-step.
232. No implicit owner change.
233. Hard rule.
234. Ownership Transfer Flow
propose new owner
→ authenticate/authorize
→ accept
→ rotate admin capability
→ revoke old owner capability if transferring fully
235. No Account Password Transfer.
236. Ownership Capability
Scoped to social space.
237. Community Deletion
Long-running lifecycle.
238. Similar to Part 83 deletion saga.
239. Stop new joins.
240. Revoke roles.
241. Delete/expire directory entry.
242. Process content/retention.
243. Hard Rule
Deleted social space ID not reused.
244. Space Deactivation
Temporary.
245. No new messages/joins.
246. Can later reactivate.
247. Deactivation ≠ deletion.
248. Group Archive
Read-only local/server state.
249. Does not revoke membership automatically.
250. Hard rule.
251. Federation
Cross-domain communities.
252. Federation Gateway
Explicit.
253. No shared global DB.
254. Remote membership represented through federation-scoped pseudonym/capability.
255. No global user identity merge.
256. Hard rule.
257. Federation Join
Bilateral policy.
258. Remote moderation
Limited to federated space scope.
259. No remote domain admin authority.
260. Hard rule.
261. Federation Directory
Signed projection.
262. Remote domain may choose listed/public metadata.
263. No remote member list by default.
264. Cross-Domain Ban
Only federated space.
265. Not global.
266. Tenant/Enterprise Spaces
Part 69.
267. Org membership can auto-provision managed-space membership.
268. But
Only managed space.
269. No personal contact creation.
270. Hard rule.
271. Offboarding
Revokes managed-space membership.
272. Personal communities unaffected.
273. Managed directory
Can list org spaces.
274. User may join external/public communities separately.
275. Policy can restrict managed profile only.
276. No org visibility into personal community memberships by default.
277. Hard rule.
278. Group Discovery From Contacts
Optional local suggestion.
279. No server mutual-membership mining.
280. Hard rule.
281. Invitation Sharing
Member may invite if capability.
282. Invite privilege separate.
283. Member Role Does Not Automatically Include Invite
Hard rule.
284. Invite Capability
#![allow(unused)] fn main() { pub struct SpaceInviteCapability { pub space: SocialSpaceId, pub inviter_scope: MembershipPseudonym, pub max_uses: u32, pub expires_at: Timestamp, } }
285. Revocable.
286. Invitation Attribution
Optional.
287. Avoid public invite graph.
288. No "who invited whom" global analytics.
289. Hard rule.
290. Join Spam
Bound request queue.
291. Join Request Rate Limits
Per invitation/source capability.
292. CAPTCHA optional but privacy-costly.
293. Prefer proof/credits/approval.
294. No real-world identity requirement.
295. Social Space Search Abuse
Prevent crawling.
296. Rate limit.
297. Pagination bounds.
298. No complete downloadable public directory snapshot if threat model rejects enumeration.
299. Hard rule.
300. Public Directory Snapshot
If intentionally public, can be signed/replicated.
301. But
No member data.
302. Directory Caching
Allowed.
303. Metadata TTL.
304. Remove hidden/deleted entries on refresh.
305. No eternal cache guarantee.
306. Subscription Privacy
Announcement channels.
307. Server should avoid exposing subscriber list.
308. Publisher need not know subscribers individually.
309. Hard rule.
310. Subscriber Delivery
Can use mailbox/pub-sub distribution.
311. No per-subscriber read tracking by default.
312. Analytics
Aggregate only if enabled.
313. No subscriber identity export.
314. Hard rule.
315. Read Receipts
Off by default for large/public channels.
316. Why
Massive privacy/metadata leakage.
317. Per-message view counts
Optional aggregate.
318. Must not reveal individual viewers.
319. Hard rule.
320. Presence In Large Groups
Avoid full presence roster.
321. Use coarse aggregate or none.
322. Typing indicators
Small groups only.
323. No typing broadcast to thousands.
324. Hard rule.
325. Member Count
Coarse.
326. Exact count may be hidden.
327. Public spaces can show bucket.
328. Large Group Privacy
No precise online/member count if not needed.
329. Content Moderation Architecture
Moderator action on content references.
330. E2EE Challenge
Server cannot inspect private group content.
331. Private group moderation
Client/member reports + moderator decryption context.
332. No universal server-side plaintext moderation key.
333. Hard rule.
334. Public channels
Content may intentionally be public.
335. Server-side moderation can inspect public content if policy says so.
336. Keep identity metadata minimized.
337. Moderator Key
Separate capability, not decryption escrow.
338. No "moderator can decrypt all historical private groups" key.
339. Hard rule.
340. Content Retention
Per social-space policy.
341. Ephemeral groups
Possible.
342. Public archives
Possible.
343. Private groups
Local/device retention.
344. No global retention default.
345. Retention Class
#![allow(unused)] fn main() { pub enum SocialRetentionClass { Ephemeral, Standard, Archive, Managed, } }
346. Deletion
Part 67.
347. Removed content
Tombstone/reference if needed.
348. Do not retain plaintext moderation copies indefinitely.
349. Hard rule.
350. Search
Local search for private groups.
351. Server index for public content only if explicitly public.
352. No server private-content index.
353. Hard rule.
354. Channel Threads
Optional.
355. Thread ID
Channel-scoped.
356. No cross-space thread ID.
357. Pinning
Moderation capability.
358. Slow Mode
Channel policy.
359. Rate Limit
Can be local/server enforced.
360. No user behavior profiling needed.
361. Reaction Aggregation
Optional.
362. Private group
E2EE event.
363. Public channel
Aggregate.
364. No public list of reactors unless explicitly enabled.
365. Hard rule.
366. Community Recommendations
Default disabled.
367. If added
Use public metadata + local interest selection.
368. No centralized personal-interest model.
369. Hard rule.
370. Trending
Can be aggregate public-space metric.
371. Use coarse counts.
372. Do not combine with user identity.
373. No targeted recommendation required.
374. Social Space Repository
#![allow(unused)] fn main() { pub trait SocialSpaceRepository { fn get( &self, id: SocialSpaceId, ) -> Result<Option<SocialSpaceRecord>, SocialSpaceError>; fn update_policy( &self, id: SocialSpaceId, policy: SocialSpacePolicy, ) -> Result<(), SocialSpaceError>; } }
375. Membership Repository
#![allow(unused)] fn main() { pub trait MembershipRepository { fn membership( &self, space: SocialSpaceId, subject: MembershipSubjectRef, ) -> Result<MembershipState, SocialSpaceError>; } }
376. Directory Service
#![allow(unused)] fn main() { pub trait SocialDirectoryService { fn search( &self, query: DirectorySearchQuery, ) -> Result<Vec<DirectorySearchResult>, SocialSpaceError>; } }
377. Join Service
#![allow(unused)] fn main() { pub trait SocialJoinService { fn join( &self, request: JoinRequest, ) -> Result<MembershipId, SocialSpaceError>; } }
378. Moderation Service
#![allow(unused)] fn main() { pub trait ModerationService { fn apply( &self, actor: ModerationCapability, action: ModerationCommand, ) -> Result<ModerationResult, SocialSpaceError>; } }
379. No direct DB mutation.
380. Hard rule.
381. Authorization Integration
Part 81.
382. Roles compile to capabilities.
383. Membership state is ABAC input.
384. Moderator action checked at PEP.
385. No Role Name Shortcut.
386. Authentication Integration
Part 82.
387. Public/pseudonymous group participation may not require global account exposure.
388. Managed spaces require org auth.
389. Account Lifecycle
Part 83.
390. Deleted account loses memberships.
391. Suspended account policy-specific.
392. Managed offboarding removes managed memberships.
393. No stale membership after account deletion.
394. Hard rule.
395. Profile Integration
Part 84.
396. Group profile projection separate.
397. Member nickname/avatar can be space-scoped.
398. No automatic personal profile disclosure.
399. Hard rule.
400. Contact Integration
Part 85.
401. Group membership does not create contact edge.
402. Direct contact invite separate.
403. No mutual membership leakage.
404. Messaging Integration
Part 43.
405. Group encryption/membership epoch.
406. Channel message envelope.
407. No directory service in message path.
408. Hard rule.
409. Event Bus Integration
Part 75.
410. Membership/moderation events scoped.
411. No global social event stream.
412. Hard rule.
413. Directory Discovery Integration
Part 77.
414. Public/tenant directory service can use signed service records.
415. Group identity trust separate from endpoint identity.
416. Edge Integration
Part 78.
417. Directory/join APIs L7.
418. Group ciphertext paths opaque.
419. No WAF decryption of private group content.
420. Hard rule.
421. East-West Integration
Part 79.
422. Membership service, directory, moderation service use service identity/mTLS.
423. Secrets Integration
Part 80.
424. Group signing/admin keys scoped.
425. No shared moderator secret.
426. Federation Integration
Part 58.
427. Cross-domain social space state uses explicit federation protocol.
428. No database-level cross-domain join.
429. Hard rule.
430. Storage
Directory metadata:
public/signed projection
Membership:
encrypted/scoped authority state
Messages:
ciphertext/public content by mode
431. No single giant table.
432. Hard rule.
433. Membership Privacy At Rest
Encrypt sensitive roster.
434. Tenant-specific keys.
435. No global member index.
436. Hard rule.
437. Search Index
Public/listed directory only.
438. Private groups not indexed.
439. Hidden groups absent from search.
440. Exact-handle groups only exact resolution.
441. No accidental indexing.
442. Hard rule.
443. Observability
Safe aggregate metrics:
directory query success
join success
moderation action count
fanout lag
444. Forbidden:
user→group membership graph
member identity list
viewer list
moderator dossier
445. Hard rule.
446. Per-Space Metrics
For owner/moderator, aggregate.
447. Examples:
message volume
active member bucket
join request count
448. No individual participation analytics by default.
449. Hard rule.
450. Audit
High-risk moderation/admin actions.
451. Minimal.
452. No message content unless explicitly part of moderation evidence and permitted.
453. Retention bounded.
454. Moderation audit record:
#![allow(unused)] fn main() { pub struct ModerationAuditRecord { pub space: SocialSpaceId, pub action: ModerationAction, pub actor_scope: MembershipPseudonym, pub outcome: AuditOutcome, } }
455. No global account identifier where avoidable.
456. Privacy Threat Model
Attacks:
directory crawling
membership enumeration
cross-group identity correlation
join-token leakage
moderator overreach
subscriber tracking
457. Directory Crawling
Rate limit/pagination.
458. Membership Enumeration
No public roster by default.
459. Cross-Group Correlation
Scoped pseudonyms.
460. Join Token Leakage
Expiry/revocation/scope.
461. Moderator Overreach
Capability-scoped enforcement.
462. Subscriber Tracking
No subscriber identity export.
463. No Stable Analytics Identifier Across Spaces
Hard rule.
464. Abuse Threat Model
spam groups
mass invites
join-request flood
malicious moderators
raids
465. Spam Groups
Directory quality/rate limiting.
466. Mass Invites
Invite capability quotas.
467. Join Flood
Queue/admission controls.
468. Malicious Moderator
Scoped audit/appeal.
469. Raids
Join approval/slow mode/capabilities.
470. No Central Real-World Identity Verification Requirement.
471. Hard rule.
472. Security Events
Examples:
ownership transfer
moderator grant
membership key rotation
policy publicization
473. Notify affected admins/members as appropriate.
474. No unnecessary broadcast of member identity.
475. Group Key Rotation
Part 43/66.
476. Membership change triggers as policy requires.
477. Large group may use sender-key generations.
478. Key compromise
rotate.
479. Removed member
future secrecy.
480. New member
past history exposure policy explicit.
481. History Sharing Policy
#![allow(unused)] fn main() { pub enum HistoryAccessPolicy { FromJoinOnly, RecentWindow, FullHistory, None, } }
482. Default Private Group
FromJoinOnly.
483. Public channel
FullHistory possible.
484. Hard Rule
New membership never silently gains historical private content unless policy explicitly says so.
485. Message History Export
Member scope.
486. Public content export unrestricted per policy.
487. Private group export may require capability.
488. No moderator automatic export-all authority.
489. Hard rule.
490. Backup
Part 33.
491. Own membership state backed up logically.
492. Live group ratchets/session keys not blindly restored.
493. New device re-provisioned.
494. Removed/banned state must survive restore.
495. Hard rule.
496. Deletion Anti-Resurrection
Space deletion epoch.
497. Membership revocation epoch.
498. Stale backup cannot re-list/rejoin deleted space automatically.
499. Hard rule.
500. Social Space Deletion Epoch
#![allow(unused)] fn main() { pub struct SocialSpaceDeletionEpoch(pub u64); }
501. Directory Publication Epoch
Monotonic.
502. Old public record rejected.
503. No stale directory resurrection.
504. Hard rule.
505. Schema Versioning
Separate versions for:
directory record
membership protocol
moderation protocol
message protocol
storage
506. No conflation.
507. Mixed Version
Rolling compatible.
508. Unknown moderation action
deny.
509. Unknown membership state
restrictive.
510. Hard rule.
511. Serialization
Postcard internal/protocol.
512. RON policy/config.
513. JSON external directory/API interop.
514. Strict bounds.
515. Directory text
Bounded.
516. Community children
Bounded.
517. Member list response
Bounded/paged.
518. No unbounded roster payload.
519. Hard rule.
520. Performance
Directory search
indexed.
521. Membership check
local/cache.
522. No central directory call per message.
523. Hard rule.
524. Large Group Fanout
Asynchronous.
525. Bounded queues.
526. Backpressure.
527. Bulk/attachment priority lower.
528. Moderation/security events prioritized.
529. No one space exhausts global fanout capacity.
530. Tenant/social-space quotas.
531. Fair scheduling.
532. Channel Partitioning
By social-space/channel ID.
533. No user-based partition key.
534. Hard rule.
535. Large Community Scaling
Shard independent channels.
536. Community metadata small.
537. Avoid giant cross-channel transaction.
538. Use saga/events.
539. No distributed 2PC.
540. Hard rule.
541. Crate Layout
Recommended:
crates/
├── siar-social-space-core/
├── siar-community/
├── siar-channel/
├── siar-social-directory/
├── siar-membership/
├── siar-membership-privacy/
├── siar-social-invitation/
├── siar-social-moderation/
├── siar-social-federation/
├── siar-social-observability/
└── siar-social-testkit/
542. siar-social-space-core
Owns:
space IDs
visibility
errors
543. siar-community
Community hierarchy/policy.
544. siar-channel
Channel types/capabilities/subscriptions.
545. siar-social-directory
Directory projections/search.
546. siar-membership
Join/leave/remove/ban state.
547. siar-membership-privacy
Scoped pseudonyms/roster visibility.
548. siar-social-invitation
Join capabilities/invite lifecycle.
549. siar-social-moderation
Scoped moderator capabilities/actions/audits.
550. siar-social-federation
Cross-domain spaces.
551. siar-social-observability
Aggregate metrics only.
552. siar-social-testkit
Directory/join/moderation/privacy/federation tests.
553. Error Taxonomy
#![allow(unused)] fn main() { pub enum SocialSpaceError { SpaceNotFound, DirectoryEntryNotFound, NotMember, MembershipSuspended, MembershipRemoved, MembershipBanned, JoinDenied, InviteInvalid, InviteExpired, CapabilityDenied, ModerationDenied, RosterHidden, FederationDenied, PolicyViolation, SpaceDeleted, Internal, } }
554. Testing
Need dedicated social-space testkit.
555. Test Scenarios
private group
public channel
hidden roster
join approval
ban
ownership transfer
556. Directory Test
Private space never appears.
557. Exact Handle Test
Only exact lookup resolves.
558. Public Directory Test
No member list leakage.
559. Membership Test
Active required before private content.
560. Hidden Roster Test
Non-authorized caller cannot enumerate members.
561. Scoped Pseudonym Test
Same user gets unrelated pseudonyms across spaces.
562. Join Token Replay Test
Single-use invite cannot create duplicate memberships.
563. Remove/Ban Test
Future key access denied.
564. Group History Test
New member receives only allowed history.
565. Moderator Scope Test
Moderator cannot act outside space/channel.
566. Role Escalation Test
Moderator cannot self-promote to owner/admin.
567. Managed Space Test
Org admin controls managed space only.
568. Personal Space Test
Org admin cannot moderate personal group.
569. Federation Test
Remote domain cannot gain internal admin authority.
570. Subscription Test
Publisher cannot enumerate subscribers.
571. Analytics Test
No user→space membership graph emitted.
572. Deleted Space Test
Stale directory cache cannot resurrect listing.
573. Backup Test
Banned membership does not reactivate after restore.
574. Publicization Test
Private→public requires explicit authorized action.
575. Fuzzing
Fuzz:
directory record
join capability
membership envelope
moderation command
federation social envelope
576. Property Tests
Properties:
private space never becomes listed without explicit authorized transition
banned/removed membership cannot decrypt future group epochs
moderator authority is subset of social-space scope
public directory output never contains private roster state
577. Formal Verification Targets
Strong candidates:
join/leave/remove/ban state machine
role/capability lattice
roster visibility
private→public transition
578. Kani Candidate
role/capability/scope subset checks.
579. TLA+ Candidate
membership epoch + remove/ban + concurrent delivery.
580. Loom Candidate
concurrent join/leave/moderation/ownership update.
581. Security, Privacy & Correctness Invariants
Mandatory:
1. A public or listed social space never implies a public member roster.
2. Membership identities are scoped to the social space where possible and are not globally correlatable account identifiers.
3. Group/community/channel membership never automatically creates pairwise personal contacts.
4. Directory records contain discovery metadata only and never become membership or moderation authority.
5. Join capabilities are scoped, expiring, replay-resistant, and cannot grant unrelated administrative authority.
6. Moderation roles compile to scoped capabilities; moderators cannot affect accounts, contacts, groups, or tenants outside their explicit scope.
7. Removed or banned members cannot derive future private-group message keys after the relevant membership/key epoch advances.
8. Private social-space content remains E2EE and moderation never relies on a universal server-side plaintext decryption key.
9. Enterprise/tenant administrators control only managed social spaces and cannot inspect or govern unrelated personal communities by default.
10. Social-space telemetry, search, moderation logs, and analytics never create a remotely queryable user-to-group membership graph.
11. Federation social spaces preserve independent domain identity/authority and do not create a global member namespace or transitive moderator power.
12. Deleted/private/revoked space state cannot be resurrected through stale directory caches, backups, offline clients, or old capabilities.
582. Initial Production Scope
Implement first:
typed SocialSpaceId/CommunityId/ChannelId
private/invite/exact/listed/public visibility
signed directory projections
group/community/channel models
join capability + request/approval flow
scoped MembershipId/pseudonym
membership state machine
full/members/hidden/moderator-only roster modes
role-to-capability moderation
ban/remove/suspend flows
membership epochs and group-key integration
announcement channel subscriptions
public/private history policy
tenant/managed space isolation
basic federation gateway integration
privacy-safe metrics/audit
social-space testkit
Then add:
advanced hidden-roster protocols
reviewed anonymous-membership credentials
large-scale encrypted pub/sub fanout
federated community directory
privacy-preserving popularity/trending
formal membership/moderation verification
583. Definition of Done
Part 86 is complete when:
- group/community/channel identities are explicit and opaque
- public/private/listed/invite-only discovery states are separate
- directory metadata is not membership authority
- member rosters can remain hidden
- scoped pseudonyms prevent cross-space correlation
- join/leave/remove/ban state machines are durable
- roles compile to scoped moderator capabilities
- private content stays E2EE without universal moderation escrow
- large/public channels can support private subscriptions
- group membership never creates personal contact edges
- enterprise/federation scopes stay isolated
- directory/search/analytics never expose global membership graphs
- stale cache/backup/capability cannot resurrect deleted/revoked state
- join/moderation/roster/federation/fuzz/formal tests are specified
584. Final Architecture
DIRECTORY / INVITE / HANDLE
│
▼
SOCIAL SPACE
┌───────────────┼───────────────┐
│ │ │
GROUP COMMUNITY CHANNEL
│ │ │
└───────────────┼───────────────┘
▼
MEMBERSHIP STATE
│
┌──────────┼──────────┐
│ │ │
ROLES ROSTER KEY EPOCH
│ │ │
└──────────┼──────────┘
▼
MODERATION BOUNDARY
Social-space safety model:
scoped membership identity
+
separate directory projection
+
capability-based joins
+
hidden roster options
+
scoped moderation authority
+
membership epochs
+
E2EE private content
+
no global social graph
not:
put every group, member, moderator, subscriber, and participation event into one centralized people graph
585. Final Principle
A social space should expose only the information necessary for people to discover and participate in that space, not the identities and relationships of everyone inside it.
The correct model is:
discover metadata
+
join through scoped capability
+
identify members locally/scoped
+
moderate within boundaries
+
hide membership by default
+
federate explicitly
+
never centralize participation graphs
This architecture gives SIAR a privacy-preserving foundation for groups, communities, channels, announcement systems, membership discovery, moderation, public directories, tenant spaces, and federated social spaces while preserving the anonymity, local-first, least-authority, and social-graph privacy guarantees established across Parts 34–85.