Skip to main content

qualia_core_db/services/
daemon.rs

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/// Fractal-sharding topology configured at daemon boot (`qualia-cli daemon --workers N`).
10#[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
18/// Called by `qualia-cli daemon` before the HTTP server starts.
19pub 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
30/// JSON block shared by `/health`, benchmark exports, and the comparative harness.
31pub 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
61/// Starts the native loopback daemon on 127.0.0.1 with strict token checks.
62pub 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
69/// Starts the native loopback daemon with WebSocket and REST handoff routes.
70pub 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    // Create Isolated Paths for Ontological Path Isolation.
84    // `selfhood` = the guarded store of the records that *pertain to* the self (mind + secrets — the
85    // dignity foundation). The self is NOT in the machine and is never captured; this store is a
86    // *selfhood guardian* holding those records on the principal's (the natural person's) behalf, in a
87    // principal→agent relationship, later linked with rights (the outward personhood layer). An identifier
88    // or record is *about* the person, never *is* the person. Migrate the legacy `sovereign` store name
89    // if an existing install still carries it (one-time, non-destructive: only when the new name is absent).
90    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); // Graceful fail on volume disconnect
107    }
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    // -----------------------------------------------------------------------
137    // WebSocket bridge — handshake + query metrics + bench_load
138    // -----------------------------------------------------------------------
139
140    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    // Wire up control channel
150    let state_clone = state.clone();
151    tokio::spawn(async move {
152        while let Some(cmd) = control_rx.recv().await {
153            if cmd == "REVOKE" {
154                // broadcast revocation over the unified telemetry channel
155                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    // -----------------------------------------------------------------------
205    // P2P Network Swarm (CBOR-LD Semantic Sync)
206    // -----------------------------------------------------------------------
207    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] = &[]; // Mock of memory mapped DB slice
228        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        // Bind to all IPv6 interfaces
246        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                                            // Zero-allocation cast for the 48-byte Quin
271                                            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                                            // Mock ORG_MEMBER_HASH for demonstration
279                                            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; // Instant approval
285                                                }
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                                        // Strict 2-Hop Limit for the Web Civics Mesh
308                                        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                                            // Actual serialized payload: CBOR overhead + (blocks_sent * 48 bytes per NQuin)
348                                            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    // -----------------------------------------------------------------------
369    // Web Civics SOCKS5 Userspace Proxy
370    // -----------------------------------------------------------------------
371    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    // -----------------------------------------------------------------------
385    // Semantic Task Engine (Economics & Scientific Compute)
386    // -----------------------------------------------------------------------
387    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            // In a real implementation, we would query the CRDT graph here:
394            // SELECT ?task WHERE { ?task rdf:type qualia:MonteCarloSimulation . ?task status "Pending" }
395
396            // Mocking a detected task for the walkthrough:
397            let task_detected = false; // Set to true to test
398            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                // We would then write the result back into the graph as a qualia:SimulationResult
408            }
409        }
410    });
411
412    // -----------------------------------------------------------------------
413    // Micropayment Settlement Engine (Soft/Hard Limits & Grace Period)
414    // -----------------------------------------------------------------------
415    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            // Iterate over the DashMap without blocking the main router
425            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                    // HARD LIMIT
436                    println!(
437                        "[Micropayment Engine] {} hit Hard Limit (${:.4}). Disconnecting.",
438                        peer_id, liability
439                    );
440                    // Disconnect peer immediately
441                    // network::disconnect_and_block(&peer_id);
442                    ledger.unbilled_bytes = 0;
443                    ledger.warning_issued_at = None;
444                } else if liability >= 0.010 && ledger.warning_issued_at.is_none() {
445                    // SOFT LIMIT
446                    println!("[Micropayment Engine] {} hit Soft Limit (${:.4}). Emitting DebtQuin. Grace Period started.", peer_id, liability);
447                    // 1. Emit DebtQuin to local graph
448                    // query_engine.insert_debt_quin(&peer_id, liability);
449
450                    // 2. Send warning over libp2p
451                    // network::send_payment_warning(&peer_id, liability);
452
453                    // 3. Start the grace period clock
454                    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                        // 5 Minute Grace Period
458                        println!(
459                            "[Micropayment Engine] {} Grace Period EXPIRED. Disconnecting.",
460                            peer_id
461                        );
462                        // Disconnect peer immediately
463                        // network::disconnect_and_block(&peer_id);
464                        ledger.unbilled_bytes = 0;
465                        ledger.warning_issued_at = None;
466                    }
467                }
468            }
469        }
470    });
471
472    control_tx
473}