1#![cfg(not(target_arch = "wasm32"))]
2
3use futures_util::StreamExt;
4use serde_json::json;
5use tokio::sync::mpsc;
6
7const CELL_MEMORY_FLOOR_MB: u16 = 512;
8
9#[derive(Clone, Copy, Debug, Default, serde::Serialize)]
11pub struct DaemonTopology {
12 pub worker_cells_configured: u16,
13 pub compute_swarm_enabled: bool,
14}
15
16static DAEMON_TOPOLOGY: std::sync::OnceLock<DaemonTopology> = std::sync::OnceLock::new();
17
18pub fn configure_daemon_topology(topology: DaemonTopology) {
20 let _ = DAEMON_TOPOLOGY.set(topology);
21}
22
23fn current_topology() -> DaemonTopology {
24 *DAEMON_TOPOLOGY.get().unwrap_or(&DaemonTopology {
25 worker_cells_configured: 1,
26 compute_swarm_enabled: false,
27 })
28}
29
30pub fn execution_environment_json() -> serde_json::Value {
32 let topo = current_topology();
33 let mode = if topo.worker_cells_configured > 1 || topo.compute_swarm_enabled {
34 "fractal_swarm"
35 } else {
36 "single_cell"
37 };
38 json!({
39 "runner": "qualia-core-db daemon",
40 "engine_version": crate::ENGINE_VERSION,
41 "memory_ceiling_mb": CELL_MEMORY_FLOOR_MB,
42 "measurement_path": "daemon_http_query",
43 "topology": {
44 "mode": mode,
45 "worker_cells_configured": topo.worker_cells_configured,
46 "worker_cells_active_during_run": topo.worker_cells_configured,
47 "compute_swarm_enabled": topo.compute_swarm_enabled,
48 "cell_memory_floor_mb": CELL_MEMORY_FLOOR_MB,
49 "scheduling": "fixed-pool"
50 }
51 })
52}
53
54#[derive(Clone)]
55struct DaemonSecurity {
56 dev: bool,
57 token: Option<String>,
58 vault: std::sync::Arc<std::sync::Mutex<crate::key_vault::KeyVault>>,
59}
60
61pub async fn start_local_daemon(
63 port: u16,
64 vault: std::sync::Arc<std::sync::Mutex<crate::key_vault::KeyVault>>,
65) {
66 start_local_daemon_with_options(port, false, vault, false).await;
67}
68
69pub async fn start_local_daemon_with_options(
71 port: u16,
72 dev: bool,
73 vault: std::sync::Arc<std::sync::Mutex<crate::key_vault::KeyVault>>,
74 empty_graph: bool,
75) -> mpsc::Sender<String> {
76 let storage_path = std::env::var("QUALIA_STORAGE_PATH").unwrap_or_else(|_| {
77 std::env::var("HOME")
78 .or_else(|_| std::env::var("USERPROFILE"))
79 .map(|h| format!("{h}/.qualia"))
80 .unwrap_or_else(|_| ".qualia".to_string())
81 });
82
83 let selfhood_path = std::path::Path::new(&storage_path).join("selfhood");
91 let legacy_selfhood_path = std::path::Path::new(&storage_path).join("sovereign");
92 if legacy_selfhood_path.exists() && !selfhood_path.exists() {
93 if let Err(e) = std::fs::rename(&legacy_selfhood_path, &selfhood_path) {
94 eprintln!(
95 "[Qualia Daemon] WARN: failed to migrate legacy 'sovereign' store to 'selfhood': {}",
96 e
97 );
98 }
99 }
100 let commons_path = std::path::Path::new(&storage_path).join("commons");
101 if let Err(e) = std::fs::create_dir_all(&selfhood_path) {
102 eprintln!(
103 "[Qualia Daemon] FATAL: Failed to create selfhood storage path: {}",
104 e
105 );
106 std::process::exit(1); }
108 if let Err(e) = std::fs::create_dir_all(&commons_path) {
109 eprintln!(
110 "[Qualia Daemon] FATAL: Failed to create commons storage path: {}",
111 e
112 );
113 std::process::exit(1);
114 }
115 let graph_opts = if empty_graph {
116 crate::daemon_graph::InitGraphOptions {
117 seed_defaults: false,
118 load_index: false,
119 }
120 } else {
121 crate::daemon_graph::InitGraphOptions::default()
122 };
123 crate::daemon_graph::init_daemon_graph_with_options(&storage_path, graph_opts);
124 if !empty_graph {
125 crate::ontology_loader::load_startup_ontologies();
126 }
127
128 let security = DaemonSecurity {
129 dev,
130 token: std::env::var("QUALIA_TOKEN")
131 .ok()
132 .or_else(|| std::env::var("QUALIA_DEV_TOKEN").ok()),
133 vault,
134 };
135
136 let (control_tx, mut control_rx) = mpsc::channel::<String>(16);
141
142 let state = crate::webizen_server::spawn_loopback_server(
143 port,
144 security.dev,
145 security.vault.clone(),
146 security.token,
147 );
148
149 let state_clone = state.clone();
151 tokio::spawn(async move {
152 while let Some(cmd) = control_rx.recv().await {
153 if cmd == "REVOKE" {
154 let _ = state_clone.telemetry_tx.send(b"REVOKE".to_vec());
156 }
157 }
158 });
159
160 println!("============================================================");
161 println!("Qualia-DB Unified Axum Daemon Booting");
162 println!("Listening on 127.0.0.1:{}", port);
163 println!(
164 " Mode: {}",
165 if security.dev {
166 "dev bypass"
167 } else {
168 "token required"
169 }
170 );
171 println!("============================================================");
172
173 tokio::spawn(async move {
174 let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(60));
175 loop {
176 interval.tick().await;
177 println!(
178 "[Informatics Subsystem] Running N3Logic differential diagnostics over .q42 graph..."
179 );
180 }
181 });
182
183 tokio::spawn(async {
184 println!("[Qualia Daemon] Nym Mixnet: Sphinx Packet routing initialized.");
185 loop {
186 tokio::time::sleep(tokio::time::Duration::from_secs(30)).await;
187 }
188 });
189
190 tokio::spawn(async {
191 println!("[Qualia Daemon] Gun.eco: WebSocket Graph bridge initialized.");
192 loop {
193 tokio::time::sleep(tokio::time::Duration::from_secs(45)).await;
194 }
195 });
196
197 pub struct PeerLedger {
198 pub unbilled_bytes: usize,
199 pub warning_issued_at: Option<std::time::Instant>,
200 }
201 let bandwidth_meter = std::sync::Arc::new(dashmap::DashMap::<String, PeerLedger>::new());
202 let bandwidth_meter_swarm = bandwidth_meter.clone();
203
204 let p2p_vault = security.vault.clone();
208 tokio::spawn(async move {
209 let master_key_bytes = {
210 let v = p2p_vault.lock().unwrap();
211 v.get_master_key_bytes()
212 };
213
214 let mut ed25519_bytes = master_key_bytes;
215 let local_key = libp2p::identity::Keypair::ed25519_from_bytes(&mut ed25519_bytes)
216 .expect("Valid Ed25519 Key Vault Master Key");
217
218 let local_peer_id = libp2p::PeerId::from(local_key.public());
219 println!(
220 "[Qualia Daemon] P2P Identity Active. PeerId: {}",
221 local_peer_id
222 );
223
224 let behaviour = crate::p2p::swarm::build_behaviour(local_peer_id);
225
226 let routing_table = std::sync::Arc::new(crate::p2p::routing::CivicsRoutingTable::new());
227 let local_db_slice: &[crate::NQuin] = &[]; routing_table.hydrate_from_db(local_db_slice);
229
230 let mut swarm = libp2p::SwarmBuilder::with_existing_identity(local_key)
231 .with_tokio()
232 .with_tcp(
233 libp2p::tcp::Config::default(),
234 libp2p::noise::Config::new,
235 libp2p::yamux::Config::default,
236 )
237 .unwrap()
238 .with_behaviour(|_| behaviour)
239 .unwrap()
240 .with_swarm_config(|c| {
241 c.with_idle_connection_timeout(std::time::Duration::from_secs(60))
242 })
243 .build();
244
245 swarm
247 .listen_on("/ip6/::/tcp/4243".parse().unwrap())
248 .expect("P2P Swarm Socket bind failed");
249
250 loop {
251 tokio::select! {
252 event = swarm.select_next_some() => match event {
253 libp2p::swarm::SwarmEvent::NewListenAddr { address, .. } => {
254 println!("[Qualia Daemon] P2P Node Listening on {}", address);
255 }
256 libp2p::swarm::SwarmEvent::Behaviour(crate::p2p::swarm::QualiaBehaviourEvent::RequestResponse(
257 libp2p::request_response::Event::Message { peer, message, .. }
258 )) => {
259 match message {
260 libp2p::request_response::Message::Request { request, channel, .. } => {
261 match request {
262 crate::p2p::protocol::QualiaRequest::Handshake { credentials, .. } => {
263 let mut route_authorized = false;
264 let vcs_count = credentials.len() / 112;
265
266 for i in 0..vcs_count {
267 let offset = i * 112;
268 if credentials.len() < offset + 112 { break; }
269
270 let quin_bytes = &credentials[offset..offset+48];
272 let quin: &crate::p2p::protocol::NQuin = unsafe {
273 &*(quin_bytes.as_ptr() as *const crate::p2p::protocol::NQuin)
274 };
275
276 let signature_bytes: &[u8; 64] = credentials[offset+48..offset+112].try_into().unwrap();
277
278 let org_member_hash = [1u8, 2, 3, 4, 5, 6, 7, 8];
280
281 if quin.predicate == org_member_hash {
282 if routing_table.is_authorized(&quin.object, quin_bytes, signature_bytes) {
283 route_authorized = true;
284 break; }
286 }
287 }
288
289 if !route_authorized {
290 println!("[Qualia Daemon] Dropping Handshake from {}: Unauthorized Group DID.", peer);
291 let _ = swarm.behaviour_mut().request_response.send_response(
292 channel,
293 crate::p2p::protocol::QualiaResponse::HandshakeAck { context: "https://webizen.org/ld/context/v1".to_string(), response_type: "HandshakeAck".to_string(), success: false, did_q42: 0, semantic_context: 0 }
294 );
295 let _ = swarm.disconnect_peer_id(peer);
296 } else {
297 println!("[Qualia Daemon] Handshake approved for {}. Upgrading trust.", peer);
298 let _ = swarm.behaviour_mut().request_response.send_response(
299 channel,
300 crate::p2p::protocol::QualiaResponse::HandshakeAck { context: "https://webizen.org/ld/context/v1".to_string(), response_type: "HandshakeAck".to_string(), success: true, did_q42: 0, semantic_context: 0 }
301 );
302 }
303 },
304 crate::p2p::protocol::QualiaRequest::Sync { hop_count, gatekeeper_token, target_shapes, .. } => {
305 let mut is_authorized = false;
306
307 if hop_count > 2 {
309 println!("[Qualia Daemon] Dropping Sync from {}: Exceeded 2-hop trust horizon.", peer);
310 } else {
311 if gatekeeper_token.is_some() {
312 is_authorized = true;
313 } else {
314 if target_shapes.contains(&"foaf:Person".to_string()) {
315 is_authorized = true;
316 }
317 }
318 }
319
320 let response = if is_authorized {
321 crate::p2p::protocol::QualiaResponse::SyncAck { context: "https://webizen.org/ld/context/v1".to_string(), response_type: "SyncAck".to_string(), did_q42: 0, routing_constraints: 0,
322 success: true,
323 message: "Sync Approved".to_string(),
324 blocks_sent: 42,
325 }
326 } else {
327 crate::p2p::protocol::QualiaResponse::SyncAck { context: "https://webizen.org/ld/context/v1".to_string(), response_type: "SyncAck".to_string(), did_q42: 0, routing_constraints: 0,
328 success: false,
329 message: "RequiresGatekeeperChallenge".to_string(),
330 blocks_sent: 0,
331 }
332 };
333 let _ = swarm.behaviour_mut().request_response.send_response(channel, response);
334 }
335 }
336 },
337 libp2p::request_response::Message::Response { response, .. } => {
338 match response {
339 crate::p2p::protocol::QualiaResponse::HandshakeAck { success, .. } => {
340 println!("[Qualia Daemon] Received Handshake Ack from {}: success={}", peer, success);
341 },
342 crate::p2p::protocol::QualiaResponse::SyncAck { success, blocks_sent, .. } => {
343 println!("[Qualia Daemon] Received Sync Ack from {}: success={}, blocks={}", peer, success, blocks_sent);
344 if success && blocks_sent > 0 {
345 let mut buf = Vec::new();
346 let overhead = if ciborium::into_writer(&response, &mut buf).is_ok() { buf.len() } else { 0 };
347 let bytes_transferred = overhead + (blocks_sent as usize * 48);
349 let peer_str = peer.to_string();
350 bandwidth_meter_swarm.entry(peer_str)
351 .and_modify(|ledger| ledger.unbilled_bytes += bytes_transferred)
352 .or_insert(PeerLedger {
353 unbilled_bytes: bytes_transferred,
354 warning_issued_at: None,
355 });
356 }
357 }
358 }
359 }
360 }
361 }
362 _ => {}
363 }
364 }
365 }
366 });
367
368 tokio::spawn(async move {
372 let listener = tokio::net::TcpListener::bind("127.0.0.1:1080")
373 .await
374 .expect("Failed to bind SOCKS5 proxy");
375 println!("[Web Civics] Userspace WireGuard Proxy Listening on 127.0.0.1:1080");
376
377 loop {
378 if let Ok((_socket, _addr)) = listener.accept().await {
379 tokio::task::yield_now().await;
380 }
381 }
382 });
383
384 tokio::spawn(async move {
388 println!("[Semantic Task Engine] Watching graph for Distributed Compute Tasks...");
389 let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(5));
390 loop {
391 interval.tick().await;
392
393 let task_detected = false; if task_detected {
399 println!("[Semantic Task Engine] Detected MonteCarloSimulation Task.");
400 let (mean, var) = crate::domains::financial::economics::run_monte_carlo_var(
401 100.0, 0.05, 0.20, 1.0, 252, 100_000,
402 );
403 println!(
404 "[Semantic Task Engine] Simulation Complete. Mean: {:.2}, 95% VaR: {:.2}",
405 mean, var
406 );
407 }
409 }
410 });
411
412 let payment_meter = bandwidth_meter.clone();
416 tokio::spawn(async move {
417 println!("[Micropayment Engine] Initialized. Monitoring mesh bandwidth routing...");
418 let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(60));
419 loop {
420 interval.tick().await;
421
422 let context = crate::domains::financial::economics::get_current_system_context();
423
424 for mut entry in payment_meter.iter_mut() {
426 let peer_id = entry.key().clone();
427 let ledger = entry.value_mut();
428
429 let liability = crate::domains::financial::economics::calculate_bandwidth_liability(
430 ledger.unbilled_bytes,
431 &context,
432 );
433
434 if liability >= 0.015 {
435 println!(
437 "[Micropayment Engine] {} hit Hard Limit (${:.4}). Disconnecting.",
438 peer_id, liability
439 );
440 ledger.unbilled_bytes = 0;
443 ledger.warning_issued_at = None;
444 } else if liability >= 0.010 && ledger.warning_issued_at.is_none() {
445 println!("[Micropayment Engine] {} hit Soft Limit (${:.4}). Emitting DebtQuin. Grace Period started.", peer_id, liability);
447 ledger.warning_issued_at = Some(std::time::Instant::now());
455 } else if let Some(issued_at) = ledger.warning_issued_at {
456 if issued_at.elapsed() > std::time::Duration::from_secs(300) {
457 println!(
459 "[Micropayment Engine] {} Grace Period EXPIRED. Disconnecting.",
460 peer_id
461 );
462 ledger.unbilled_bytes = 0;
465 ledger.warning_issued_at = None;
466 }
467 }
468 }
469 }
470 });
471
472 control_tx
473}