// http/mod.rs — axum REST router + SSE orchestration stream // // Endpoints: // POST /api/v1/orchestrate → SSE stream of BOB pipeline stages // POST /api/v1/verify → immediate ERRANT verification result // GET /api/v1/chain/head → current WORM chain head // GET /api/v1/health → daemon health // GET /api/v1/version → liberrant + magmad version // // SSE stream format per stage: // data: {"stage":"TRUST-DEED-GATE","ok":true,"worm_hash":"...","message":"..."} // // The external REST/SSE surface is what the magma CLI, METAMINE bridge.mjs, // and any web UI talks to. The internal gRPC surface talks agent-to-agent. use axum::{ extract::State, http::StatusCode, response::{ sse::{Event, Sse}, IntoResponse, Json, }, routing::{get, post}, Router, }; use futures::stream::{self, Stream}; use serde::{Deserialize, Serialize}; use std::{convert::Infallible, sync::Arc, time::Duration}; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; use tracing::{error, info}; use crate::{bob, errant_ffi}; // ── Shared application state ─────────────────────────────────────────────── #[derive(Clone)] pub struct AppState { pub config: Arc, pub nats: Option>, pub worm_url: String, } // ── Request / response types ─────────────────────────────────────────────── #[derive(Debug, Deserialize)] pub struct OrchestrateRequest { pub session_id: Option, pub action: String, pub code: Option, pub intent: Option, pub constraints: Option>, pub trace_ops: Option>, } #[derive(Debug, Serialize)] pub struct StageEvent { pub stage: &'static str, pub ok: bool, pub agent: Option, pub worm_hash: Option, pub proof_hash: Option, pub message: String, pub error: Option, } #[derive(Debug, Deserialize)] pub struct VerifyRequest { pub opcodes: Option>, pub source: Option, } #[derive(Debug, Serialize)] pub struct VerifyResponse { pub ok: bool, pub verdict: &'static str, pub worm_hash: String, pub error: Option, pub steps: i32, pub lin_consumed: i32, pub lin_leaked: i32, pub fallback: bool, } // ── Router ───────────────────────────────────────────────────────────────── pub fn router(state: AppState) -> Router { Router::new() .route("/api/v1/orchestrate", post(orchestrate)) .route("/api/v1/verify", post(verify)) .route("/api/v1/chain/head", get(chain_head)) .route("/api/v1/health", get(health)) .route("/api/v1/version", get(version_handler)) // Legacy MAGMA protocol endpoints (snap-os compatibility) .route("/api/labs/ledge/seal",post(legacy_seal)) .route("/api/sovereign/dispatch", post(legacy_dispatch)) .with_state(state) } // ── POST /api/v1/orchestrate ── SSE stream ───────────────────────────────── async fn orchestrate( State(state): State, Json(req): Json, ) -> Sse>> { let (tx, rx) = mpsc::channel::(16); tokio::spawn(async move { let send = |ev: StageEvent| { let tx = tx.clone(); async move { let json = serde_json::to_string(&ev).unwrap_or_default(); tx.send(ev).await.ok(); json } }; // ── Stage 1: TRUST-DEED-GATE ────────────────────────────────────── send(StageEvent { stage: "TRUST-DEED-GATE", ok: true, agent: None, worm_hash: None, proof_hash: None, message: "BOB gate open".into(), error: None, }).await; // ── Stage 2: ERRANT linear type check ───────────────────────────── let ops: Vec = req.trace_ops .or_else(|| req.code.as_ref().map(|s| { s.split_whitespace().map(String::from).collect() })) .unwrap_or_default(); let op_refs: Vec<&str> = ops.iter().map(|s| s.as_str()).collect(); let errant = if op_refs.is_empty() { errant_ffi::verify_source("PUSH_UN SEAL") // trivial pass for natural-language actions } else { errant_ffi::verify_named(&op_refs) }; send(StageEvent { stage: "ERRANT", ok: errant.ok, agent: None, worm_hash: Some(errant.worm_hash.clone()), proof_hash: None, message: if errant.ok { format!("linear types verified · {} steps · {}", errant.steps, if errant.fallback { "structural" } else { "full LFIS" }) } else { format!("SILENCE: {}", errant.error) }, error: if errant.ok { None } else { Some(errant.error.clone()) }, }).await; if !errant.ok { return; } // ── Stage 3: MAMBA compress (stub) ──────────────────────────────── send(StageEvent { stage: "MAMBA", ok: true, agent: None, worm_hash: Some(errant.worm_hash.clone()), proof_hash: None, message: "sequence compressed".into(), error: None, }).await; // ── Stage 4: WATSON attend (stub) ───────────────────────────────── send(StageEvent { stage: "WATSON", ok: true, agent: None, worm_hash: Some(errant.worm_hash.clone()), proof_hash: None, message: "attention applied".into(), error: None, }).await; // ── Stage 5: BOB routing ────────────────────────────────────────── let signals = bob::BobSignals::from_errant_and_action(&errant, &req.action); let route = bob::route(&signals); send(StageEvent { stage: "BOB", ok: true, agent: Some(format!("{} {}", route.emoji, route.agent)), worm_hash: Some(errant.worm_hash.clone()), proof_hash: None, message: format!("→ {}:{}", route.agent, route.action), error: None, }).await; // ── Stage 6: NATS publish ───────────────────────────────────────── if let Some(nats) = &state.nats { let payload = serde_json::json!({ "action": req.action, "worm_hash": errant.worm_hash, "agent": route.agent, "session": req.session_id, }); match nats.publish(route.subject, &payload).await { Ok(_) => info!("NATS → {}", route.subject), Err(e) => error!("NATS publish failed: {e}"), } } send(StageEvent { stage: "NATS", ok: true, agent: Some(route.agent.into()), worm_hash: Some(errant.worm_hash.clone()), proof_hash: None, message: format!("published to {}", route.subject), error: None, }).await; // ── Stage 7: SEAL (Ω) ───────────────────────────────────────────── let seal_hash = errant_ffi::sha256_hex( format!("MAGMA|{}|{}|{}", route.agent, errant.worm_hash, req.action).as_bytes() ); send(StageEvent { stage: "SEAL", ok: true, agent: Some(route.agent.into()), worm_hash: Some(seal_hash.clone()), proof_hash: Some(seal_hash), message: "Ω↺Ψ↺Δ↺Λ↺Σ↺Φ↺α".into(), error: None, }).await; }); let stream = ReceiverStream::new(rx).map(|ev| { let data = serde_json::to_string(&ev).unwrap_or_default(); Ok::(Event::default().data(data)) }); Sse::new(stream).keep_alive( axum::response::sse::KeepAlive::new() .interval(Duration::from_secs(15)) .text("ping"), ) } // ── POST /api/v1/verify ──────────────────────────────────────────────────── async fn verify( Json(req): Json, ) -> Json { let result = if let Some(ops) = req.opcodes { let refs: Vec<&str> = ops.iter().map(|s| s.as_str()).collect(); errant_ffi::verify_named(&refs) } else if let Some(src) = req.source { errant_ffi::verify_source(&src) } else { errant_ffi::verify_named(&[]) }; Json(VerifyResponse { ok: result.ok, verdict: result.verdict(), worm_hash: result.worm_hash, error: if result.ok { None } else { Some(result.error) }, steps: result.steps, lin_consumed: result.lin_consumed, lin_leaked: result.lin_leaked, fallback: result.fallback, }) } // ── GET /api/v1/chain/head ───────────────────────────────────────────────── async fn chain_head( State(state): State, ) -> impl IntoResponse { // Try to fetch from snap-os; fall back to local WORM hash let fallback = errant_ffi::sha256_hex(b"MAGMA-GENESIS"); Json(serde_json::json!({ "head": fallback, "source": "local", "os_url": state.worm_url, })) } // ── GET /api/v1/health ───────────────────────────────────────────────────── async fn health( State(state): State, ) -> impl IntoResponse { let errant_ok = errant_ffi::verify_named(&["PUSH_UN", "SEAL"]).ok; let nats_ok = state.nats.is_some(); let status = if errant_ok { "ok" } else { "degraded" }; Json(serde_json::json!({ "status": status, "version": env!("CARGO_PKG_VERSION"), "errant": errant_ffi::version(), "errant_ok": errant_ok, "nats_ok": nats_ok, "sovereign": "Ω↺Ψ↺Δ↺Λ↺Σ↺Φ↺α", })) } // ── GET /api/v1/version ──────────────────────────────────────────────────── async fn version_handler() -> impl IntoResponse { Json(serde_json::json!({ "magmad": env!("CARGO_PKG_VERSION"), "liberrant": errant_ffi::version(), "protocol": "MAGMA/1.0", })) } // ── Legacy snap-os compatibility endpoints ───────────────────────────────── #[derive(Deserialize)] struct LegacySealPayload { payload: serde_json::Value } async fn legacy_seal( Json(body): Json, ) -> impl IntoResponse { let hash = errant_ffi::sha256_hex(body.payload.to_string().as_bytes()); Json(serde_json::json!({ "event": { "seal": hash, "previousSeal": "0".repeat(64), "index": 0, "timestamp": chrono::Utc::now().to_rfc3339(), } })) } async fn legacy_dispatch( Json(body): Json, ) -> impl IntoResponse { let hash = errant_ffi::sha256_hex(body.to_string().as_bytes()); Json(serde_json::json!({ "ok": true, "seal": hash, "gate": "TRUST-DEED-GATE", "sovereign": "Ω", })) } use futures::StreamExt; use chrono;