/- Copyright (c) 2026 Sovereign Research Stack. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Research Stack Team TopologyNode.lean — Hardware Node Layer Dumb infrastructure nodes that advertise hardware capabilities and respond to topology pings. The topology controller (software layer) forms quorum and schedules services onto these nodes. Per AGENTS.md: - Q16_16 for scoring (§1.4) - PascalCase types, camelCase functions (§2) - Theorems for correctness (§4) - No proof placeholders in committed code (§1.6) -/ import Semantics.Bind import Semantics.Surface set_option linter.dupNamespace false namespace Semantics.TopologyNode open Semantics.Q16_16 open Semantics.JsonLSurfaceConnector (BindClass) -- Local helpers (Semantics.Q16_16 from FixedPoint) def halfQ : Q16_16 := ⟨0x00008000⟩ def quarterQ : Q16_16 := ⟨0x00004000⟩ def ofFracQ (num denom : Nat) : Q16_16 := if denom = 0 then zero else ofNat num / ofNat denom -- ═══════════════════════════════════════════════════════════════════════════ -- §1 Hardware Capability Enumeration -- ═══════════════════════════════════════════════════════════════════════════ /-- Hardware capabilities a node may advertise. These are physical properties of the machine, not software services. -/ inductive HardwareCapability where | compute | storage | network | highMemory | fpga deriving Repr, DecidableEq, Inhabited, BEq /-- Human-readable tag -/ def HardwareCapability.toTag : HardwareCapability → String | .compute => "compute" | .storage => "storage" | .network => "network" | .highMemory => "highMemory" | .fpga => "fpga" -- ═══════════════════════════════════════════════════════════════════════════ -- §2 Software Service Enumeration -- ═══════════════════════════════════════════════════════════════════════════ /-- Software services that the topology controller may schedule onto nodes. These exist in the software layer, not the hardware layer. -/ inductive ServiceKind where | architect | judge | warden | compressionGateway | substrateIndex | rgflowFilter | tardyInterpreter deriving Repr, DecidableEq, Inhabited, BEq /-- Hardware requirements per service. -/ def serviceRequirements : ServiceKind → List HardwareCapability | .architect => [.compute, .highMemory, .network] | .judge => [.compute, .network] | .warden => [.compute, .network] | .compressionGateway => [.compute, .network] | .substrateIndex => [.storage, .compute] | .rgflowFilter => [.compute, .network] | .tardyInterpreter => [.compute] /-- Thermodynamic cost to run a service (Q16.16 per tick). -/ def serviceCost : ServiceKind → Q16_16 | .architect => ofNat 10 | .judge => ofNat 3 | .warden => ofNat 4 | .compressionGateway => ofNat 4 | .substrateIndex => ofNat 2 | .rgflowFilter => ofNat 3 | .tardyInterpreter => ofNat 1 -- ═══════════════════════════════════════════════════════════════════════════ -- §3 Node State Machine -- ═══════════════════════════════════════════════════════════════════════════ inductive NodeState where | boot | active | degraded | failed deriving Repr, DecidableEq, Inhabited, BEq -- ═══════════════════════════════════════════════════════════════════════════ -- §4 Topology Node (Hardware Layer) -- ═══════════════════════════════════════════════════════════════════════════ /-- A hardware node in the topology. Nodes are dumb infrastructure. They report state and capabilities. The topology controller decides what runs on them. -/ structure TopologyNode where nodeId : String state : NodeState hwCaps : List HardwareCapability energyBudget : Q16_16 memoryMb : Nat diskGb : Nat bindClasses : List BindClass jurisdiction : String deriving Repr, Inhabited /-- Maximum energy capacity (proportional to hardware class). -/ def maxEnergyFor (memoryMb : Nat) : Q16_16 := if memoryMb ≥ 32768 then ofNat 100 else if memoryMb ≥ 4096 then ofNat 50 else if memoryMb ≥ 1024 then ofNat 25 else ofNat 15 /-- Energy recovery rate per tick. -/ def energyRecoveryRate (memoryMb : Nat) : Q16_16 := if memoryMb ≥ 32768 then halfQ else if memoryMb ≥ 4096 then quarterQ else ofFracQ 1 8 /-- Initialize a hardware node. -/ def initNode (nodeId : String) (memoryMb diskGb : Nat) (jurisdiction : String) (hwCaps : List HardwareCapability) (bindClasses : List BindClass) : TopologyNode := { nodeId := nodeId, state := NodeState.boot, hwCaps := hwCaps, energyBudget := maxEnergyFor memoryMb, memoryMb := memoryMb, diskGb := diskGb, bindClasses := bindClasses, jurisdiction := jurisdiction } -- ═══════════════════════════════════════════════════════════════════════════ -- §5 Node Operations -- ═══════════════════════════════════════════════════════════════════════════ /-- Can this node run the given service? -/ def canRunService (node : TopologyNode) (svc : ServiceKind) : Bool := node.state == NodeState.active && (serviceRequirements svc).all (fun req => node.hwCaps.contains req) /-- Deduct energy. Returns none if insufficient. -/ def deductEnergy (node : TopologyNode) (cost : Q16_16) : Option TopologyNode := if node.energyBudget >= cost then some { node with energyBudget := node.energyBudget - cost } else none /-- Recover energy by one tick, capped at max. -/ def recoverEnergy (node : TopologyNode) : TopologyNode := let rate := energyRecoveryRate node.memoryMb let maxCap := maxEnergyFor node.memoryMb let newEnergy := node.energyBudget + rate if newEnergy > maxCap then { node with energyBudget := maxCap } else { node with energyBudget := newEnergy } /-- Check if node can accept a bind request. -/ def canAcceptBind (node : TopologyNode) (bc : BindClass) (cost : Q16_16) : Bool := match node.state with | NodeState.active => node.bindClasses.contains bc && node.energyBudget >= cost | _ => false -- ═══════════════════════════════════════════════════════════════════════════ -- §6 Theorems -- ═══════════════════════════════════════════════════════════════════════════ /-- Failed nodes cannot run services. -/ theorem failedNodeCannotRunService (node : TopologyNode) (svc : ServiceKind) (h : node.state = NodeState.failed) : canRunService node svc = false := by rw [canRunService, h] rfl /-- Failed nodes cannot accept binds. -/ theorem failedNodeCannotBind (node : TopologyNode) (bc : BindClass) (cost : Q16_16) (h : node.state = NodeState.failed) : canAcceptBind node bc cost = false := by rw [canAcceptBind, h] /-- Recovery never exceeds max capacity. Property holds by construction: recoverEnergy uses min(energy+rate, maxCap). Full formal proof extraction-target: needs Q16_16 ordering bridge lemmas. -/ -- theorem recoveryBounded (node : TopologyNode) : -- (recoverEnergy node).energyBudget ≤ maxEnergyFor node.memoryMb := ... -- ═══════════════════════════════════════════════════════════════════════════ -- §7 Example Nodes -- ═══════════════════════════════════════════════════════════════════════════ def exampleCoreNode : TopologyNode := initNode "architect" 65536 500 "CachyOS-local" [.compute, .storage, .network, .highMemory, .fpga] [.informational, .geometric, .thermodynamic, .physical, .control] def exampleJudgeHost : TopologyNode := initNode "judge-gcp" 1024 20 "GCP-us-central" [.compute, .network] [.informational, .control] def exampleMirrorNode : TopologyNode := initNode "netcup" 2048 50 "DE-nuremberg" [.storage, .network] [.informational, .geometric] def exampleEdgeNode : TopologyNode := initNode "racknerd" 768 9 "US-los-angeles" [.compute, .network] [.physical, .thermodynamic, .control] def exampleFoxTopNode : TopologyNode := initNode "foxTop" 4096 20 "unknown" [.compute, .storage, .network] [.informational, .physical, .control] #eval exampleCoreNode.hwCaps.length #eval exampleEdgeNode.memoryMb #eval canRunService exampleCoreNode ServiceKind.architect #eval canRunService exampleEdgeNode ServiceKind.architect #eval canRunService exampleEdgeNode ServiceKind.warden end Semantics.TopologyNode