Skip to main content

sovereign_profile/
lib.rs

1// Copyright (C) 2024-2026 Tristan Stoltz / Luminous Dynamics
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! # 8-Dimensional Sovereign Profile
5//!
6//! Anti-tyranny civic identity for the Mycelix governance system.
7//!
8//! When you reduce a human being to a single number — like a credit score or
9//! social credit rating — you create a system that is infinitely gamifiable
10//! and inherently oppressive. The 8D Sovereign Profile maps identity as a
11//! holographic, multi-faceted geometry rather than a flat number.
12//!
13//! ## The 8 Axes of Civic Identity
14//!
15//! 1. **Epistemic Integrity** — truth/claim verification history
16//! 2. **Thermodynamic Yield** — physical energy contribution to local commons
17//! 3. **Network Resilience** — node uptime and bandwidth provision
18//! 4. **Economic Velocity** — compliance with anti-hoarding demurrage
19//! 5. **Civic Participation** — sortition jury duty, voting, dispute resolution
20//! 6. **Stewardship & Care** — verified physical labor on the commons
21//! 7. **Semantic Resonance** — entanglement with community consensus
22//! 8. **Domain Competence** — peer-verified specialization
23//!
24//! Each dimension is directly measurable from a primary source (smart meter,
25//! node log, transaction ledger, jury record, telemetry shim). Multiple
26//! pathways to citizenship are respected — a reclusive solar engineer and a
27//! social mediator both pass the threshold through completely different
28//! dimensional profiles.
29//!
30//! ## Privacy
31//!
32//! The profile never leaves the local device. ZKP circuits prove the
33//! *aggregate* weight exceeds a threshold without revealing individual
34//! dimensions. The network knows you are a net-positive citizen but has
35//! no idea *how* you achieved that.
36
37pub mod collectors;
38pub mod compat;
39pub mod decay;
40#[cfg(feature = "hdc")]
41pub mod hdc;
42pub mod i18n;
43pub mod weights;
44
45#[cfg(test)]
46mod tests;
47
48// ---------------------------------------------------------------------------
49// Core types
50// ---------------------------------------------------------------------------
51
52/// The 8 axes of civic identity.
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55pub enum SovereignDimension {
56    /// D0: Truth/claim verification history.
57    EpistemicIntegrity,
58    /// D1: Physical energy contribution to local commons.
59    ThermodynamicYield,
60    /// D2: Node uptime and bandwidth provision.
61    NetworkResilience,
62    /// D3: Compliance with anti-hoarding demurrage.
63    EconomicVelocity,
64    /// D4: Sortition jury duty, voting, dispute resolution.
65    CivicParticipation,
66    /// D5: Verified physical labor on the commons.
67    StewardshipCare,
68    /// D6: Entanglement with community consensus.
69    SemanticResonance,
70    /// D7: Peer-verified specialization.
71    DomainCompetence,
72}
73
74impl SovereignDimension {
75    /// All 8 dimensions in canonical order.
76    pub const ALL: [Self; 8] = [
77        Self::EpistemicIntegrity,
78        Self::ThermodynamicYield,
79        Self::NetworkResilience,
80        Self::EconomicVelocity,
81        Self::CivicParticipation,
82        Self::StewardshipCare,
83        Self::SemanticResonance,
84        Self::DomainCompetence,
85    ];
86
87    /// Index (0-7) for array-based access.
88    pub fn index(&self) -> usize {
89        match self {
90            Self::EpistemicIntegrity => 0,
91            Self::ThermodynamicYield => 1,
92            Self::NetworkResilience => 2,
93            Self::EconomicVelocity => 3,
94            Self::CivicParticipation => 4,
95            Self::StewardshipCare => 5,
96            Self::SemanticResonance => 6,
97            Self::DomainCompetence => 7,
98        }
99    }
100
101    /// i18n key for this dimension.
102    pub fn key(&self) -> &'static str {
103        match self {
104            Self::EpistemicIntegrity => "epistemic_integrity",
105            Self::ThermodynamicYield => "thermodynamic_yield",
106            Self::NetworkResilience => "network_resilience",
107            Self::EconomicVelocity => "economic_velocity",
108            Self::CivicParticipation => "civic_participation",
109            Self::StewardshipCare => "stewardship_care",
110            Self::SemanticResonance => "semantic_resonance",
111            Self::DomainCompetence => "domain_competence",
112        }
113    }
114}
115
116// ---------------------------------------------------------------------------
117// SovereignProfile
118// ---------------------------------------------------------------------------
119
120/// 8-dimensional sovereign civic profile.
121///
122/// Each dimension is normalized to [0.0, 1.0]. Values outside this range
123/// are clamped during scoring. NaN and Inf are treated as 0.0.
124#[derive(Debug, Clone, PartialEq)]
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126pub struct SovereignProfile {
127    /// D0: Truth/claim verification history.
128    pub epistemic_integrity: f64,
129    /// D1: Physical energy contribution to local commons.
130    pub thermodynamic_yield: f64,
131    /// D2: Node uptime and bandwidth provision.
132    pub network_resilience: f64,
133    /// D3: Compliance with anti-hoarding demurrage.
134    pub economic_velocity: f64,
135    /// D4: Sortition jury duty, voting, dispute resolution.
136    pub civic_participation: f64,
137    /// D5: Verified physical labor on the commons.
138    pub stewardship_care: f64,
139    /// D6: Entanglement with community consensus.
140    pub semantic_resonance: f64,
141    /// D7: Peer-verified specialization.
142    pub domain_competence: f64,
143}
144
145impl SovereignProfile {
146    /// Create a profile with all dimensions at zero (Observer).
147    pub fn zero() -> Self {
148        Self {
149            epistemic_integrity: 0.0,
150            thermodynamic_yield: 0.0,
151            network_resilience: 0.0,
152            economic_velocity: 0.0,
153            civic_participation: 0.0,
154            stewardship_care: 0.0,
155            semantic_resonance: 0.0,
156            domain_competence: 0.0,
157        }
158    }
159
160    /// Get a dimension value by enum variant.
161    pub fn get(&self, dim: SovereignDimension) -> f64 {
162        match dim {
163            SovereignDimension::EpistemicIntegrity => self.epistemic_integrity,
164            SovereignDimension::ThermodynamicYield => self.thermodynamic_yield,
165            SovereignDimension::NetworkResilience => self.network_resilience,
166            SovereignDimension::EconomicVelocity => self.economic_velocity,
167            SovereignDimension::CivicParticipation => self.civic_participation,
168            SovereignDimension::StewardshipCare => self.stewardship_care,
169            SovereignDimension::SemanticResonance => self.semantic_resonance,
170            SovereignDimension::DomainCompetence => self.domain_competence,
171        }
172    }
173
174    /// Set a dimension value by enum variant.
175    pub fn set(&mut self, dim: SovereignDimension, value: f64) {
176        let target = match dim {
177            SovereignDimension::EpistemicIntegrity => &mut self.epistemic_integrity,
178            SovereignDimension::ThermodynamicYield => &mut self.thermodynamic_yield,
179            SovereignDimension::NetworkResilience => &mut self.network_resilience,
180            SovereignDimension::EconomicVelocity => &mut self.economic_velocity,
181            SovereignDimension::CivicParticipation => &mut self.civic_participation,
182            SovereignDimension::StewardshipCare => &mut self.stewardship_care,
183            SovereignDimension::SemanticResonance => &mut self.semantic_resonance,
184            SovereignDimension::DomainCompetence => &mut self.domain_competence,
185        };
186        *target = value;
187    }
188
189    /// All 8 dimension values as an array in canonical order.
190    pub fn as_array(&self) -> [f64; 8] {
191        [
192            self.epistemic_integrity,
193            self.thermodynamic_yield,
194            self.network_resilience,
195            self.economic_velocity,
196            self.civic_participation,
197            self.stewardship_care,
198            self.semantic_resonance,
199            self.domain_competence,
200        ]
201    }
202
203    /// Create from an array of 8 values in canonical order.
204    pub fn from_array(values: [f64; 8]) -> Self {
205        Self {
206            epistemic_integrity: values[0],
207            thermodynamic_yield: values[1],
208            network_resilience: values[2],
209            economic_velocity: values[3],
210            civic_participation: values[4],
211            stewardship_care: values[5],
212            semantic_resonance: values[6],
213            domain_competence: values[7],
214        }
215    }
216
217    /// Sanitize a single dimension value: NaN/Inf → 0.0, then clamp [0.0, 1.0].
218    fn sanitize(v: f64) -> f64 {
219        if v.is_finite() {
220            v.clamp(0.0, 1.0)
221        } else {
222            0.0
223        }
224    }
225
226    /// Combined score using the given dimension weights.
227    ///
228    /// Each dimension is sanitized (NaN/Inf → 0.0, clamped [0,1]) then
229    /// multiplied by its weight. The result is clamped to [0.0, 1.0].
230    pub fn combined_score(&self, weights: &weights::DimensionWeights) -> f64 {
231        let dims = self.as_array();
232        let w = &weights.weights;
233        let mut score = 0.0;
234        for i in 0..8 {
235            score += Self::sanitize(dims[i]) * w[i];
236        }
237        score.clamp(0.0, 1.0)
238    }
239
240    /// Derive the civic tier from this profile using the given weights.
241    pub fn tier(&self, weights: &weights::DimensionWeights) -> CivicTier {
242        CivicTier::from_score(self.combined_score(weights))
243    }
244
245    /// Check whether this profile meets a civic requirement.
246    pub fn meets_requirement(
247        &self,
248        requirement: &CivicRequirement,
249        weights: &weights::DimensionWeights,
250    ) -> bool {
251        let tier = self.tier(weights);
252        if tier < requirement.min_tier {
253            return false;
254        }
255        for &(dim, min_value) in &requirement.min_dimensions {
256            if Self::sanitize(self.get(dim)) < min_value {
257                return false;
258            }
259        }
260        true
261    }
262}
263
264impl Default for SovereignProfile {
265    fn default() -> Self {
266        Self::zero()
267    }
268}
269
270// ---------------------------------------------------------------------------
271// CivicTier
272// ---------------------------------------------------------------------------
273
274/// Progressive civic tier derived from the combined 8D score.
275///
276/// Higher tiers grant greater governance capabilities. The tier ordering
277/// is total and monotonic.
278#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
279#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
280pub enum CivicTier {
281    /// Read-only access. No governance participation.
282    Observer,
283    /// Basic proposals, commenting.
284    Participant,
285    /// Voting rights.
286    Citizen,
287    /// Constitutional amendments, budget decisions.
288    Steward,
289    /// Emergency powers, system administration.
290    Guardian,
291}
292
293impl CivicTier {
294    /// Minimum combined score for each tier.
295    pub fn min_score(&self) -> f64 {
296        match self {
297            Self::Observer => 0.0,
298            Self::Participant => 0.3,
299            Self::Citizen => 0.4,
300            Self::Steward => 0.6,
301            Self::Guardian => 0.8,
302        }
303    }
304
305    /// Derive tier from a combined score.
306    pub fn from_score(score: f64) -> Self {
307        if score >= 0.8 {
308            Self::Guardian
309        } else if score >= 0.6 {
310            Self::Steward
311        } else if score >= 0.4 {
312            Self::Citizen
313        } else if score >= 0.3 {
314            Self::Participant
315        } else {
316            Self::Observer
317        }
318    }
319
320    /// Vote weight in basis points (1 bp = 0.01%).
321    pub fn vote_weight_bp(&self) -> u32 {
322        match self {
323            Self::Observer => 0,
324            Self::Participant => 5_000,
325            Self::Citizen => 7_500,
326            Self::Steward => 10_000,
327            Self::Guardian => 10_000,
328        }
329    }
330
331    /// i18n key for this tier.
332    pub fn key(&self) -> &'static str {
333        match self {
334            Self::Observer => "observer",
335            Self::Participant => "participant",
336            Self::Citizen => "citizen",
337            Self::Steward => "steward",
338            Self::Guardian => "guardian",
339        }
340    }
341
342    /// English label.
343    pub fn label(&self) -> &'static str {
344        match self {
345            Self::Observer => "Observer",
346            Self::Participant => "Participant",
347            Self::Citizen => "Citizen",
348            Self::Steward => "Steward",
349            Self::Guardian => "Guardian",
350        }
351    }
352
353    /// CSS class suffix for UI styling.
354    pub fn css_class(&self) -> &'static str {
355        match self {
356            Self::Observer => "observer",
357            Self::Participant => "participant",
358            Self::Citizen => "citizen",
359            Self::Steward => "steward",
360            Self::Guardian => "guardian",
361        }
362    }
363}
364
365// ---------------------------------------------------------------------------
366// CivicRequirement
367// ---------------------------------------------------------------------------
368
369/// A governance requirement specifying minimum tier and optional per-dimension minimums.
370///
371/// An agent meets the requirement when:
372/// 1. Their derived tier >= `min_tier`
373/// 2. For each (dimension, threshold) in `min_dimensions`, the agent's
374///    sanitized dimension value >= threshold
375#[derive(Debug, Clone)]
376#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
377pub struct CivicRequirement {
378    pub min_tier: CivicTier,
379    pub min_dimensions: Vec<(SovereignDimension, f64)>,
380}
381
382/// Requirement for basic civic participation (viewing proposals, commenting).
383pub fn civic_requirement_basic() -> CivicRequirement {
384    CivicRequirement {
385        min_tier: CivicTier::Participant,
386        min_dimensions: vec![],
387    }
388}
389
390/// Requirement for submitting proposals.
391pub fn civic_requirement_proposal() -> CivicRequirement {
392    CivicRequirement {
393        min_tier: CivicTier::Participant,
394        min_dimensions: vec![(SovereignDimension::EpistemicIntegrity, 0.25)],
395    }
396}
397
398/// Requirement for casting votes.
399pub fn civic_requirement_voting() -> CivicRequirement {
400    CivicRequirement {
401        min_tier: CivicTier::Citizen,
402        min_dimensions: vec![(SovereignDimension::EpistemicIntegrity, 0.25)],
403    }
404}
405
406/// Requirement for constitutional changes (amendments, budget).
407pub fn civic_requirement_constitutional() -> CivicRequirement {
408    CivicRequirement {
409        min_tier: CivicTier::Steward,
410        min_dimensions: vec![
411            (SovereignDimension::EpistemicIntegrity, 0.5),
412            (SovereignDimension::CivicParticipation, 0.3),
413        ],
414    }
415}
416
417/// Requirement for guardian-level operations (emergency powers).
418pub fn civic_requirement_guardian() -> CivicRequirement {
419    CivicRequirement {
420        min_tier: CivicTier::Guardian,
421        min_dimensions: vec![
422            (SovereignDimension::EpistemicIntegrity, 0.7),
423            (SovereignDimension::CivicParticipation, 0.5),
424        ],
425    }
426}
427
428// ---------------------------------------------------------------------------
429// SovereignCredential
430// ---------------------------------------------------------------------------
431
432/// A time-limited, cryptographically-bound civic credential.
433///
434/// Issued by the identity bridge after gathering all 8 dimensions from
435/// their source clusters. Evaluated locally by pure functions.
436#[derive(Debug, Clone)]
437#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
438pub struct SovereignCredential {
439    /// Decentralized identifier (e.g. "did:mycelix:uCAES...")
440    pub did: String,
441    /// The 8-dimensional profile at issuance time.
442    pub profile: SovereignProfile,
443    /// Derived tier at issuance.
444    pub tier: CivicTier,
445    /// Microseconds since epoch when issued.
446    pub issued_at: u64,
447    /// Microseconds since epoch when this credential expires.
448    pub expires_at: u64,
449    /// DID of the issuing bridge.
450    pub issuer: String,
451    /// Extensible key-value pairs for future dimensions or metadata.
452    pub extensions: Vec<(String, Vec<u8>)>,
453}