1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
// SPDX-License-Identifier: LGPL-3.0-only
#![allow(missing_docs)]
//! Genesis protocol: bootstrapping the ring from pipeline to closed loop.
//!
//! ## Spec traceability
//! - Spec §10.1: The bootstrapping problem
//! - Spec §10.2: Genesis cycle specification (Definition 9)
//! - Spec §10.3: Formal properties (genesis hash, standard operation, instance identity)
//!
//! ## Design notes
//!
//! The Genesis struct manages the state machine for the first cycle (t = 0).
//! During genesis, the ring operates as a directed pipeline (FU → MU → ... → HU).
//! Upon HU's completion, the system fingerprint is computed and deposited in FU's
//! Data layer. The ring then transitions permanently to closed-loop operation.
use crate::error::SomaError;
use crate::fingerprint::ring_system_fingerprint;
use crate::quad::Tree;
use crate::ring::{Ring, RingState};
use crate::types::{Layer, UnitId};
use tracing::instrument;
/// The genesis seed: an opaque byte vector.
///
/// Spec §10.2: "FU initializes with a seed value s₀ (implementation-defined)."
/// Spec §10.3: "The genesis seed functions as a root certificate in a PKI hierarchy."
pub type Seed = Vec<u8>;
/// Genesis protocol state machine.
///
/// Tracks progress through the genesis pipeline and manages the
/// transition from pipeline mode to ring mode.
#[derive(Debug, Clone)]
pub struct Genesis {
/// The genesis seed s₀.
seed: Seed,
/// Which unit has been completed. `None` means no unit has been processed yet.
/// The pipeline progresses: FU → MU → CU → OU → SU → HU.
last_completed: Option<UnitId>,
}
impl Genesis {
/// Create a new genesis protocol instance with the given seed.
///
/// The seed is implementation-defined (Spec §14.2) and trusted by
/// stipulation (Spec §10.2). Its entropy and generation mechanism
/// are implementation choices.
pub fn new(seed: Seed) -> Self {
Self {
seed,
last_completed: None,
}
}
/// Get the genesis seed.
pub fn seed(&self) -> &[u8] {
&self.seed
}
/// The next unit that must be processed in the genesis pipeline.
/// Returns `None` if all units have been processed (ready for closure).
pub fn next_unit(&self) -> Option<UnitId> {
match self.last_completed {
None => Some(UnitId::FU),
Some(UnitId::FU) => Some(UnitId::MU),
Some(UnitId::MU) => Some(UnitId::CU),
Some(UnitId::CU) => Some(UnitId::OU),
Some(UnitId::OU) => Some(UnitId::SU),
Some(UnitId::SU) => Some(UnitId::HU),
Some(UnitId::HU) => None, // Pipeline complete
}
}
/// Is the genesis pipeline complete (all 6 units processed)?
pub fn is_pipeline_complete(&self) -> bool {
self.last_completed == Some(UnitId::HU)
}
/// Mark a unit as completed in the genesis pipeline.
///
/// Enforces sequential processing: units must be completed in ring order.
pub fn mark_completed(&mut self, unit: UnitId) -> Result<(), SomaError> {
let expected = self.next_unit().ok_or(SomaError::GenesisAlreadyComplete)?;
if unit != expected {
return Err(SomaError::GenesisOutOfOrder {
expected,
received: unit,
});
}
self.last_completed = Some(unit);
Ok(())
}
/// Initialize the ring for genesis: set state to Genesis, seed FU.
///
/// This is Step 1 of the genesis cycle (Spec §10.2):
/// "FU initializes with a seed value s₀."
#[instrument(skip_all, name = "core.genesis.initialize_ring")]
pub fn initialize_ring(&self, ring: &mut Ring) -> Result<(), SomaError> {
if ring.state != RingState::Uninitialized {
return Err(SomaError::GenesisAlreadyStarted);
}
ring.state = RingState::Genesis;
ring.cycle_index = 0;
// Seed FU's Data layer with s₀
let seed_hash = blake3::hash(&self.seed);
let mut seed_tree = Tree::new();
seed_tree.insert("genesis.seed".into(), self.seed.clone());
seed_tree.insert("genesis.seed_hash".into(), seed_hash.as_bytes().to_vec());
let fu_data = ring.unit_mut(UnitId::FU).som_quad_mut(Layer::Data);
fu_data.root = *seed_hash.as_bytes();
fu_data.pointer = *blake3::hash(b"genesis.fu.data.pointer").as_bytes();
fu_data.tree = seed_tree;
Ok(())
}
/// Close the ring: compute system fingerprint, deposit in FU, transition to Ring mode.
///
/// This is Steps 6–8 of the genesis cycle (Spec §10.2):
/// - Compute F_system^(0) from all 90 positions
/// - Deposit F_system^(0) and s₀ in FU's Data layer
/// - Transition permanently from pipeline mode to ring mode
///
/// ## Spec §10.3: Formal properties
///
/// - Genesis hash: SOM_0.hash = H(SOM_0.state ‖ s₀)
/// - Instance identity: F_system^(0) is the identity of this SOMA instance
/// - Irreversibility: once closed, there is no mechanism to return to genesis
pub fn close_ring(&self, ring: &mut Ring) -> Result<GenesisResult, SomaError> {
if ring.state != RingState::Genesis {
return Err(SomaError::GenesisNotInProgress);
}
if !self.is_pipeline_complete() {
return Err(SomaError::GenesisPipelineIncomplete);
}
// Compute the system fingerprint from all 90 positions
let system_fp = ring_system_fingerprint(ring);
// Compute the genesis hash: H(SOM_0.state ‖ s₀)
let mut hasher = blake3::Hasher::new();
hasher.update(&system_fp);
hasher.update(&self.seed);
let genesis_hash: [u8; 32] = *hasher.finalize().as_bytes();
// Deposit in FU's Data layer
let fu_data = ring.unit_mut(UnitId::FU).som_quad_mut(Layer::Data);
fu_data
.tree
.insert("genesis.system_fingerprint".into(), system_fp.to_vec());
fu_data
.tree
.insert("genesis.hash".into(), genesis_hash.to_vec());
fu_data.tree.insert("genesis.closed".into(), vec![1]);
// Transition to ring mode — this is irreversible (Spec §10.2, Condition 3)
ring.state = RingState::Ring;
Ok(GenesisResult {
system_fingerprint: system_fp,
genesis_hash,
seed_hash: *blake3::hash(&self.seed).as_bytes(),
})
}
}
/// Result of a successful genesis closure.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GenesisResult {
/// The system fingerprint F_system^(0): identity of this SOMA instance.
pub system_fingerprint: [u8; 32],
/// The genesis hash: H(SOM_0.state ‖ s₀). Root of the temporal chain.
pub genesis_hash: [u8; 32],
/// Hash of the genesis seed for reference.
pub seed_hash: [u8; 32],
}
// inline: exercises module-private items via super::*
#[cfg(test)]
mod tests {
use super::*;
use crate::quad::Quad;
/// Simulate a minimal genesis pipeline: each unit gets a basic Quad.
/// FU's Data layer is NOT overwritten — it retains the seed from initialize_ring.
fn run_genesis_pipeline(ring: &mut Ring, genesis: &mut Genesis) {
for &unit in &UnitId::ALL {
let us = ring.unit_mut(unit);
us.soma_quad = Quad::from_strings(
&format!("{unit}.soma.root"),
&format!("{unit}.soma.ptr"),
{
let mut t = Tree::new();
t.insert("unit".into(), format!("{unit}").into_bytes());
t
},
);
for &layer in &Layer::ALL {
// Skip FU.Data — it already holds the genesis seed from initialize_ring.
// Overwriting it would erase the seed, making all instances identical.
if unit == UnitId::FU && layer == Layer::Data {
continue;
}
let mut tree = Tree::new();
tree.insert("origin".into(), format!("{unit}.{layer}").into_bytes());
*us.som_quad_mut(layer) = Quad::from_strings(
&format!("{unit}.{layer}.root"),
&format!("{unit}.{layer}.ptr"),
tree,
);
}
genesis.mark_completed(unit).unwrap();
}
}
#[test]
fn genesis_pipeline_order() {
let genesis = Genesis::new(b"test-seed".to_vec());
assert_eq!(genesis.next_unit(), Some(UnitId::FU));
}
#[test]
fn genesis_rejects_out_of_order() {
let mut genesis = Genesis::new(b"seed".to_vec());
// Must start with FU, not MU
assert!(genesis.mark_completed(UnitId::MU).is_err());
}
#[test]
fn genesis_full_lifecycle() {
let mut ring = Ring::new();
let mut genesis = Genesis::new(b"soma-genesis-seed-v1".to_vec());
// Initialize
genesis.initialize_ring(&mut ring).unwrap();
assert_eq!(ring.state, RingState::Genesis);
// Run pipeline
run_genesis_pipeline(&mut ring, &mut genesis);
assert!(genesis.is_pipeline_complete());
// Close ring
let result = genesis.close_ring(&mut ring).unwrap();
assert_eq!(ring.state, RingState::Ring);
assert_ne!(result.system_fingerprint, [0u8; 32]);
assert_ne!(result.genesis_hash, [0u8; 32]);
// Verify FU Data layer has genesis deposits
let fu_data = ring.unit(UnitId::FU).som_quad(Layer::Data);
assert!(fu_data.tree.contains_key("genesis.system_fingerprint"));
assert!(fu_data.tree.contains_key("genesis.hash"));
assert!(fu_data.tree.contains_key("genesis.closed"));
}
#[test]
fn genesis_is_irreversible() {
let mut ring = Ring::new();
let mut genesis = Genesis::new(b"seed".to_vec());
genesis.initialize_ring(&mut ring).unwrap();
run_genesis_pipeline(&mut ring, &mut genesis);
genesis.close_ring(&mut ring).unwrap();
// Cannot re-initialize after closure
let genesis2 = Genesis::new(b"another-seed".to_vec());
assert!(genesis2.initialize_ring(&mut ring).is_err());
}
#[test]
fn different_seeds_produce_different_identities() {
// Spec §10.3: "Two instances with different seeds produce different
// identities, even if their structural topology is identical."
let result1 = {
let mut ring = Ring::new();
let mut genesis = Genesis::new(b"seed-alpha".to_vec());
genesis.initialize_ring(&mut ring).unwrap();
run_genesis_pipeline(&mut ring, &mut genesis);
genesis.close_ring(&mut ring).unwrap()
};
let result2 = {
let mut ring = Ring::new();
let mut genesis = Genesis::new(b"seed-beta".to_vec());
genesis.initialize_ring(&mut ring).unwrap();
run_genesis_pipeline(&mut ring, &mut genesis);
genesis.close_ring(&mut ring).unwrap()
};
assert_ne!(result1.genesis_hash, result2.genesis_hash);
assert_ne!(result1.seed_hash, result2.seed_hash);
// System fingerprints will also differ because FU's Data layer
// contains the seed, which differs between instances.
assert_ne!(result1.system_fingerprint, result2.system_fingerprint);
}
}