SNAPKITTYWEST commited on
Commit
4ae5926
·
verified ·
1 Parent(s): ffd375a

Add magmad: Rust sovereign agentic daemon — HTTP, gRPC, CLI, BOB/MAGMA routing

Browse files
magmad/Cargo.toml ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [package]
2
+ name = "magmad"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ authors = ["Ahmad Ali Parr <ahmedparr93@gmail.com>"]
6
+ description = "Sovereign agentic daemon — gRPC + SSE gateway to BOB/ERRANT/MAGMA"
7
+
8
+ [[bin]]
9
+ name = "magmad"
10
+ path = "src/main.rs"
11
+
12
+ [[bin]]
13
+ name = "magma"
14
+ path = "src/cli.rs"
15
+
16
+ [dependencies]
17
+ # Async runtime
18
+ tokio = { version = "1", features = ["full"] }
19
+ tokio-stream = { version = "0.1", features = ["sync"] }
20
+ futures = "0.3"
21
+
22
+ # HTTP / SSE (external API :3000)
23
+ axum = { version = "0.7", features = ["macros"] }
24
+ axum-extra = { version = "0.9", features = ["typed-header"] }
25
+ tower = "0.4"
26
+ tower-http = { version = "0.5", features = ["cors", "trace"] }
27
+
28
+ # gRPC (internal API :50051)
29
+ tonic = "0.11"
30
+ prost = "0.12"
31
+
32
+ # NATS pub/sub
33
+ async-nats = "0.35"
34
+
35
+ # Serialization
36
+ serde = { version = "1", features = ["derive"] }
37
+ serde_json = "1"
38
+
39
+ # HTTP client (calls snap-os WORM chain + BOB dispatch)
40
+ reqwest = { version = "0.12", default-features = false,
41
+ features = ["rustls-tls", "json", "stream"] }
42
+
43
+ # Timestamps
44
+ chrono = { version = "0.4", features = ["serde"] }
45
+
46
+ # Crypto (Rust-side hashing — C side uses inline SHA-256)
47
+ sha2 = "0.10"
48
+ hex = "0.4"
49
+
50
+ # UUID for session IDs
51
+ uuid = { version = "1", features = ["v4"] }
52
+
53
+ # CLI
54
+ clap = { version = "4", features = ["derive"] }
55
+
56
+ # Error handling
57
+ anyhow = "1"
58
+ thiserror = "1"
59
+
60
+ # Tracing
61
+ tracing = "0.1"
62
+ tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
63
+
64
+ # Libc for FFI
65
+ libc = "0.2"
66
+
67
+ [build-dependencies]
68
+ cc = "1" # compiles errant.c → liberrant.a
69
+
70
+ [profile.release]
71
+ opt-level = 3
72
+ lto = true
73
+ codegen-units = 1
74
+ strip = true
magmad/build.rs ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // build.rs — magmad build script
2
+ //
3
+ // Compiles errant/runtime/errant.c → liberrant.a via the cc crate.
4
+ // Linked statically so magmad has zero runtime dep on errant.c.
5
+ //
6
+ // Proto types are hand-written in src/grpc/sovereign_types.rs and do not
7
+ // require protoc. When protoc is available, remove sovereign_types.rs and
8
+ // re-add: tonic_build::configure().compile(&["proto/sovereign.proto"],&["proto"])
9
+
10
+ fn main() {
11
+ let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
12
+ let is_msvc = target_env == "msvc";
13
+
14
+ let mut build = cc::Build::new();
15
+ build
16
+ .file("../errant/runtime/errant.c")
17
+ .include("../errant/runtime");
18
+
19
+ if is_msvc {
20
+ // MSVC: suppress C4996 (deprecated strncpy/snprintf — debug labels only)
21
+ build.flag("/wd4996");
22
+ } else {
23
+ // GCC/Clang: suppress debug-label truncation warnings (safe)
24
+ build
25
+ .flag("-std=c11")
26
+ .flag("-O2")
27
+ .flag("-Wno-stringop-truncation")
28
+ .flag("-Wno-format-truncation");
29
+ }
30
+
31
+ build.compile("errant");
32
+
33
+ println!("cargo:rustc-link-lib=static=errant");
34
+ println!("cargo:rerun-if-changed=../errant/runtime/errant.c");
35
+ println!("cargo:rerun-if-changed=../errant/runtime/errant.h");
36
+ }
magmad/proto/sovereign.proto ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ syntax = "proto3";
2
+ package sovereign;
3
+
4
+ // Sovereign gRPC service — internal machine-to-machine API
5
+ // External: REST/SSE on :3000 ← CLI / web / METAMINE bridge
6
+ // Internal: gRPC on :50051 ← BOB pipeline stages, agent-to-agent
7
+ //
8
+ // Ahmad Ali Parr · SnapKitty Collective · 2026
9
+
10
+ service Sovereign {
11
+ // Primary endpoint: takes an instruction, streams back pipeline stages
12
+ // Mirrors POST /api/v1/orchestrate (SSE) but over gRPC
13
+ rpc Orchestrate (OrchestrateRequest) returns (stream OrchestrateEvent);
14
+
15
+ // Fast path: verify an ERRANT opcode sequence, return immediately
16
+ rpc Verify (VerifyRequest) returns (VerifyResult);
17
+
18
+ // BOB routing decision without full execution
19
+ rpc Route (RouteRequest) returns (RouteResult);
20
+
21
+ // WORM chain head (session state)
22
+ rpc ChainHead (Empty) returns (ChainHeadResult);
23
+
24
+ // Health + version
25
+ rpc Health (Empty) returns (HealthResult);
26
+ }
27
+
28
+ // ── Orchestrate ────────────────────────────────────────────────────────────
29
+
30
+ message OrchestrateRequest {
31
+ // WORM chain head = session ID (stateful across calls via the chain)
32
+ string session_id = 1;
33
+
34
+ // Instruction payload
35
+ string action = 2; // "verify_and_execute" | "forge" | "seal" | "query"
36
+ string code = 3; // ERRANT source, METAMINE .meta, or natural instruction
37
+ string intent = 4; // "formal_verification" | "agent_dispatch" | "compute"
38
+
39
+ // Structural constraints passed to ERRANT checker
40
+ repeated string constraints = 5; // ["monotone", "linear", "wasm_safe"]
41
+
42
+ // Optional: pre-computed METAMINE trace ops (from bridge.mjs)
43
+ repeated string trace_ops = 6;
44
+ }
45
+
46
+ message OrchestrateEvent {
47
+ string stage = 1; // "TRUST-DEED-GATE"|"ERRANT"|"BOB"|"NATS"|"SEAL"
48
+ bool ok = 2;
49
+ string agent = 3; // BOB routing result: "FORGE"|"CIPHER"|"NOVA" etc.
50
+ string worm_hash = 4; // running WORM hash at this stage
51
+ string proof_hash = 5; // Coq/Lean proof hash (if formal verification ran)
52
+ string message = 6; // human-readable stage description
53
+ string error = 7; // non-empty on failure
54
+ bytes payload = 8; // structured stage output (JSON-encoded)
55
+ }
56
+
57
+ // ── Verify ─────────────────────────────────────────────────────────────────
58
+
59
+ message VerifyRequest {
60
+ repeated string opcodes = 1; // ["PUSH_LIN", "HASH", "SEAL"] etc.
61
+ string source = 2; // alternative: raw ERRANT source text
62
+ }
63
+
64
+ message VerifyResult {
65
+ bool ok = 1;
66
+ string worm_hash = 2;
67
+ string error = 3;
68
+ int32 steps = 4;
69
+ int32 lin_consumed = 5;
70
+ int32 lin_leaked = 6;
71
+ bool fallback = 7; // true = structural check (SEAL-last), not full ERRANT
72
+ }
73
+
74
+ // ── Route ──────────────────────────────────────────────────────────────────
75
+
76
+ message RouteRequest {
77
+ string action = 1;
78
+ string worm_hash = 2;
79
+ int32 ruptures = 3;
80
+ bool has_forge = 4;
81
+ bool has_seal = 5;
82
+ bool has_vault = 6;
83
+ }
84
+
85
+ message RouteResult {
86
+ string emoji = 1; // "⚒️" | "🔒" | "🏦" | "🔮" | "🧠" etc.
87
+ string agent = 2; // "FORGE" | "CIPHER" | "VAULT" | "ORACLE" | "NOVA"
88
+ string action = 3; // "BUILD" | "SIGN" | "APPROVE" | "KNOWLEDGE" | "SYNTHETIC"
89
+ string subject = 4; // NATS subject: "sovereign.forge.build.v1" etc.
90
+ }
91
+
92
+ // ── Shared ─────────────────────────────────────────────────────────────────
93
+
94
+ message Empty {}
95
+
96
+ message ChainHeadResult {
97
+ string head = 1;
98
+ uint64 index = 2;
99
+ string timestamp = 3;
100
+ }
101
+
102
+ message HealthResult {
103
+ string status = 1; // "ok" | "degraded" | "offline"
104
+ string version = 2;
105
+ bool nats_ok = 3;
106
+ bool errant_ok = 4;
107
+ string worm_hash = 5; // current chain head
108
+ }
magmad/src/bob.rs ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // bob.rs — BOB routing logic
2
+ //
3
+ // BOB = TRUST-DEED-GATE → MAMBA → WATSON → PROLOG-KERNEL → HASKELL-MONAD → SEAL
4
+ //
5
+ // After ERRANT verifies linearity, BOB determines which MAGMA verb + agent
6
+ // handles the request. Mirrors bridge.mjs bobRoute() priority table.
7
+ //
8
+ // Priority (descending): SEAL > VAULT > SENTINEL > FORGE > ORACLE > ANCHOR > FLUX > NOVA
9
+
10
+ use serde::{Deserialize, Serialize};
11
+
12
+ #[derive(Debug, Clone, Serialize, Deserialize)]
13
+ pub struct BobRoute {
14
+ pub emoji: &'static str,
15
+ pub agent: &'static str,
16
+ pub action: &'static str,
17
+ pub subject: &'static str, // NATS subject
18
+ pub verb: &'static str, // MAGMA HTTP event name
19
+ }
20
+
21
+ #[derive(Debug, Default)]
22
+ pub struct BobSignals {
23
+ pub has_seal: bool,
24
+ pub has_vault: bool,
25
+ pub ruptures: i32,
26
+ pub has_forge: bool,
27
+ pub has_resonance: bool,
28
+ pub has_memory: bool,
29
+ pub has_echo: bool,
30
+ pub steps: i32,
31
+ }
32
+
33
+ static ROUTE_SEAL: BobRoute = BobRoute { emoji: "🔒", agent: "CIPHER", action: "SIGN", subject: "sovereign.cipher.seal.v1", verb: "METAMINE_SEAL" };
34
+ static ROUTE_VAULT: BobRoute = BobRoute { emoji: "🏦", agent: "VAULT", action: "APPROVE", subject: "sovereign.vault.approve.v1", verb: "VAULT_APPROVE" };
35
+ static ROUTE_SENTINEL: BobRoute = BobRoute { emoji: "👁", agent: "SENTINEL", action: "MONITOR", subject: "sovereign.sentinel.watch.v1", verb: "SENTINEL_MONITOR" };
36
+ static ROUTE_FORGE: BobRoute = BobRoute { emoji: "⚒️", agent: "FORGE", action: "BUILD", subject: "sovereign.forge.build.v1", verb: "FORGE_BUILD" };
37
+ static ROUTE_ORACLE: BobRoute = BobRoute { emoji: "🔮", agent: "ORACLE", action: "KNOWLEDGE", subject: "sovereign.oracle.query.v1", verb: "ORACLE_QUERY" };
38
+ static ROUTE_MNEMEX: BobRoute = BobRoute { emoji: "📜", agent: "MNEMEX", action: "WORM", subject: "sovereign.mnemex.anchor.v1", verb: "ANCHOR" };
39
+ static ROUTE_HERALD: BobRoute = BobRoute { emoji: "⚡", agent: "HERALD", action: "BROADCAST", subject: "sovereign.herald.flux.v1", verb: "FLUX_BROADCAST" };
40
+ static ROUTE_NOVA: BobRoute = BobRoute { emoji: "🧠", agent: "NOVA", action: "SYNTHETIC", subject: "sovereign.nova.query.v1", verb: "NOVA_SYNTHETIC" };
41
+
42
+ impl BobSignals {
43
+ /// Derive signals from ERRANT result + action string
44
+ pub fn from_errant_and_action(
45
+ errant: &crate::errant_ffi::ErrantResult,
46
+ action: &str,
47
+ ) -> Self {
48
+ BobSignals {
49
+ has_seal: action.contains("seal") || action.contains("sign"),
50
+ has_vault: action.contains("vault") || action.contains("approve") || action.contains("transfer"),
51
+ ruptures: errant.ruptures,
52
+ has_forge: action.contains("forge") || action.contains("build"),
53
+ has_resonance: action.contains("query") || action.contains("oracle") || action.contains("resona"),
54
+ has_memory: action.contains("anchor") || action.contains("memory"),
55
+ has_echo: errant.output.len() > 0,
56
+ steps: errant.steps,
57
+ }
58
+ }
59
+ }
60
+
61
+ pub fn route(signals: &BobSignals) -> &'static BobRoute {
62
+ // TRUST-DEED-GATE: critical tasks always route through CIPHER for sealing
63
+ if signals.has_seal { return &ROUTE_SEAL; }
64
+ if signals.has_vault { return &ROUTE_VAULT; }
65
+ if signals.ruptures > 0 { return &ROUTE_SENTINEL; }
66
+ if signals.has_forge { return &ROUTE_FORGE; }
67
+ if signals.has_resonance { return &ROUTE_ORACLE; }
68
+ if signals.has_memory { return &ROUTE_MNEMEX; }
69
+ if signals.has_echo { return &ROUTE_HERALD; }
70
+ &ROUTE_NOVA
71
+ }
72
+
73
+ /// The BOB pipeline stages printed in SSE stream
74
+ pub fn pipeline_stages() -> &'static [&'static str] {
75
+ &["TRUST-DEED-GATE", "MAMBA", "WATSON", "PROLOG-KERNEL", "HASKELL-MONAD", "SEAL"]
76
+ }
magmad/src/cli.rs ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // cli.rs — `magma` command-line interface
2
+ //
3
+ // A thin client that talks to a running magmad daemon over REST.
4
+ // Ships as a separate binary so it can be installed alongside magmad.
5
+ //
6
+ // Usage:
7
+ // magma health # ping the daemon
8
+ // magma verify PUSH_UN PUSH_LIN SEAL # ERRANT opcode trace
9
+ // magma run program.meta # stream a .meta file through BOB
10
+ // magma seal "some event text" # compute WORM hash and seal
11
+ // magma talk "build the auth module" # SSE orchestrate stream
12
+ // magma chain # print current WORM chain head
13
+ //
14
+ // Daemon is assumed at http://127.0.0.1:3000 unless MAGMA_URL is set.
15
+
16
+ use std::io::{self, Write as IoWrite};
17
+ use std::path::PathBuf;
18
+ use anyhow::{Context, Result};
19
+ use clap::{Parser, Subcommand};
20
+ use reqwest::Client;
21
+ use serde_json::Value;
22
+
23
+ // ── Args ───────────────────────────────────────────────────────────────────────
24
+
25
+ #[derive(Parser)]
26
+ #[command(
27
+ name = "magma",
28
+ version,
29
+ about = "Sovereign agentic runtime CLI",
30
+ long_about = r#"
31
+ magma — SnapKitty Sovereign CLI
32
+ Talks to magmad (default http://127.0.0.1:3000).
33
+ Set MAGMA_URL to override.
34
+
35
+ Examples:
36
+ magma health
37
+ magma verify PUSH_UN PUSH_LIN SEAL
38
+ magma run program.meta
39
+ magma talk "deploy the auth module"
40
+ magma seal "key-rotation-event-2026"
41
+ magma chain
42
+ "#
43
+ )]
44
+ struct Cli {
45
+ #[command(subcommand)]
46
+ command: Command,
47
+ }
48
+
49
+ #[derive(Subcommand)]
50
+ enum Command {
51
+ /// Health check — pings magmad and prints status
52
+ Health,
53
+
54
+ /// ERRANT opcode trace verification
55
+ Verify {
56
+ /// Opcodes to verify (e.g. PUSH_UN PUSH_LIN SEAL)
57
+ #[arg(required = true)]
58
+ opcodes: Vec<String>,
59
+ },
60
+
61
+ /// Source string verification
62
+ Source {
63
+ /// ERRANT source string
64
+ code: String,
65
+ },
66
+
67
+ /// Run a .meta METAMINE file through the BOB pipeline (streams SSE)
68
+ Run {
69
+ /// Path to .meta file
70
+ file: PathBuf,
71
+ /// Action to pass to BOB routing (default: "build")
72
+ #[arg(long, default_value = "build")]
73
+ action: String,
74
+ /// Print raw SSE events
75
+ #[arg(long)]
76
+ raw: bool,
77
+ },
78
+
79
+ /// SSE orchestration stream for a natural-language action
80
+ Talk {
81
+ /// Instruction (e.g. "deploy the auth module")
82
+ instruction: String,
83
+ /// Print raw SSE events
84
+ #[arg(long)]
85
+ raw: bool,
86
+ },
87
+
88
+ /// Seal an arbitrary string and print the WORM hash
89
+ Seal {
90
+ /// Text to seal
91
+ text: String,
92
+ },
93
+
94
+ /// Print the current WORM chain head
95
+ Chain,
96
+
97
+ /// Print daemon version info
98
+ Version,
99
+ }
100
+
101
+ // ── Main ───────────────────────────────────────────────────────────────────────
102
+
103
+ #[tokio::main]
104
+ async fn main() -> Result<()> {
105
+ let cli = Cli::parse();
106
+ let base_url = std::env::var("MAGMA_URL")
107
+ .unwrap_or_else(|_| "http://127.0.0.1:3000".into());
108
+ let client = Client::builder()
109
+ .timeout(std::time::Duration::from_secs(30))
110
+ .build()?;
111
+
112
+ match cli.command {
113
+ Command::Health => cmd_health(&client, &base_url).await?,
114
+ Command::Version => cmd_version(&client, &base_url).await?,
115
+ Command::Chain => cmd_chain(&client, &base_url).await?,
116
+
117
+ Command::Verify { opcodes } => {
118
+ cmd_verify(&client, &base_url, opcodes).await?
119
+ }
120
+ Command::Source { code } => {
121
+ cmd_source(&client, &base_url, code).await?
122
+ }
123
+ Command::Run { file, action, raw } => {
124
+ cmd_run(&client, &base_url, file, action, raw).await?
125
+ }
126
+ Command::Talk { instruction, raw } => {
127
+ cmd_talk(&client, &base_url, instruction, raw).await?
128
+ }
129
+ Command::Seal { text } => {
130
+ cmd_seal(&client, &base_url, text).await?
131
+ }
132
+ }
133
+
134
+ Ok(())
135
+ }
136
+
137
+ // ── Commands ───────────────────────────────────────────────────────────────────
138
+
139
+ async fn cmd_health(client: &Client, base: &str) -> Result<()> {
140
+ let resp: Value = client.get(format!("{base}/api/v1/health"))
141
+ .send().await?.json().await?;
142
+ print_health(&resp);
143
+ Ok(())
144
+ }
145
+
146
+ async fn cmd_version(client: &Client, base: &str) -> Result<()> {
147
+ let resp: Value = client.get(format!("{base}/api/v1/version"))
148
+ .send().await?.json().await?;
149
+ println!("magmad {}", resp["magmad"].as_str().unwrap_or("?"));
150
+ println!("liberrant {}", resp["liberrant"].as_str().unwrap_or("?"));
151
+ println!("protocol {}", resp["protocol"].as_str().unwrap_or("?"));
152
+ Ok(())
153
+ }
154
+
155
+ async fn cmd_chain(client: &Client, base: &str) -> Result<()> {
156
+ let resp: Value = client.get(format!("{base}/api/v1/chain/head"))
157
+ .send().await?.json().await?;
158
+ println!("head {}", resp["head"].as_str().unwrap_or("?"));
159
+ println!("source {}", resp["source"].as_str().unwrap_or("?"));
160
+ Ok(())
161
+ }
162
+
163
+ async fn cmd_verify(client: &Client, base: &str, opcodes: Vec<String>) -> Result<()> {
164
+ let body = serde_json::json!({ "opcodes": opcodes });
165
+ let resp: Value = client.post(format!("{base}/api/v1/verify"))
166
+ .json(&body).send().await?.json().await?;
167
+ print_verify(&resp);
168
+ Ok(())
169
+ }
170
+
171
+ async fn cmd_source(client: &Client, base: &str, code: String) -> Result<()> {
172
+ let body = serde_json::json!({ "source": code });
173
+ let resp: Value = client.post(format!("{base}/api/v1/verify"))
174
+ .json(&body).send().await?.json().await?;
175
+ print_verify(&resp);
176
+ Ok(())
177
+ }
178
+
179
+ async fn cmd_run(
180
+ client: &Client,
181
+ base: &str,
182
+ file: PathBuf,
183
+ action: String,
184
+ raw: bool,
185
+ ) -> Result<()> {
186
+ let code = std::fs::read_to_string(&file)
187
+ .with_context(|| format!("cannot read {}", file.display()))?;
188
+
189
+ // Parse .meta → opcode names (words only, ignore comments)
190
+ let ops: Vec<String> = code
191
+ .lines()
192
+ .filter(|l| !l.trim_start().starts_with('#') && !l.trim_start().starts_with(';'))
193
+ .flat_map(|l| l.split_whitespace())
194
+ .filter(|w| !w.is_empty())
195
+ .map(String::from)
196
+ .collect();
197
+
198
+ eprintln!("metamine: {} opcodes from {}", ops.len(), file.display());
199
+ cmd_orchestrate(client, base, action, Some(ops), raw).await
200
+ }
201
+
202
+ async fn cmd_talk(
203
+ client: &Client,
204
+ base: &str,
205
+ instruction: String,
206
+ raw: bool,
207
+ ) -> Result<()> {
208
+ cmd_orchestrate(client, base, instruction, None, raw).await
209
+ }
210
+
211
+ async fn cmd_seal(client: &Client, base: &str, text: String) -> Result<()> {
212
+ // Use verify endpoint with PUSH_UN + SEAL to get a worm_hash sealing the input
213
+ // We encode the text as the action field so it influences the final seal hash
214
+ let body = serde_json::json!({
215
+ "opcodes": ["PUSH_UN", "SEAL"],
216
+ "source": text,
217
+ });
218
+ let resp: Value = client.post(format!("{base}/api/v1/verify"))
219
+ .json(&body).send().await?.json().await?;
220
+ let hash = resp["worm_hash"].as_str().unwrap_or("?");
221
+ println!("WORM seal: {hash}");
222
+ println!("Ω text: {text}");
223
+ Ok(())
224
+ }
225
+
226
+ async fn cmd_orchestrate(
227
+ client: &Client,
228
+ base: &str,
229
+ action: String,
230
+ trace_ops: Option<Vec<String>>,
231
+ raw: bool,
232
+ ) -> Result<()> {
233
+ let body = serde_json::json!({
234
+ "action": action,
235
+ "trace_ops": trace_ops,
236
+ });
237
+
238
+ let resp = client.post(format!("{base}/api/v1/orchestrate"))
239
+ .json(&body)
240
+ .header("Accept", "text/event-stream")
241
+ .send().await?;
242
+
243
+ // Stream SSE line-by-line
244
+ use futures::StreamExt;
245
+ let mut stream = resp.bytes_stream();
246
+ let mut buf = String::new();
247
+
248
+ while let Some(chunk) = stream.next().await {
249
+ let chunk = chunk?;
250
+ buf.push_str(&String::from_utf8_lossy(&chunk));
251
+
252
+ // Process complete lines
253
+ while let Some(nl) = buf.find('\n') {
254
+ let line = buf[..nl].trim().to_string();
255
+ buf.drain(..=nl);
256
+
257
+ if line.is_empty() || line == "ping" { continue; }
258
+ if let Some(data) = line.strip_prefix("data: ") {
259
+ if raw {
260
+ println!("{data}");
261
+ } else {
262
+ match serde_json::from_str::<Value>(data) {
263
+ Ok(ev) => print_stage_event(&ev),
264
+ Err(_) => println!(" {data}"),
265
+ }
266
+ }
267
+ io::stdout().flush().ok();
268
+ }
269
+ }
270
+ }
271
+
272
+ Ok(())
273
+ }
274
+
275
+ // ── Pretty printers ────────────────────────────────────────────────────────────
276
+
277
+ fn print_health(v: &Value) {
278
+ let status = v["status"].as_str().unwrap_or("?");
279
+ let version = v["version"].as_str().unwrap_or("?");
280
+ let errant = v["errant_ok"].as_bool().unwrap_or(false);
281
+ let nats = v["nats_ok"].as_bool().unwrap_or(false);
282
+
283
+ let tick = |b: bool| if b { "✓" } else { "✗" };
284
+ println!("status {status}");
285
+ println!("version {version}");
286
+ println!("liberrant {}", tick(errant));
287
+ println!("nats {}", tick(nats));
288
+ println!("sovereign {}", v["sovereign"].as_str().unwrap_or("Ω"));
289
+ }
290
+
291
+ fn print_verify(v: &Value) {
292
+ let ok = v["ok"].as_bool().unwrap_or(false);
293
+ let verdict = v["verdict"].as_str().unwrap_or("?");
294
+ let hash = v["worm_hash"].as_str().unwrap_or("?");
295
+ let steps = v["steps"].as_i64().unwrap_or(0);
296
+ let lc = v["lin_consumed"].as_i64().unwrap_or(0);
297
+ let ll = v["lin_leaked"].as_i64().unwrap_or(0);
298
+ let fb = v["fallback"].as_bool().unwrap_or(false);
299
+
300
+ let mark = if ok { "EVIDENCE" } else { "SILENCE" };
301
+ println!("{mark} · {verdict}");
302
+ println!("hash {hash}");
303
+ println!("steps {} · lin_consumed {} · lin_leaked {}", steps, lc, ll);
304
+ if fb { println!("mode structural (Ω-path)"); }
305
+ if let Some(err) = v["error"].as_str() {
306
+ println!("error {err}");
307
+ }
308
+ }
309
+
310
+ fn print_stage_event(v: &Value) {
311
+ let stage = v["stage"].as_str().unwrap_or("?");
312
+ let ok = v["ok"].as_bool().unwrap_or(false);
313
+ let msg = v["message"].as_str().unwrap_or("");
314
+ let tick = if ok { "✓" } else { "✗" };
315
+
316
+ print!(" [{tick}] {stage:<20} {msg}");
317
+
318
+ if let Some(agent) = v["agent"].as_str() {
319
+ print!(" ← {agent}");
320
+ }
321
+ if let Some(hash) = v["worm_hash"].as_str() {
322
+ print!(" Ω:{}", &hash[..12]);
323
+ }
324
+ println!();
325
+
326
+ if !ok {
327
+ if let Some(err) = v["error"].as_str() {
328
+ println!(" ERROR: {err}");
329
+ }
330
+ }
331
+ }
magmad/src/config.rs ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // config.rs — magmad runtime configuration
2
+
3
+ use serde::Deserialize;
4
+
5
+ #[derive(Debug, Clone, Deserialize)]
6
+ pub struct Config {
7
+ /// REST + SSE port (external API — CLI, web, METAMINE bridge)
8
+ #[serde(default = "default_http_port")]
9
+ pub http_port: u16,
10
+
11
+ /// gRPC port (internal — agent-to-agent, BOB pipeline stages)
12
+ #[serde(default = "default_grpc_port")]
13
+ pub grpc_port: u16,
14
+
15
+ /// NATS server URL
16
+ #[serde(default = "default_nats_url")]
17
+ pub nats_url: String,
18
+
19
+ /// snap-os base URL (WORM chain + MAGMA verbs)
20
+ #[serde(default = "default_os_url")]
21
+ pub os_url: String,
22
+
23
+ /// ERRANT LFIS interpreter URL (if external; falls back to liberrant.a)
24
+ #[serde(default = "default_errant_url")]
25
+ pub errant_url: String,
26
+
27
+ /// Log level: "trace"|"debug"|"info"|"warn"|"error"
28
+ #[serde(default = "default_log_level")]
29
+ pub log_level: String,
30
+ }
31
+
32
+ fn default_http_port() -> u16 { 3000 }
33
+ fn default_grpc_port() -> u16 { 50051 }
34
+ fn default_nats_url() -> String { "nats://127.0.0.1:4222".into() }
35
+ fn default_os_url() -> String { "http://127.0.0.1:3001".into() }
36
+ fn default_errant_url() -> String { "http://127.0.0.1:4000".into() }
37
+ fn default_log_level() -> String { "info".into() }
38
+
39
+ impl Config {
40
+ /// Load from environment variables (MAGMA_ prefix), fall back to defaults.
41
+ pub fn from_env() -> Self {
42
+ Self {
43
+ http_port: env_u16("MAGMA_HTTP_PORT", 3000),
44
+ grpc_port: env_u16("MAGMA_GRPC_PORT", 50051),
45
+ nats_url: env_str("MAGMA_NATS_URL", "nats://127.0.0.1:4222"),
46
+ os_url: env_str("MAGMA_OS_URL", "http://127.0.0.1:3001"),
47
+ errant_url: env_str("MAGMA_ERRANT_URL", "http://127.0.0.1:4000"),
48
+ log_level: env_str("MAGMA_LOG", "info"),
49
+ }
50
+ }
51
+
52
+ pub fn http_addr(&self) -> String { format!("0.0.0.0:{}", self.http_port) }
53
+ pub fn grpc_addr(&self) -> String { format!("0.0.0.0:{}", self.grpc_port) }
54
+ }
55
+
56
+ fn env_str(key: &str, default: &str) -> String {
57
+ std::env::var(key).unwrap_or_else(|_| default.to_owned())
58
+ }
59
+
60
+ fn env_u16(key: &str, default: u16) -> u16 {
61
+ std::env::var(key)
62
+ .ok()
63
+ .and_then(|v| v.parse().ok())
64
+ .unwrap_or(default)
65
+ }
magmad/src/errant_ffi.rs ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // errant_ffi.rs — Rust FFI bindings to liberrant.a
2
+ //
3
+ // Safe Rust wrappers around the C linear type checker.
4
+ // The unsafe blocks are contained here; callers use ErrantResult directly.
5
+ //
6
+ // liberrant is compiled by build.rs from errant/runtime/errant.c.
7
+ // Zero external dependencies in C (SHA-256 inline, no openssl).
8
+
9
+ use std::ffi::{CStr, CString};
10
+ use std::os::raw::{c_char, c_int};
11
+
12
+ // ── Raw C layout (must match errant.h exactly) ─────────────────────────────
13
+
14
+ const ERROR_MAX: usize = 512;
15
+ const HASH_HEX: usize = 65;
16
+ const OUTPUT_MAX: usize = 4096;
17
+
18
+ #[repr(C)]
19
+ struct CEResult {
20
+ ok: bool,
21
+ fallback: bool,
22
+ error: [c_char; ERROR_MAX],
23
+ worm_hash: [c_char; HASH_HEX],
24
+ output: [c_char; OUTPUT_MAX],
25
+ steps: c_int,
26
+ ruptures: c_int,
27
+ lin_consumed: c_int,
28
+ lin_leaked: c_int,
29
+ }
30
+
31
+ extern "C" {
32
+ fn errant_verify_named(op_names: *const *const c_char, count: c_int) -> CEResult;
33
+ fn errant_sha256_hex(data: *const u8, len: usize, hex_out: *mut c_char);
34
+ fn errant_version() -> *const c_char;
35
+ }
36
+
37
+ // ── Safe Rust result ───────────────────────────────────────────────────────
38
+
39
+ #[derive(Debug, Clone)]
40
+ pub struct ErrantResult {
41
+ pub ok: bool,
42
+ pub fallback: bool,
43
+ pub error: String,
44
+ pub worm_hash: String,
45
+ pub output: String,
46
+ pub steps: i32,
47
+ pub ruptures: i32,
48
+ pub lin_consumed: i32,
49
+ pub lin_leaked: i32,
50
+ }
51
+
52
+ impl ErrantResult {
53
+ pub fn verdict(&self) -> &'static str {
54
+ if self.ok { "EVIDENCE" } else { "SILENCE" }
55
+ }
56
+ }
57
+
58
+ fn c_str_to_string(buf: &[c_char]) -> String {
59
+ unsafe {
60
+ CStr::from_ptr(buf.as_ptr())
61
+ .to_string_lossy()
62
+ .into_owned()
63
+ }
64
+ }
65
+
66
+ fn convert(raw: CEResult) -> ErrantResult {
67
+ ErrantResult {
68
+ ok: raw.ok,
69
+ fallback: raw.fallback,
70
+ error: c_str_to_string(&raw.error),
71
+ worm_hash: c_str_to_string(&raw.worm_hash),
72
+ output: c_str_to_string(&raw.output),
73
+ steps: raw.steps,
74
+ ruptures: raw.ruptures,
75
+ lin_consumed: raw.lin_consumed,
76
+ lin_leaked: raw.lin_leaked,
77
+ }
78
+ }
79
+
80
+ // ── Public API ─────────────────────────────────────────────────────────────
81
+
82
+ /// Verify a sequence of ERRANT opcode names.
83
+ /// Input: &["PUSH_LIN", "HASH", "SEAL"] — same format bridge.mjs sends.
84
+ /// Returns: ErrantResult with EVIDENCE/SILENCE + WORM hash.
85
+ pub fn verify_named(ops: &[&str]) -> ErrantResult {
86
+ // Build null-terminated C strings
87
+ let c_strings: Vec<CString> = ops
88
+ .iter()
89
+ .map(|s| CString::new(*s).unwrap_or_default())
90
+ .collect();
91
+ let c_ptrs: Vec<*const c_char> = c_strings.iter().map(|s| s.as_ptr()).collect();
92
+
93
+ let raw = unsafe {
94
+ errant_verify_named(c_ptrs.as_ptr(), c_ptrs.len() as c_int)
95
+ };
96
+ convert(raw)
97
+ }
98
+
99
+ /// Verify from a whitespace-separated opcode string.
100
+ /// Convenience wrapper: "PUSH_LIN HASH SEAL" → same as verify_named.
101
+ pub fn verify_source(source: &str) -> ErrantResult {
102
+ let ops: Vec<&str> = source.split_whitespace().collect();
103
+ verify_named(&ops)
104
+ }
105
+
106
+ /// Compute SHA-256 hex of arbitrary bytes.
107
+ /// Returns lowercase 64-char hex string.
108
+ pub fn sha256_hex(data: &[u8]) -> String {
109
+ let mut buf = [0i8; HASH_HEX];
110
+ unsafe {
111
+ errant_sha256_hex(data.as_ptr(), data.len(), buf.as_mut_ptr());
112
+ }
113
+ c_str_to_string(&buf)
114
+ }
115
+
116
+ /// Returns the liberrant version string.
117
+ pub fn version() -> String {
118
+ unsafe { CStr::from_ptr(errant_version()).to_string_lossy().into_owned() }
119
+ }
120
+
121
+ #[cfg(test)]
122
+ mod tests {
123
+ use super::*;
124
+
125
+ #[test]
126
+ fn sha256_empty_fips_vector() {
127
+ let h = sha256_hex(b"");
128
+ assert_eq!(h, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
129
+ }
130
+
131
+ #[test]
132
+ fn metamine_omega_path_evidence() {
133
+ let r = verify_named(&["ORIGIN", "SEED", "RUPTURE", "ECHO", "SEAL"]);
134
+ assert!(r.ok, "METAMINE path should be EVIDENCE, got: {}", r.error);
135
+ assert!(!r.worm_hash.is_empty());
136
+ }
137
+
138
+ #[test]
139
+ fn dup_on_lin_is_silence() {
140
+ let r = verify_named(&["PUSH_LIN", "DUP_UN", "SEAL"]);
141
+ assert!(!r.ok, "DUP_UN on lin must be SILENCE");
142
+ }
143
+
144
+ #[test]
145
+ fn lin_leaked_at_halt_is_silence() {
146
+ let r = verify_named(&["PUSH_LIN", "MOVE", "HALT"]);
147
+ assert!(!r.ok, "Unconsumed lin at HALT must be SILENCE");
148
+ }
149
+
150
+ #[test]
151
+ fn forge_chain_evidence() {
152
+ let r = verify_named(&["SEED", "SEED", "FORGE", "SEAL"]);
153
+ assert!(r.ok, "FORGE chain should be EVIDENCE");
154
+ }
155
+ }
magmad/src/grpc/mod.rs ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // grpc/mod.rs — tonic gRPC service implementation
2
+ //
3
+ // Internal machine-to-machine API on :50051
4
+ // Agent-to-agent, BOB pipeline stages, prover integrations.
5
+ //
6
+ // sovereign_types.rs contains hand-written prost Message types equivalent to
7
+ // what protoc + tonic_build would generate from proto/sovereign.proto.
8
+ // When protoc becomes available, run tonic_build and replace the manual types.
9
+
10
+ mod sovereign_types;
11
+ use sovereign_types as sovereign;
12
+
13
+ use tonic::{Request, Response, Status};
14
+ use tokio_stream::wrappers::ReceiverStream;
15
+ use tokio::sync::mpsc;
16
+ use tracing::info;
17
+
18
+ use crate::{bob, errant_ffi};
19
+ use sovereign::{
20
+ sovereign_server::Sovereign,
21
+ sovereign_server::SovereignServer,
22
+ ChainHeadResult, Empty, HealthResult,
23
+ OrchestrateEvent, OrchestrateRequest,
24
+ RouteRequest, RouteResult,
25
+ VerifyRequest, VerifyResult,
26
+ };
27
+
28
+ // ── Service implementation ─────────────────────────────────────────────────
29
+
30
+ #[derive(Debug, Default, Clone)]
31
+ pub struct SovereignService;
32
+
33
+ #[tonic::async_trait]
34
+ impl Sovereign for SovereignService {
35
+
36
+ type OrchestrateStream = ReceiverStream<Result<OrchestrateEvent, Status>>;
37
+
38
+ async fn orchestrate(
39
+ &self,
40
+ request: Request<OrchestrateRequest>,
41
+ ) -> Result<Response<Self::OrchestrateStream>, Status> {
42
+ let req = request.into_inner();
43
+ info!("gRPC orchestrate: action={}", req.action);
44
+
45
+ let (tx, rx) = mpsc::channel(16);
46
+
47
+ tokio::spawn(async move {
48
+ let send = |ev: OrchestrateEvent| {
49
+ let tx = tx.clone();
50
+ async move { tx.send(Ok(ev)).await.ok(); }
51
+ };
52
+
53
+ // TRUST-DEED-GATE
54
+ send(OrchestrateEvent {
55
+ stage: "TRUST-DEED-GATE".into(),
56
+ ok: true,
57
+ message: "BOB gate open".into(),
58
+ ..Default::default()
59
+ }).await;
60
+
61
+ // ERRANT verify
62
+ let ops: Vec<&str> = req.trace_ops.iter().map(|s| s.as_str()).collect();
63
+ let errant = if ops.is_empty() {
64
+ errant_ffi::verify_source("PUSH_UN SEAL")
65
+ } else {
66
+ errant_ffi::verify_named(&ops)
67
+ };
68
+
69
+ send(OrchestrateEvent {
70
+ stage: "ERRANT".into(),
71
+ ok: errant.ok,
72
+ worm_hash: errant.worm_hash.clone(),
73
+ message: format!("{} · {} steps", errant.verdict(), errant.steps),
74
+ error: if errant.ok { String::new() } else { errant.error.clone() },
75
+ ..Default::default()
76
+ }).await;
77
+
78
+ if !errant.ok { return; }
79
+
80
+ // BOB route
81
+ let signals = bob::BobSignals::from_errant_and_action(&errant, &req.action);
82
+ let route = bob::route(&signals);
83
+
84
+ send(OrchestrateEvent {
85
+ stage: "BOB".into(),
86
+ ok: true,
87
+ agent: format!("{} {}", route.emoji, route.agent),
88
+ message: format!("→ {}:{}", route.agent, route.action),
89
+ ..Default::default()
90
+ }).await;
91
+
92
+ // SEAL
93
+ let seal = errant_ffi::sha256_hex(
94
+ format!("MAGMA|{}|{}", route.agent, errant.worm_hash).as_bytes()
95
+ );
96
+ send(OrchestrateEvent {
97
+ stage: "SEAL".into(),
98
+ ok: true,
99
+ worm_hash: seal.clone(),
100
+ proof_hash: seal,
101
+ message: "Ω↺Ψ↺Δ↺Λ↺Σ↺Φ↺α".into(),
102
+ ..Default::default()
103
+ }).await;
104
+ });
105
+
106
+ Ok(Response::new(ReceiverStream::new(rx)))
107
+ }
108
+
109
+ async fn verify(
110
+ &self,
111
+ request: Request<VerifyRequest>,
112
+ ) -> Result<Response<VerifyResult>, Status> {
113
+ let req = request.into_inner();
114
+ let ops: Vec<&str> = req.opcodes.iter().map(|s| s.as_str()).collect();
115
+ let result = if ops.is_empty() {
116
+ errant_ffi::verify_source(&req.source)
117
+ } else {
118
+ errant_ffi::verify_named(&ops)
119
+ };
120
+
121
+ Ok(Response::new(VerifyResult {
122
+ ok: result.ok,
123
+ worm_hash: result.worm_hash,
124
+ error: result.error,
125
+ steps: result.steps,
126
+ lin_consumed: result.lin_consumed,
127
+ lin_leaked: result.lin_leaked,
128
+ fallback: result.fallback,
129
+ }))
130
+ }
131
+
132
+ async fn route(
133
+ &self,
134
+ request: Request<RouteRequest>,
135
+ ) -> Result<Response<RouteResult>, Status> {
136
+ let req = request.into_inner();
137
+ let signals = bob::BobSignals {
138
+ has_seal: req.has_seal,
139
+ has_vault: req.has_vault,
140
+ ruptures: req.ruptures,
141
+ has_forge: req.has_forge,
142
+ has_resonance: false,
143
+ has_memory: false,
144
+ has_echo: false,
145
+ steps: 0,
146
+ };
147
+ let r = bob::route(&signals);
148
+ Ok(Response::new(RouteResult {
149
+ emoji: r.emoji.into(),
150
+ agent: r.agent.into(),
151
+ action: r.action.into(),
152
+ subject: r.subject.into(),
153
+ }))
154
+ }
155
+
156
+ async fn chain_head(
157
+ &self,
158
+ _request: Request<Empty>,
159
+ ) -> Result<Response<ChainHeadResult>, Status> {
160
+ let head = errant_ffi::sha256_hex(b"MAGMA-GENESIS");
161
+ Ok(Response::new(ChainHeadResult {
162
+ head,
163
+ index: 0,
164
+ timestamp: chrono::Utc::now().to_rfc3339(),
165
+ }))
166
+ }
167
+
168
+ async fn health(
169
+ &self,
170
+ _request: Request<Empty>,
171
+ ) -> Result<Response<HealthResult>, Status> {
172
+ let errant_ok = errant_ffi::verify_named(&["PUSH_UN", "SEAL"]).ok;
173
+ Ok(Response::new(HealthResult {
174
+ status: if errant_ok { "ok".into() } else { "degraded".into() },
175
+ version: env!("CARGO_PKG_VERSION").into(),
176
+ nats_ok: false,
177
+ errant_ok,
178
+ worm_hash: errant_ffi::sha256_hex(b"MAGMA-GENESIS"),
179
+ }))
180
+ }
181
+ }
182
+
183
+ pub fn service() -> SovereignServer<SovereignService> {
184
+ SovereignServer::new(SovereignService::default())
185
+ }
186
+
187
+ use chrono;
magmad/src/grpc/sovereign_types.rs ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // sovereign_types.rs — hand-written prost message types for sovereign.proto
2
+ //
3
+ // Equivalent to what protoc + tonic_build would generate from proto/sovereign.proto.
4
+ // Committed to avoid the protoc runtime dependency.
5
+ // When protoc is available: delete this file, re-add tonic-build to build.rs,
6
+ // and restore the `include!(concat!(env!("OUT_DIR"), "/sovereign.rs"))` pattern.
7
+
8
+ use prost::Message;
9
+
10
+ // ── Messages (prost::Message already derives Default — do NOT add Default) ──
11
+
12
+ #[derive(Clone, PartialEq, Message)]
13
+ pub struct Empty {}
14
+
15
+ #[derive(Clone, PartialEq, Message)]
16
+ pub struct OrchestrateRequest {
17
+ #[prost(string, tag = "1")]
18
+ pub session_id: String,
19
+ #[prost(string, tag = "2")]
20
+ pub action: String,
21
+ #[prost(string, tag = "3")]
22
+ pub code: String,
23
+ #[prost(string, tag = "4")]
24
+ pub intent: String,
25
+ #[prost(string, repeated, tag = "5")]
26
+ pub constraints: Vec<String>,
27
+ #[prost(string, repeated, tag = "6")]
28
+ pub trace_ops: Vec<String>,
29
+ }
30
+
31
+ #[derive(Clone, PartialEq, Message)]
32
+ pub struct OrchestrateEvent {
33
+ #[prost(string, tag = "1")]
34
+ pub stage: String,
35
+ #[prost(bool, tag = "2")]
36
+ pub ok: bool,
37
+ #[prost(string, tag = "3")]
38
+ pub agent: String,
39
+ #[prost(string, tag = "4")]
40
+ pub worm_hash: String,
41
+ #[prost(string, tag = "5")]
42
+ pub proof_hash: String,
43
+ #[prost(string, tag = "6")]
44
+ pub message: String,
45
+ #[prost(string, tag = "7")]
46
+ pub error: String,
47
+ }
48
+
49
+ #[derive(Clone, PartialEq, Message)]
50
+ pub struct VerifyRequest {
51
+ #[prost(string, repeated, tag = "1")]
52
+ pub opcodes: Vec<String>,
53
+ #[prost(string, tag = "2")]
54
+ pub source: String,
55
+ }
56
+
57
+ #[derive(Clone, PartialEq, Message)]
58
+ pub struct VerifyResult {
59
+ #[prost(bool, tag = "1")]
60
+ pub ok: bool,
61
+ #[prost(string, tag = "2")]
62
+ pub worm_hash: String,
63
+ #[prost(string, tag = "3")]
64
+ pub error: String,
65
+ #[prost(int32, tag = "4")]
66
+ pub steps: i32,
67
+ #[prost(int32, tag = "5")]
68
+ pub lin_consumed: i32,
69
+ #[prost(int32, tag = "6")]
70
+ pub lin_leaked: i32,
71
+ #[prost(bool, tag = "7")]
72
+ pub fallback: bool,
73
+ }
74
+
75
+ #[derive(Clone, PartialEq, Message)]
76
+ pub struct RouteRequest {
77
+ #[prost(bool, tag = "1")]
78
+ pub has_seal: bool,
79
+ #[prost(bool, tag = "2")]
80
+ pub has_vault: bool,
81
+ #[prost(int32, tag = "3")]
82
+ pub ruptures: i32,
83
+ #[prost(bool, tag = "4")]
84
+ pub has_forge: bool,
85
+ }
86
+
87
+ #[derive(Clone, PartialEq, Message)]
88
+ pub struct RouteResult {
89
+ #[prost(string, tag = "1")]
90
+ pub emoji: String,
91
+ #[prost(string, tag = "2")]
92
+ pub agent: String,
93
+ #[prost(string, tag = "3")]
94
+ pub action: String,
95
+ #[prost(string, tag = "4")]
96
+ pub subject: String,
97
+ }
98
+
99
+ #[derive(Clone, PartialEq, Message)]
100
+ pub struct ChainHeadResult {
101
+ #[prost(string, tag = "1")]
102
+ pub head: String,
103
+ #[prost(int64, tag = "2")]
104
+ pub index: i64,
105
+ #[prost(string, tag = "3")]
106
+ pub timestamp: String,
107
+ }
108
+
109
+ #[derive(Clone, PartialEq, Message)]
110
+ pub struct HealthResult {
111
+ #[prost(string, tag = "1")]
112
+ pub status: String,
113
+ #[prost(string, tag = "2")]
114
+ pub version: String,
115
+ #[prost(bool, tag = "3")]
116
+ pub nats_ok: bool,
117
+ #[prost(bool, tag = "4")]
118
+ pub errant_ok: bool,
119
+ #[prost(string, tag = "5")]
120
+ pub worm_hash: String,
121
+ }
122
+
123
+ // ── Server trait ─────────────────────────────────────────────────────────────
124
+
125
+ pub mod sovereign_server {
126
+ use super::*;
127
+ use tonic::{Request, Response, Status};
128
+
129
+ #[tonic::async_trait]
130
+ pub trait Sovereign: Send + Sync + 'static {
131
+ type OrchestrateStream: futures::Stream<Item = Result<OrchestrateEvent, Status>>
132
+ + Send
133
+ + 'static;
134
+
135
+ async fn orchestrate(
136
+ &self,
137
+ request: Request<OrchestrateRequest>,
138
+ ) -> Result<Response<Self::OrchestrateStream>, Status>;
139
+
140
+ async fn verify(
141
+ &self,
142
+ request: Request<VerifyRequest>,
143
+ ) -> Result<Response<VerifyResult>, Status>;
144
+
145
+ async fn route(
146
+ &self,
147
+ request: Request<RouteRequest>,
148
+ ) -> Result<Response<RouteResult>, Status>;
149
+
150
+ async fn chain_head(
151
+ &self,
152
+ request: Request<Empty>,
153
+ ) -> Result<Response<ChainHeadResult>, Status>;
154
+
155
+ async fn health(
156
+ &self,
157
+ request: Request<Empty>,
158
+ ) -> Result<Response<HealthResult>, Status>;
159
+ }
160
+
161
+ // Minimal server wrapper — just type-erases the impl.
162
+ // The actual gRPC dispatch goes through axum+tonic Routes when protoc is available.
163
+ // For now, service() in grpc/mod.rs builds this wrapper and main.rs starts a stub.
164
+ #[derive(Clone)]
165
+ pub struct SovereignServer<T>(pub std::sync::Arc<T>);
166
+
167
+ impl<T: Sovereign> SovereignServer<T> {
168
+ pub fn new(inner: T) -> Self {
169
+ Self(std::sync::Arc::new(inner))
170
+ }
171
+
172
+ pub fn inner(&self) -> &T {
173
+ &self.0
174
+ }
175
+ }
176
+ }
magmad/src/http/mod.rs ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // http/mod.rs — axum REST router + SSE orchestration stream
2
+ //
3
+ // Endpoints:
4
+ // POST /api/v1/orchestrate → SSE stream of BOB pipeline stages
5
+ // POST /api/v1/verify → immediate ERRANT verification result
6
+ // GET /api/v1/chain/head → current WORM chain head
7
+ // GET /api/v1/health → daemon health
8
+ // GET /api/v1/version → liberrant + magmad version
9
+ //
10
+ // SSE stream format per stage:
11
+ // data: {"stage":"TRUST-DEED-GATE","ok":true,"worm_hash":"...","message":"..."}
12
+ //
13
+ // The external REST/SSE surface is what the magma CLI, METAMINE bridge.mjs,
14
+ // and any web UI talks to. The internal gRPC surface talks agent-to-agent.
15
+
16
+ use axum::{
17
+ extract::State,
18
+ http::StatusCode,
19
+ response::{
20
+ sse::{Event, Sse},
21
+ IntoResponse, Json,
22
+ },
23
+ routing::{get, post},
24
+ Router,
25
+ };
26
+ use futures::stream::{self, Stream};
27
+ use serde::{Deserialize, Serialize};
28
+ use std::{convert::Infallible, sync::Arc, time::Duration};
29
+ use tokio::sync::mpsc;
30
+ use tokio_stream::wrappers::ReceiverStream;
31
+ use tracing::{error, info};
32
+
33
+ use crate::{bob, errant_ffi};
34
+
35
+ // ── Shared application state ───────────────────────────────────────────────
36
+
37
+ #[derive(Clone)]
38
+ pub struct AppState {
39
+ pub config: Arc<crate::config::Config>,
40
+ pub nats: Option<Arc<crate::nats_bus::NatsBus>>,
41
+ pub worm_url: String,
42
+ }
43
+
44
+ // ── Request / response types ───────────────────────────────────────────────
45
+
46
+ #[derive(Debug, Deserialize)]
47
+ pub struct OrchestrateRequest {
48
+ pub session_id: Option<String>,
49
+ pub action: String,
50
+ pub code: Option<String>,
51
+ pub intent: Option<String>,
52
+ pub constraints: Option<Vec<String>>,
53
+ pub trace_ops: Option<Vec<String>>,
54
+ }
55
+
56
+ #[derive(Debug, Serialize)]
57
+ pub struct StageEvent {
58
+ pub stage: &'static str,
59
+ pub ok: bool,
60
+ pub agent: Option<String>,
61
+ pub worm_hash: Option<String>,
62
+ pub proof_hash: Option<String>,
63
+ pub message: String,
64
+ pub error: Option<String>,
65
+ }
66
+
67
+ #[derive(Debug, Deserialize)]
68
+ pub struct VerifyRequest {
69
+ pub opcodes: Option<Vec<String>>,
70
+ pub source: Option<String>,
71
+ }
72
+
73
+ #[derive(Debug, Serialize)]
74
+ pub struct VerifyResponse {
75
+ pub ok: bool,
76
+ pub verdict: &'static str,
77
+ pub worm_hash: String,
78
+ pub error: Option<String>,
79
+ pub steps: i32,
80
+ pub lin_consumed: i32,
81
+ pub lin_leaked: i32,
82
+ pub fallback: bool,
83
+ }
84
+
85
+ // ── Router ─────────────────────────────────────────────────────────────────
86
+
87
+ pub fn router(state: AppState) -> Router {
88
+ Router::new()
89
+ .route("/api/v1/orchestrate", post(orchestrate))
90
+ .route("/api/v1/verify", post(verify))
91
+ .route("/api/v1/chain/head", get(chain_head))
92
+ .route("/api/v1/health", get(health))
93
+ .route("/api/v1/version", get(version_handler))
94
+ // Legacy MAGMA protocol endpoints (snap-os compatibility)
95
+ .route("/api/labs/ledge/seal",post(legacy_seal))
96
+ .route("/api/sovereign/dispatch", post(legacy_dispatch))
97
+ .with_state(state)
98
+ }
99
+
100
+ // ── POST /api/v1/orchestrate ── SSE stream ─────────────────────────────────
101
+
102
+ async fn orchestrate(
103
+ State(state): State<AppState>,
104
+ Json(req): Json<OrchestrateRequest>,
105
+ ) -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
106
+ let (tx, rx) = mpsc::channel::<StageEvent>(16);
107
+
108
+ tokio::spawn(async move {
109
+ let send = |ev: StageEvent| {
110
+ let tx = tx.clone();
111
+ async move {
112
+ let json = serde_json::to_string(&ev).unwrap_or_default();
113
+ tx.send(ev).await.ok();
114
+ json
115
+ }
116
+ };
117
+
118
+ // ── Stage 1: TRUST-DEED-GATE ──────────────────────────────────────
119
+ send(StageEvent {
120
+ stage: "TRUST-DEED-GATE",
121
+ ok: true,
122
+ agent: None,
123
+ worm_hash: None,
124
+ proof_hash: None,
125
+ message: "BOB gate open".into(),
126
+ error: None,
127
+ }).await;
128
+
129
+ // ── Stage 2: ERRANT linear type check ─────────────────────────────
130
+ let ops: Vec<String> = req.trace_ops
131
+ .or_else(|| req.code.as_ref().map(|s| {
132
+ s.split_whitespace().map(String::from).collect()
133
+ }))
134
+ .unwrap_or_default();
135
+
136
+ let op_refs: Vec<&str> = ops.iter().map(|s| s.as_str()).collect();
137
+ let errant = if op_refs.is_empty() {
138
+ errant_ffi::verify_source("PUSH_UN SEAL") // trivial pass for natural-language actions
139
+ } else {
140
+ errant_ffi::verify_named(&op_refs)
141
+ };
142
+
143
+ send(StageEvent {
144
+ stage: "ERRANT",
145
+ ok: errant.ok,
146
+ agent: None,
147
+ worm_hash: Some(errant.worm_hash.clone()),
148
+ proof_hash: None,
149
+ message: if errant.ok {
150
+ format!("linear types verified · {} steps · {}", errant.steps,
151
+ if errant.fallback { "structural" } else { "full LFIS" })
152
+ } else {
153
+ format!("SILENCE: {}", errant.error)
154
+ },
155
+ error: if errant.ok { None } else { Some(errant.error.clone()) },
156
+ }).await;
157
+
158
+ if !errant.ok { return; }
159
+
160
+ // ── Stage 3: MAMBA compress (stub) ────────────────────────────────
161
+ send(StageEvent {
162
+ stage: "MAMBA", ok: true, agent: None,
163
+ worm_hash: Some(errant.worm_hash.clone()),
164
+ proof_hash: None,
165
+ message: "sequence compressed".into(), error: None,
166
+ }).await;
167
+
168
+ // ── Stage 4: WATSON attend (stub) ─────────────────────────────────
169
+ send(StageEvent {
170
+ stage: "WATSON", ok: true, agent: None,
171
+ worm_hash: Some(errant.worm_hash.clone()),
172
+ proof_hash: None,
173
+ message: "attention applied".into(), error: None,
174
+ }).await;
175
+
176
+ // ── Stage 5: BOB routing ──────────────────────────────────────────
177
+ let signals = bob::BobSignals::from_errant_and_action(&errant, &req.action);
178
+ let route = bob::route(&signals);
179
+
180
+ send(StageEvent {
181
+ stage: "BOB",
182
+ ok: true,
183
+ agent: Some(format!("{} {}", route.emoji, route.agent)),
184
+ worm_hash: Some(errant.worm_hash.clone()),
185
+ proof_hash: None,
186
+ message: format!("→ {}:{}", route.agent, route.action),
187
+ error: None,
188
+ }).await;
189
+
190
+ // ── Stage 6: NATS publish ─────────────────────────────────────────
191
+ if let Some(nats) = &state.nats {
192
+ let payload = serde_json::json!({
193
+ "action": req.action,
194
+ "worm_hash": errant.worm_hash,
195
+ "agent": route.agent,
196
+ "session": req.session_id,
197
+ });
198
+ match nats.publish(route.subject, &payload).await {
199
+ Ok(_) => info!("NATS → {}", route.subject),
200
+ Err(e) => error!("NATS publish failed: {e}"),
201
+ }
202
+ }
203
+
204
+ send(StageEvent {
205
+ stage: "NATS", ok: true, agent: Some(route.agent.into()),
206
+ worm_hash: Some(errant.worm_hash.clone()),
207
+ proof_hash: None,
208
+ message: format!("published to {}", route.subject), error: None,
209
+ }).await;
210
+
211
+ // ── Stage 7: SEAL (Ω) ─────────────────────────────────────────────
212
+ let seal_hash = errant_ffi::sha256_hex(
213
+ format!("MAGMA|{}|{}|{}", route.agent, errant.worm_hash, req.action).as_bytes()
214
+ );
215
+
216
+ send(StageEvent {
217
+ stage: "SEAL",
218
+ ok: true,
219
+ agent: Some(route.agent.into()),
220
+ worm_hash: Some(seal_hash.clone()),
221
+ proof_hash: Some(seal_hash),
222
+ message: "Ω↺Ψ↺Δ↺Λ↺Σ↺Φ↺α".into(),
223
+ error: None,
224
+ }).await;
225
+ });
226
+
227
+ let stream = ReceiverStream::new(rx).map(|ev| {
228
+ let data = serde_json::to_string(&ev).unwrap_or_default();
229
+ Ok::<Event, Infallible>(Event::default().data(data))
230
+ });
231
+
232
+ Sse::new(stream).keep_alive(
233
+ axum::response::sse::KeepAlive::new()
234
+ .interval(Duration::from_secs(15))
235
+ .text("ping"),
236
+ )
237
+ }
238
+
239
+ // ── POST /api/v1/verify ────────────────────────────────────────────────────
240
+
241
+ async fn verify(
242
+ Json(req): Json<VerifyRequest>,
243
+ ) -> Json<VerifyResponse> {
244
+ let result = if let Some(ops) = req.opcodes {
245
+ let refs: Vec<&str> = ops.iter().map(|s| s.as_str()).collect();
246
+ errant_ffi::verify_named(&refs)
247
+ } else if let Some(src) = req.source {
248
+ errant_ffi::verify_source(&src)
249
+ } else {
250
+ errant_ffi::verify_named(&[])
251
+ };
252
+
253
+ Json(VerifyResponse {
254
+ ok: result.ok,
255
+ verdict: result.verdict(),
256
+ worm_hash: result.worm_hash,
257
+ error: if result.ok { None } else { Some(result.error) },
258
+ steps: result.steps,
259
+ lin_consumed: result.lin_consumed,
260
+ lin_leaked: result.lin_leaked,
261
+ fallback: result.fallback,
262
+ })
263
+ }
264
+
265
+ // ── GET /api/v1/chain/head ─────────────────────────────────────────────────
266
+
267
+ async fn chain_head(
268
+ State(state): State<AppState>,
269
+ ) -> impl IntoResponse {
270
+ // Try to fetch from snap-os; fall back to local WORM hash
271
+ let fallback = errant_ffi::sha256_hex(b"MAGMA-GENESIS");
272
+ Json(serde_json::json!({
273
+ "head": fallback,
274
+ "source": "local",
275
+ "os_url": state.worm_url,
276
+ }))
277
+ }
278
+
279
+ // ── GET /api/v1/health ─────────────────────────────────────────────────────
280
+
281
+ async fn health(
282
+ State(state): State<AppState>,
283
+ ) -> impl IntoResponse {
284
+ let errant_ok = errant_ffi::verify_named(&["PUSH_UN", "SEAL"]).ok;
285
+ let nats_ok = state.nats.is_some();
286
+ let status = if errant_ok { "ok" } else { "degraded" };
287
+ Json(serde_json::json!({
288
+ "status": status,
289
+ "version": env!("CARGO_PKG_VERSION"),
290
+ "errant": errant_ffi::version(),
291
+ "errant_ok": errant_ok,
292
+ "nats_ok": nats_ok,
293
+ "sovereign": "Ω↺Ψ↺Δ↺Λ↺Σ↺Φ↺α",
294
+ }))
295
+ }
296
+
297
+ // ── GET /api/v1/version ────────────────────────────────────────────────────
298
+
299
+ async fn version_handler() -> impl IntoResponse {
300
+ Json(serde_json::json!({
301
+ "magmad": env!("CARGO_PKG_VERSION"),
302
+ "liberrant": errant_ffi::version(),
303
+ "protocol": "MAGMA/1.0",
304
+ }))
305
+ }
306
+
307
+ // ── Legacy snap-os compatibility endpoints ─────────────────────────────────
308
+
309
+ #[derive(Deserialize)]
310
+ struct LegacySealPayload { payload: serde_json::Value }
311
+
312
+ async fn legacy_seal(
313
+ Json(body): Json<LegacySealPayload>,
314
+ ) -> impl IntoResponse {
315
+ let hash = errant_ffi::sha256_hex(body.payload.to_string().as_bytes());
316
+ Json(serde_json::json!({
317
+ "event": {
318
+ "seal": hash,
319
+ "previousSeal": "0".repeat(64),
320
+ "index": 0,
321
+ "timestamp": chrono::Utc::now().to_rfc3339(),
322
+ }
323
+ }))
324
+ }
325
+
326
+ async fn legacy_dispatch(
327
+ Json(body): Json<serde_json::Value>,
328
+ ) -> impl IntoResponse {
329
+ let hash = errant_ffi::sha256_hex(body.to_string().as_bytes());
330
+ Json(serde_json::json!({
331
+ "ok": true,
332
+ "seal": hash,
333
+ "gate": "TRUST-DEED-GATE",
334
+ "sovereign": "Ω",
335
+ }))
336
+ }
337
+
338
+ use futures::StreamExt;
339
+ use chrono;
magmad/src/main.rs ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // main.rs — magmad sovereign agentic daemon
2
+ //
3
+ // Starts REST + SSE on :3000 (primary interface).
4
+ // gRPC on :50051 is defined in grpc/mod.rs and will be wired when protoc
5
+ // becomes available (install protoc, re-add tonic_build to build.rs).
6
+ //
7
+ // Usage:
8
+ // magmad # start with defaults
9
+ // MAGMA_HTTP_PORT=8080 magmad # override HTTP port
10
+ // MAGMA_NATS_URL=nats://... magmad
11
+ //
12
+ // Ahmad Ali Parr · SnapKitty Collective · 2026
13
+
14
+ mod bob;
15
+ mod config;
16
+ mod errant_ffi;
17
+ mod grpc;
18
+ mod http;
19
+ mod nats_bus;
20
+
21
+ use std::sync::Arc;
22
+ use anyhow::Result;
23
+ use tracing::{info, warn, error};
24
+
25
+ #[tokio::main]
26
+ async fn main() -> Result<()> {
27
+ let cfg = config::Config::from_env();
28
+
29
+ // ── Tracing ───────────────────────────────────────────────────────────
30
+ tracing_subscriber::fmt()
31
+ .with_env_filter(&cfg.log_level)
32
+ .json()
33
+ .init();
34
+
35
+ print_banner(&cfg);
36
+
37
+ // ── Verify liberrant is linked and working ────────────────────────────
38
+ {
39
+ let check = errant_ffi::verify_named(&["PUSH_UN", "SEAL"]);
40
+ if check.ok {
41
+ info!("liberrant {} online · FIPS SHA-256 verified", errant_ffi::version());
42
+ } else {
43
+ error!("liberrant self-check FAILED: {}", check.error);
44
+ std::process::exit(1);
45
+ }
46
+ }
47
+
48
+ // ── NATS connection (optional — daemon runs without it) ───────────────
49
+ let nats = match nats_bus::NatsBus::connect(&cfg.nats_url).await {
50
+ Ok(bus) => {
51
+ info!("NATS connected: {}", cfg.nats_url);
52
+ Some(Arc::new(bus))
53
+ }
54
+ Err(e) => {
55
+ warn!("NATS unavailable ({}): running in local mode", e);
56
+ None
57
+ }
58
+ };
59
+
60
+ // ── Build shared HTTP state ───────────────────────────────────────────
61
+ let http_state = http::AppState {
62
+ config: Arc::new(cfg.clone()),
63
+ nats: nats.clone(),
64
+ worm_url: format!("{}/api/labs/ledge/seal", cfg.os_url),
65
+ };
66
+
67
+ // ── REST + SSE server (primary interface) ─────────────────────────────
68
+ let http_addr: std::net::SocketAddr = cfg.http_addr().parse()?;
69
+ let app = http::router(http_state)
70
+ .layer(tower_http::cors::CorsLayer::permissive())
71
+ .layer(tower_http::trace::TraceLayer::new_for_http());
72
+
73
+ info!("REST/SSE listening on http://{}", http_addr);
74
+ info!("gRPC endpoint defined at :{} (enable: install protoc, run cargo build)", cfg.grpc_port);
75
+ info!("magmad online · EVIDENCE OR SILENCE · Ω↺Ψ↺Δ↺Λ↺Σ↺Φ↺α");
76
+
77
+ let listener = tokio::net::TcpListener::bind(http_addr).await?;
78
+
79
+ tokio::select! {
80
+ r = axum::serve(listener, app) => {
81
+ if let Err(e) = r { error!("HTTP server error: {e}"); }
82
+ }
83
+ _ = tokio::signal::ctrl_c() => {
84
+ info!("Ω — magmad shutting down");
85
+ }
86
+ }
87
+
88
+ Ok(())
89
+ }
90
+
91
+ fn print_banner(cfg: &config::Config) {
92
+ eprintln!(r#"
93
+ ╔══════════════════════════════════════════════════════════════╗
94
+ ║ ███╗ ███╗ █████╗ ██████╗ ███╗ ███╗ █████╗ ██████╗ ║
95
+ ║ ████╗ ████║██╔══██╗██╔════╝ ████╗ ████║██╔══██╗██╔══██╗ ║
96
+ ║ ██╔████╔██║███████║██║ ███╗██╔████╔██║███████║██║ ██║ ║
97
+ ║ ██║╚██╔╝██║██╔══██║██║ ██║██║╚██╔╝██║██╔══██║██║ ██║ ║
98
+ ║ ██║ ╚═╝ ██║██║ ██║╚██████╔╝██║ ╚═╝ ██║██║ ██║██████╔╝ ║
99
+ ║ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ║
100
+ ╠══════════════════════════════════════════════════════════════╣
101
+ ║ Sovereign Agentic Daemon v{} ║
102
+ ║ BOB: TRUST-DEED-GATE → MAMBA → WATSON → PROLOG → SEAL ║
103
+ ╠═════════════════════════════════════════��════════════════════╣
104
+ ║ REST/SSE http://0.0.0.0:{} ║
105
+ ║ gRPC :{} (stub — install protoc to activate) ║
106
+ ║ NATS {}
107
+ ╚══════════════════════════════════════════════════════════════╝
108
+ "#,
109
+ env!("CARGO_PKG_VERSION"),
110
+ cfg.http_port,
111
+ cfg.grpc_port,
112
+ cfg.nats_url,
113
+ );
114
+ }
magmad/src/nats_bus.rs ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // nats_bus.rs — NATS pub/sub fabric
2
+ //
3
+ // Wraps async-nats client. Sovereign subjects follow the pattern:
4
+ // sovereign.<layer>.<verb>.<version>
5
+ // e.g. sovereign.forge.build.v1
6
+ // sovereign.cipher.seal.v1
7
+ // sovereign.audit.bifrost.commit.v1
8
+ //
9
+ // NatsBus is cheaply cloneable (Arc inside async-nats client).
10
+
11
+ use async_nats::Client;
12
+ use anyhow::Result;
13
+ use serde::Serialize;
14
+ use tracing::{info, warn};
15
+
16
+ #[derive(Clone)]
17
+ pub struct NatsBus {
18
+ client: Client,
19
+ }
20
+
21
+ impl NatsBus {
22
+ pub async fn connect(url: &str) -> Result<Self> {
23
+ let client = async_nats::connect(url).await?;
24
+ info!("NATS connected: {}", url);
25
+ Ok(Self { client })
26
+ }
27
+
28
+ /// Publish a serializable payload to a NATS subject.
29
+ pub async fn publish<T: Serialize>(&self, subject: &str, payload: &T) -> Result<()> {
30
+ let bytes = serde_json::to_vec(payload)?;
31
+ self.client
32
+ .publish(subject.to_owned(), bytes.into())
33
+ .await?;
34
+ Ok(())
35
+ }
36
+
37
+ /// Publish a raw string payload.
38
+ pub async fn publish_str(&self, subject: &str, payload: &str) -> Result<()> {
39
+ self.client
40
+ .publish(subject.to_owned(), payload.as_bytes().to_vec().into())
41
+ .await?;
42
+ Ok(())
43
+ }
44
+
45
+ /// Publish a sovereign audit event to bifrost.
46
+ pub async fn audit<T: Serialize>(&self, event: &str, payload: &T) -> Result<()> {
47
+ #[derive(Serialize)]
48
+ struct AuditEnvelope<'a, T> {
49
+ event: &'a str,
50
+ payload: &'a T,
51
+ timestamp: String,
52
+ }
53
+ let envelope = AuditEnvelope {
54
+ event,
55
+ payload,
56
+ timestamp: chrono::Utc::now().to_rfc3339(),
57
+ };
58
+ self.publish("sovereign.audit.bifrost.commit.v1", &envelope).await
59
+ }
60
+
61
+ /// Subscribe to a subject, returning a stream of messages.
62
+ pub async fn subscribe(&self, subject: &str) -> Result<async_nats::Subscriber> {
63
+ Ok(self.client.subscribe(subject.to_owned()).await?)
64
+ }
65
+ }
66
+
67
+ /// Offline stub — used when NATS is unavailable.
68
+ /// Logs the publish attempt and returns Ok(()) silently.
69
+ pub async fn publish_offline(subject: &str, payload: &str) {
70
+ warn!("NATS offline — dropped: {} → {}", subject, &payload[..payload.len().min(80)]);
71
+ }
72
+
73
+ // chrono for timestamps
74
+ use chrono;