Governance-as-Operator Architecture Specification
1. Overview
The governance-as-operator architecture establishes a clear separation between data management and business logic enforcement in the nondominium system. This design enables independent evolution of resource data structures and governance rules, providing a foundation for modular, maintainable, and extensible decentralized resource management.
1.1 Core Principles
- Resource Zome: Pure data model responsible for resource specification management, economic resource lifecycle, and data persistence
- Governance Zome: State transition operator responsible for evaluating governance rules, validating state changes, and generating economic events
- Cross-Zome Interface: Well-defined communication protocol for state transition requests and decisions
- Event-Driven Architecture: All state changes generate corresponding economic events for audit trails and reputation tracking
1.2 Business Benefits
- Modularity: Independent evolution of data structures and governance logic
- Testability: Governance logic can be unit tested independently of data management
- Swappability: Different governance schemes can be applied to the same resource types
- Maintainability: Clear separation of concerns reduces system complexity
1.3 Related specifications (post-MVP NDO & integrations)
Typed governance evaluation is specified to grow toward a single evaluate_transition_request path that can read:
- Economic closure (Unyt): endorsed
EconomicAgreementrules, transitionrave_hash, RAVE validation —documentation/requirements/ndo_prima_materia.md§6.6, REQ-NDO-CS-07–CS-11; design narrative indocumentation/requirements/post-mvp/unyt-integration.md. - Cross-app identity (Flowsta):
IdentityVerification(or equivalent) rules andFlowstaIdentityslot checks per REQ-NDO-CS-14–CS-15 —ndo_prima_materia.md§6.7;documentation/requirements/post-mvp/flowsta-integration.md. - Agent / resource ontology context:
documentation/archives/governance.md§3.7 (parallel Unyt vs Flowsta operator pattern).
MVP code may still use separate coordinator entry points (e.g. validate_agent_for_promotion); the target architecture folds these into unified transition evaluation as the generic NDO matures.
2. Cross-Zome Interface Specifications
2.1 State Transition Request
The primary interface for resource state changes follows this structure:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct GovernanceTransitionRequest { /// The action the requesting agent wants to perform pub action: VfAction, /// Current state of the resource being modified pub resource: EconomicResource, /// Agent requesting the state change /// TODO (G1, REQ-GOV-16): replace AgentPubKey with AgentContext post-MVP. /// For collective/bot agents, the signing authority is the designated operator key. /// The governance evaluation must resolve AgentContext → effective AgentPubKey /// before capability token validation. pub requesting_agent: AgentPubKey, /// Additional context for the transition pub context: TransitionContext, } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct TransitionContext { /// Target location for transport/move actions pub target_location: Option<String>, /// Quantity change for produce/consume actions pub quantity_change: Option<f64>, /// Target custodian for transfer actions /// TODO (G1, REQ-AGENT-02): replace Option<AgentPubKey> with Option<AgentContext> post-MVP /// to support custody transfer to Collective, Project, Network, and Bot agents. /// AgentContext = union of AgentPubKey | CollectiveAgentHash. pub target_custodian: Option<AgentPubKey>, /// Process notes and observations pub process_notes: Option<String>, /// Associated economic process if applicable pub process_context: Option<ActionHash>, } // TODO (G2, REQ-AGENT-03, REQ-AGENT-05): extend governance evaluation to query AffiliationState. // The validate_agent_permissions function in GovernanceEngine must be extended post-MVP to: // 1. Cross-zome call zome_person to retrieve the requesting agent's AffiliationRecord entries // 2. Derive the agent's current AffiliationState (UnaffiliatedStranger | CloseAffiliate | // ActiveAffiliate | CoreAffiliate | InactiveAffiliate) // 3. Compare AffiliationState against GovernanceRule.rule_data["min_affiliation"] condition // This enables affiliation-gated resource access without storing affiliation as a declared field. }
2.2 State Transition Result
The governance zome returns detailed results for each transition request:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Debug)] pub struct GovernanceTransitionResult { /// Whether the transition was approved pub success: bool, /// Updated resource state (if approved) pub new_resource_state: Option<EconomicResource>, /// Generated economic event for audit trail pub economic_event: Option<EconomicEvent>, /// Validation receipts from governance evaluation pub validation_receipts: Vec<ValidationReceipt>, /// Detailed reasons for rejection (if applicable) pub rejection_reasons: Option<Vec<String>>, /// Required next steps or additional validation needed pub next_steps: Option<Vec<String>>, } }
2.3 Resource State Change
Detailed resource state changes tracked by the system:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ResourceStateChange { /// Hash of the original resource state pub original_resource_hash: ActionHash, /// Hash of the new resource state pub new_resource_hash: ActionHash, /// Action that triggered the state change pub triggering_action: VfAction, /// Agent who initiated the change /// TODO (G1, REQ-GOV-16): replace with AgentContext post-MVP. pub initiated_by: AgentPubKey, /// Governance decision that authorized the change pub governance_decision: GovernanceDecision, /// Economic event recording the change pub economic_event: EconomicEvent, /// Timestamp when the change occurred pub changed_at: Timestamp, } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct GovernanceDecision { /// Whether the change was approved pub approved: bool, /// Governance rules that were evaluated pub evaluated_rules: Vec<ActionHash>, /// Role-based permission check results pub role_permissions: Vec<RolePermissionResult>, /// Additional validation constraints pub validation_constraints: Vec<ValidationConstraint>, /// Decision rationale pub rationale: String, } }
TODO (post-MVP — governance weight dampening, governance.md §6.4): When
governance_weight(agent)incorporatesunyt_credit_capacityfrom the Unyt credit system, apply logarithmic dampening (e.g.,log(1 + credit_capacity)) to prevent a feedback loop where high reputation → high credit → higher governance weight → even higher reputation. Without dampening, early participants accumulate runaway influence. Seegovernance.md §6.4.
3. Modular Design Patterns
3.1 Pure Function Governance
Governance logic is implemented as pure functions that evaluate state transitions without side effects:
#![allow(unused)] fn main() { impl GovernanceEngine { /// Pure function: evaluates state transition without side effects pub fn evaluate_transition( request: GovernanceTransitionRequest, rules: Vec<GovernanceRule>, agent_permissions: AgentPermissions, ) -> ExternResult<TransitionEvaluation> { // 1. Validate agent permissions let permission_result = validate_agent_permissions( &request.requesting_agent, &request.action, &agent_permissions, )?; // 2. Evaluate applicable governance rules let rule_results = evaluate_governance_rules( &request.resource, &request.action, &rules, )?; // 3. Check state transition validity let state_validity = validate_state_transition( &request.resource.state, &request.action, &request.context, )?; // 4. Combine all evaluations into final decision Ok(TransitionEvaluation { allowed: permission_result.allowed && rule_results.all_passed && state_validity.valid, new_state: calculate_new_state(&request.resource, &request.action, &request.context)?, required_events: generate_required_events(&request.action), validation_notes: combine_validation_notes(permission_result, rule_results, state_validity), }) } } }
3.2 State Management Isolation
Resource state management is isolated from governance evaluation:
#![allow(unused)] fn main() { // Resource zome: manages state but delegates decision making impl ResourceManager { pub fn request_state_change( &self, request: GovernanceTransitionRequest, ) -> ExternResult<GovernanceTransitionResult> { // 1. Get current resource state let current_resource = self.get_resource(request.resource_hash)?; // 2. Call governance operator for decision let governance_result = call GovernanceZome::evaluate_transition( GovernanceTransitionRequest { action: request.action, resource: current_resource, requesting_agent: request.requesting_agent, context: request.context, } )?; // 3. Apply state change if approved if governance_result.success { if let Some(new_state) = governance_result.new_resource_state { self.update_resource_state(new_state)?; } if let Some(event) = governance_result.economic_event { self.record_economic_event(event)?; } } Ok(governance_result) } } }
3.3 Event Generation Patterns
All state changes generate corresponding economic events:
#![allow(unused)] fn main() { impl EventGenerator { pub fn generate_state_change_event( action: VfAction, resource_before: &EconomicResource, resource_after: Option<&EconomicResource>, requesting_agent: AgentPubKey, governance_decision: &GovernanceDecision, ) -> ExternResult<EconomicEvent> { Ok(EconomicEvent { action, provider: resource_before.custodian, receiver: requesting_agent, resource_inventoried_as: resource_before.conforms_to, affects: get_action_hash(resource_before)?, resource_quantity: calculate_quantity_change(resource_before, resource_after)?, event_time: sys_time()?, note: Some(format!( "State change approved: {}", governance_decision.rationale )), }) } } }
4. Implementation Guidelines
4.1 Cross-Zome Call Patterns
Efficient cross-zome communication patterns:
#![allow(unused)] fn main() { // Resource zome: synchronous governance evaluation #[hdk_extern] pub fn request_resource_transition( request: GovernanceTransitionRequest, ) -> ExternResult<GovernanceTransitionResult> { // 1. Prepare cross-zome call let governance_input = request.clone(); // 2. Call governance zome let result: GovernanceTransitionResult = call( CallTargetCell::Local, "zome_gouvernance", "evaluate_state_transition".into(), None, &governance_input, )?; // 3. Handle result and update local state if result.success { update_resource_state(result.new_resource_state)?; create_economic_event(result.economic_event)?; } Ok(result) } // Governance zome: pure function evaluation #[hdk_extern] pub fn evaluate_state_transition( request: GovernanceTransitionRequest, ) -> ExternResult<GovernanceTransitionResult> { // 1. Load applicable governance rules let rules = get_applicable_rules(&request.resource)?; // 2. Evaluate using pure function let evaluation = GovernanceEngine::evaluate_transition( request, rules, get_agent_permissions(&request.requesting_agent)?, )?; // 3. Generate economic event if approved let economic_event = if evaluation.allowed { Some(generate_economic_event(&request, &evaluation)?) } else { None }; Ok(GovernanceTransitionResult { success: evaluation.allowed, new_resource_state: evaluation.new_state, economic_event, validation_receipts: evaluation.validation_receipts, rejection_reasons: evaluation.rejection_reasons, next_steps: evaluation.next_steps, }) } }
4.2 Error Handling Strategies
Robust error handling across zome boundaries:
#![allow(unused)] fn main() { #[derive(Debug, Serialize, Deserialize)] pub enum GovernanceError { PermissionDenied(String), RuleViolation(String), InvalidStateTransition(String), ResourceNotFound(String), CrossZomeCallFailed(String), } // Resource zome error handling pub fn handle_governance_result( result: Result<GovernanceTransitionResult, GovernanceError> ) -> ExternResult<()> { match result { Ok(transition_result) => { if transition_result.success { // Apply approved changes apply_state_changes(transition_result)?; } else { // Log rejection for audit trail log_governance_rejection(transition_result)?; } Ok(()) } Err(GovernanceError::CrossZomeCallFailed(msg)) => { // Log error but don't fail completely warn!("Governance evaluation failed: {}", msg); Err(WasmError::Guest(format!("Governance unavailable: {}", msg)).into()) } Err(error) => { // Log other errors error!("Governance error: {:?}", error); Err(WasmError::Guest(error.to_string()).into()) } } } }
4.3 Validation Separation
Clear separation between data validation and governance validation:
#![allow(unused)] fn main() { // Resource zome: data integrity validation impl ResourceValidator { pub fn validate_resource_data( resource: &EconomicResource, ) -> ExternResult<()> { // Pure data validation if resource.quantity <= 0.0 { return Err(ResourceError::InvalidQuantity.into()); } if resource.unit.trim().is_empty() { return Err(ResourceError::InvalidUnit.into()); } // Check specification exists let _spec = get(resource.conforms_to, GetOptions::default())? .ok_or(ResourceError::SpecificationNotFound)?; Ok(()) } } // Governance zome: business rule validation impl GovernanceValidator { pub fn validate_governance_rules( request: &GovernanceTransitionRequest, rules: &[GovernanceRule], ) -> ExternResult<Vec<RuleValidationResult>> { rules.iter() .map(|rule| evaluate_rule(request, rule)) .collect() } } }
5. Testing Strategy
5.1 Unit Testing Governance Logic
Isolated testing of governance evaluation logic:
#![allow(unused)] fn main() { #[cfg(test)] mod governance_tests { use super::*; #[test] fn test_transfer_approval() { let request = create_transfer_request(); let rules = create_test_rules(); let permissions = create_test_permissions(); let result = GovernanceEngine::evaluate_transition( request, rules, permissions, ).unwrap(); assert!(result.allowed); assert!(result.new_state.is_some()); assert!(!result.validation_receipts.is_empty()); } #[test] fn test_unauthorized_role_rejection() { let request = create_repair_request(); let permissions = AgentPermissions { roles: vec!["Transport".to_string()], // No Repair role ..Default::default() }; let result = GovernanceEngine::evaluate_transition( request, vec![], permissions, ).unwrap(); assert!(!result.allowed); assert!(result.rejection_reasons.unwrap().iter() .any(|reason| reason.contains("Insufficient role"))); } } }
5.2 Cross-Zome Integration Testing
Testing interaction between resource and governance zomes:
#![allow(unused)] fn main() { #[cfg(test)] mod cross_zome_tests { use super::*; #[test] fn test_end_to_end_state_transition() { // Setup test environment let (conductor_config, _dna_file) = setup_test_environment().await; let alice_agent = setup_agent(conductor_config.clone()).await; let bob_agent = setup_agent(conductor_config).await; // Create test resource let resource_hash = alice_agent.call::<CreateResourceOutput>( "create_economic_resource", create_test_resource_input(), ).await?.resource_hash; // Request state change let transition_request = GovernanceTransitionRequest { action: VfAction::Transfer, resource: get_resource(resource_hash).await?, requesting_agent: bob_agent.agent_pub_key().clone(), context: TransitionContext { target_custodian: Some(bob_agent.agent_pub_key().clone()), ..Default::default() }, }; // Process transition let result = alice_agent.call::<GovernanceTransitionResult>( "request_resource_transition", transition_request, ).await?; assert!(result.success); assert!(result.economic_event.is_some()); } } }
5.3 Mock Governance for Resource Testing
Mock governance for isolated resource zome testing:
#![allow(unused)] fn main() { pub struct MockGovernanceZome { approval_mode: ApprovalMode, } #[derive(Debug)] pub enum ApprovalMode { ApproveAll, RejectAll, RoleBased(Vec<String>), Conditional(Box<dyn Fn(&GovernanceTransitionRequest) -> bool>), } impl MockGovernanceZome { pub fn evaluate_transition( &self, request: GovernanceTransitionRequest, ) -> ExternResult<GovernanceTransitionResult> { let approved = match &self.approval_mode { ApprovalMode::ApproveAll => true, ApprovalMode::RejectAll => false, ApprovalMode::RoleBased(allowed_roles) => { // Mock role checking logic false // Simplified for example } ApprovalMode::Conditional(validator) => validator(&request), }; Ok(GovernanceTransitionResult { success: approved, new_resource_state: if approved { Some(calculate_new_state(&request)) } else { None }, economic_event: if approved { Some(generate_test_event(&request)) } else { None }, validation_receipts: vec![], rejection_reasons: if approved { None } else { Some(vec!["Mock rejection".to_string()]) }, next_steps: None, }) } } }
6. Performance Considerations
6.1 Cross-Zome Call Optimization
Strategies for efficient cross-zome communication:
#![allow(unused)] fn main() { // Batch multiple state changes #[hdk_extern] pub fn batch_state_transitions( requests: Vec<GovernanceTransitionRequest>, ) -> ExternResult<Vec<GovernanceTransitionResult>> { // Group requests by governance rules needed let mut grouped_requests = HashMap::new(); for request in requests { let rule_set = determine_required_rules(&request); grouped_requests.entry(rule_set) .or_insert_with(Vec::new) .push(request); } // Process each group with single governance call let mut results = Vec::new(); for (rule_set, group_requests) in grouped_requests { let group_results = evaluate_transition_group(rule_set, group_requests)?; results.extend(group_results); } Ok(results) } }
6.2 State Management Efficiency
Efficient state management patterns:
#![allow(unused)] fn main() { // Lazy evaluation of governance rules pub struct LazyRuleEvaluator { cached_rules: LruCache<String, Vec<GovernanceRule>>, rule_loader: Box<dyn Fn(&str) -> ExternResult<Vec<GovernanceRule>>>, } impl LazyRuleEvaluator { pub fn get_rules_for_resource( &mut self, resource_spec: &ActionHash, ) -> ExternResult<&Vec<GovernanceRule>> { let cache_key = format!("{}", resource_spec); if !self.cached_rules.contains(&cache_key) { let rules = (self.rule_loader)(&cache_key)?; self.cached_rules.put(cache_key.clone(), rules); } Ok(self.cached_rules.get(&cache_key).unwrap()) } } }
This architecture specification provides the technical foundation for implementing the governance-as-operator pattern, ensuring clear separation of concerns while maintaining efficient cross-zome communication and comprehensive state management.