/ docs / archive / CIRCLE_TRUST_POLICIES_old.md
CIRCLE_TRUST_POLICIES_old.md
  1  # Circle Trust Policies
  2  
  3  > **Abzu Technical Specification** — January 2026  
  4  > *Configurable privacy-accountability trade-offs for group membership.*
  5  
  6  ---
  7  
  8  ## Executive Summary
  9  
 10  Circle Trust Policies provide **configurable security postures** for group membership, enabling circles to operate anywhere on the spectrum from full accountability to complete anonymity.
 11  
 12  This system addresses a fundamental tension in group messaging:
 13  
 14  | Requirement | Tension |
 15  |-------------|---------|
 16  | **Accountability** | Communities need moderation tools and audit trails |
 17  | **Privacy** | Users need protection from surveillance and social mapping |
 18  | **Safety** | Activists need protection even if devices are seized |
 19  
 20  The solution: **Three policy tiers** that circles can choose from at creation time.
 21  
 22  ---
 23  
 24  ## The Problem
 25  
 26  ### OpSec Hazard: Permanent Social Graphs
 27  
 28  The original Circle Trust Protocol (invite trees, vouch ledgers) creates a **permanent, traceable social graph**. This is valuable for moderation but dangerous in adversarial contexts.
 29  
 30  **Scenario**: An activist group uses Circles for coordination. An adversary seizes one member's device.
 31  
 32  With full trust ledger:
 33  
 34  - ❌ Adversary traces entire invitation chain
 35  - ❌ Adversary identifies the founder
 36  - ❌ Adversary maps vouch relationships (who trusts whom)
 37  - ❌ Every member is identified and their social position mapped
 38  
 39  With anonymous policy:
 40  
 41  - ✅ Adversary sees only a list of public keys
 42  - ✅ No invitation chain exists
 43  - ✅ No vouch history exists
 44  - ✅ Social graph is invisible
 45  
 46  ### The Pruning Fairness Problem
 47  
 48  Cascading prune (remove target + all their invitees) can be unfair:
 49  
 50  - Member A invites Member B
 51  - Member B invites 50 people, builds thriving subcommunity
 52  - Member A is banned for unrelated misconduct
 53  - All 50 people lose access—"guilt by association"
 54  
 55  Different prune modes address this with varying degrees of forgiveness.
 56  
 57  ---
 58  
 59  ## Trust Policy Architecture
 60  
 61  ### The Three Tiers
 62  
 63  ```
 64  ┌────────────────────────────────────────────────────────────────────────┐
 65  │                          TRUST POLICY SPECTRUM                         │
 66  │                                                                        │
 67  │   ANONYMOUS          PRIVATE            ACCOUNTABLE                    │
 68  │   ┌────────┐        ┌────────┐         ┌────────────┐                 │
 69  │   │No logs │        │No logs │         │Full audit  │                 │
 70  │   │No tree │        │Has tree│         │Full tree   │                 │
 71  │   │No graph│        │Prunable│         │Traceable   │                 │
 72  │   └────────┘        └────────┘         └────────────┘                 │
 73  │       ▲                 ▲                   ▲                          │
 74  │       │                 │                   │                          │
 75  │   Activists         Friend groups      Moderated communities          │
 76  │   Whistleblowers    Private clubs      Public servers                 │
 77  │   High-risk users   Social circles     Organizations                  │
 78  └────────────────────────────────────────────────────────────────────────┘
 79  ```
 80  
 81  ### Policy Definitions
 82  
 83  ```rust
 84  /// Trust policy for a Circle - determines privacy/accountability trade-off
 85  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 86  pub enum TrustPolicy {
 87      /// Full accountability: complete audit trail and invitation tree
 88      /// Use case: Moderated communities, organizations, public servers
 89      Accountable {
 90          ledger_mode: LedgerMode,
 91          prune_mode: PruneMode,
 92      },
 93      
 94      /// Partial privacy: no ledger, but maintains invite tree for pruning
 95      /// Use case: Friend groups, private clubs, social circles
 96      Private {
 97          prune_mode: PruneMode,
 98      },
 99      
100      /// Maximum privacy: no ledger, no invite tree, minimal metadata
101      /// Use case: Activists, whistleblowers, high-risk users
102      /// DEFAULT - safest option for adversarial contexts
103      Anonymous,
104  }
105  
106  impl Default for TrustPolicy {
107      fn default() -> Self {
108          TrustPolicy::Anonymous  // Safety-first default
109      }
110  }
111  ```
112  
113  ---
114  
115  ## Ledger Modes
116  
117  The `LedgerMode` enum controls what gets recorded in the trust ledger (only applicable to Accountable policy):
118  
119  ```rust
120  #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
121  pub enum LedgerMode {
122      /// Complete audit trail: invites, vouches, prunes with full details
123      #[default]
124      Full,
125      
126      /// Records only join/leave events, omits social graph details
127      /// (who invited whom, who vouched for whom)
128      MembershipOnly,
129      
130      /// Rolling 30-day window, older entries automatically pruned
131      Ephemeral,
132  }
133  ```
134  
135  ### Ledger Mode Comparison
136  
137  | Mode | Records Invites | Records Vouches | Records Prunes | Retention |
138  |------|-----------------|-----------------|----------------|-----------|
139  | **Full** | ✅ With details | ✅ With details | ✅ With details | Permanent |
140  | **MembershipOnly** | ✅ Join only | ❌ | ✅ Leave only | Permanent |
141  | **Ephemeral** | ✅ With details | ✅ With details | ✅ With details | 30 days |
142  
143  ---
144  
145  ## Prune Modes
146  
147  The `PruneMode` enum controls what happens to downstream invitees when someone is pruned:
148  
149  ```rust
150  #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
151  pub enum PruneMode {
152      /// Traditional: Target + all descendants removed
153      /// "If I don't trust Alice anymore, I don't trust anyone she brought in"
154      Cascade,
155      
156      /// Merciful: Target removed, descendants become orphans (no inviter)
157      /// "Punish the individual, not their network"
158      #[default]
159      Orphan,
160      
161      /// Transfer: Descendants reassigned to founder
162      /// "The founder vouches for everyone by proxy"
163      Reassign,
164      
165      /// Voluntary only: Members can only remove themselves
166      /// "Nobody gets kicked out"
167      Voluntary,
168  }
169  ```
170  
171  ### Prune Mode Dynamics
172  
173  ```
174  BEFORE: Founder → Alice → Bob, Carol, Dave
175                         └→ Eve (invited by Carol)
176  
177  PRUNE ALICE WITH CASCADE:
178    Result: Alice, Bob, Carol, Dave, Eve ALL REMOVED
179    
180  PRUNE ALICE WITH ORPHAN:
181    Result: Alice removed
182            Bob, Carol, Dave → invited_by: None (orphaned)
183            Eve still points to Carol
184            
185  PRUNE ALICE WITH REASSIGN:
186    Result: Alice removed
187            Bob, Carol, Dave → invited_by: Founder
188            Eve still points to Carol
189  
190  PRUNE ALICE WITH VOLUNTARY:
191    Result: Error - only Alice can remove herself
192  ```
193  
194  ---
195  
196  ## Policy Behavior Matrix
197  
198  ### What Gets Recorded
199  
200  | Operation | Anonymous | Private | Accountable |
201  |-----------|-----------|---------|-------------|
202  | **Member joins** | pubkey only | pubkey + inviter | Full ledger entry |
203  | **Member vouched** | vouch stored | vouch stored | Ledger entry |
204  | **Member pruned** | member removed | member + descendants | Ledger entry |
205  | **Invite depth** | always 0 | tracked | tracked |
206  | **Invite signature** | none | stored | stored |
207  
208  ### What Gets Stored Per Member
209  
210  ```rust
211  // With Anonymous policy:
212  CircleMember {
213      pubkey: [u8; 32],
214      role: MemberRole,
215      joined_at: u64,
216      invited_by: None,           // OpSec: not recorded
217      invite_signature: None,      // OpSec: not recorded
218      invite_depth: 0,             // OpSec: not recorded
219      vouches_received: Vec::new(),
220      capacity: TrustCapacity::default(),
221  }
222  
223  // With Private/Accountable policy:
224  CircleMember {
225      pubkey: [u8; 32],
226      role: MemberRole,
227      joined_at: u64,
228      invited_by: Some(inviter_pubkey),  // Tracked for pruning
229      invite_signature: Some(sig),        // Proof of invitation
230      invite_depth: 3,                    // Distance from founder
231      vouches_received: vec![...],        // Secondary endorsements
232      capacity: TrustCapacity { ... },
233  }
234  ```
235  
236  ---
237  
238  ## API Usage
239  
240  ### Creating Circles with Explicit Policies
241  
242  ```rust
243  // Default: Anonymous (safest)
244  let activist_circle = node.create_circle("Resistance".to_string())?;
245  assert_eq!(activist_circle.trust_policy, TrustPolicy::Anonymous);
246  
247  // Private: For friend groups
248  let policy = TrustPolicy::Private {
249      prune_mode: PruneMode::Orphan,
250  };
251  let friend_circle = node.create_circle_with_policy(
252      "Book Club".to_string(), 
253      policy
254  )?;
255  
256  // Accountable: For moderated communities
257  let policy = TrustPolicy::Accountable {
258      ledger_mode: LedgerMode::Full,
259      prune_mode: PruneMode::Cascade,
260  };
261  let server = node.create_circle_with_policy(
262      "My Discord".to_string(),
263      policy
264  )?;
265  ```
266  
267  ### Checking Policy Capabilities
268  
269  ```rust
270  impl TrustPolicy {
271      /// Returns whether this policy maintains a trust ledger
272      pub fn has_ledger(&self) -> bool {
273          matches!(self, TrustPolicy::Accountable { ledger_mode, .. } 
274                   if *ledger_mode != LedgerMode::Ephemeral)
275      }
276      
277      /// Returns whether this policy records invite trees
278      pub fn records_invite_tree(&self) -> bool {
279          matches!(self, 
280              TrustPolicy::Accountable { .. } | 
281              TrustPolicy::Private { .. }
282          )
283      }
284      
285      /// Returns whether this policy records vouches
286      pub fn records_vouches(&self) -> bool {
287          matches!(self, 
288              TrustPolicy::Accountable { .. } | 
289              TrustPolicy::Private { .. }
290          )
291      }
292  }
293  ```
294  
295  ---
296  
297  ## Security Analysis
298  
299  ### Anonymous Policy: Maximum OpSec
300  
301  **Threat model**: Adversary with physical device access
302  
303  | Attack Vector | Defense |
304  |---------------|---------|
305  | Extract invitation chain | ✅ Not stored |
306  | Map social graph | ✅ Not stored |
307  | Identify founding members | ✅ Founder pubkey is member, but no special markers |
308  | Trace vouch relationships | ✅ Vouches recorded on member, but no ledger |
309  | Timeline reconstruction | ⚠️ `joined_at` timestamps remain |
310  
311  **Residual risk**: Membership list + timestamps. Future enhancement: timestamp fuzzing.
312  
313  ### Private Policy: Pruning Without Surveillance
314  
315  Maintains invite tree for functional pruning while avoiding permanent audit trail.
316  
317  | Attack Vector | Defense |
318  |---------------|---------|
319  | Extract invitation chain | ⚠️ Stored per-member, can be traced |
320  | Map vouch relationships | ⚠️ Stored per-member |
321  | Permanent historical record | ✅ No ledger, only current state |
322  
323  **Use case**: When you need moderation tools but don't want permanent records.
324  
325  ### Accountable Policy: Full Transparency
326  
327  Complete audit trail for organizations that require it.
328  
329  | Feature | Benefit |
330  |---------|---------|
331  | Invite history | Know who introduced problematic members |
332  | Vouch history | Understand reputation networks |
333  | Prune history | Audit moderation decisions |
334  | Immutable record | Legal/compliance requirements |
335  
336  ---
337  
338  ## Design Rationale
339  
340  ### Why Anonymous is Default
341  
342  > [!IMPORTANT]
343  > **Safety-first design**: The most dangerous failure mode is exposing vulnerable
344  > users to surveillance. The most inconvenient failure mode is lacking moderation
345  > tools. We chose safety over convenience.
346  
347  Communities that need accountability can explicitly opt-in. Users who don't think
348  about security get the safest option automatically.
349  
350  ### Why Three Tiers (Not Two, Not Five)
351  
352  The three tiers map to real-world social structures:
353  
354  | Tier | Real-World Analog | Example |
355  |------|-------------------|---------|
356  | **Anonymous** | Secret society | Underground resistance, journalism sources |
357  | **Private** | Private club | Book club, friend group, gaming guild |
358  | **Accountable** | Public organization | Company Slack, Discord server, forum |
359  
360  Fewer tiers = ambiguity about what you're getting.
361  More tiers = analysis paralysis, subtle differences users won't understand.
362  
363  ### Why Orphan is Default Prune Mode
364  
365  Cascade (traditional) punishes innocent people for someone else's misconduct.
366  Orphan preserves communities while removing bad actors.
367  
368  ---
369  
370  ## Implementation Notes
371  
372  ### Files Modified
373  
374  | File | Changes |
375  |------|---------|
376  | [node.rs](file:///Users/adrian/studio/abzu/abzu-core/src/node.rs) | `TrustPolicy`, `LedgerMode`, `PruneMode` enums; policy helper methods; `create_circle_with_policy()`; conditional ledger/tree storage |
377  | [lib.rs](file:///Users/adrian/studio/abzu/abzu-core/src/lib.rs) | Export new types |
378  | [circle_tests.rs](file:///Users/adrian/studio/abzu/abzu-core/tests/circle_tests.rs) | Policy-specific tests |
379  
380  ### Test Coverage
381  
382  12 trust-related tests including:
383  
384  - `test_anonymous_policy_no_ledger` — Verifies no ledger/tree for OpSec
385  - `test_private_policy_no_ledger_but_has_invite_tree` — Verifies no ledger but keeps tree
386  - Existing tests updated to use Accountable policy for ledger verification
387  
388  ---
389  
390  ## Future Work
391  
392  ### P1: Wire Protocol Frame
393  
394  ```rust
395  CircleCreate { 
396      id: [u8; 32], 
397      name: Vec<u8>,
398      trust_policy: TrustPolicy,  // NEW: Include in creation
399  }
400  ```
401  
402  ### P2: Policy Migration
403  
404  Allow circles to upgrade their policy (Anonymous → Private → Accountable) but
405  never downgrade (can't un-audit).
406  
407  ### P3: Timestamp Fuzzing
408  
409  For Anonymous circles, fuzz `joined_at` timestamps to make timeline correlation
410  harder.
411  
412  ### P4: SDK Integration
413  
414  ```dart
415  // Dart SDK
416  final circle = await node.createCircle(
417    name: 'Activists',
418    policy: TrustPolicy.anonymous,
419  );
420  ```
421  
422  ---
423  
424  ## Conclusion
425  
426  Circle Trust Policies transform Abzu's group messaging from a one-size-fits-all
427  solution into a flexible system that respects both organizational needs and
428  individual safety.
429  
430  The key insight: **Default to safety, opt-in to accountability.**
431  
432  ```
433       ┌─────────────────────────────────────────────┐
434       │                                             │
435       │   "You can always add transparency.         │
436       │    You can never take back surveillance."   │
437       │                                             │
438       └─────────────────────────────────────────────┘
439  ```
440  
441  ---
442  
443  *Document version: 1.0 • January 27, 2026*