use std::env;
use std::fs;
use std::io::{self, BufRead};
use std::path::{Path, PathBuf};
use std::process::Command;
use chrono::Utc;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use clap::{Parser, Subcommand};
use coreason_meta_engineering_rust::orchestrator::{scaffold_ast, record_cla_acceptance, enforce_cla_gate};
use coreason_meta_engineering_rust::cross_language::{generate_typescript_interface, generate_wit_record};
use coreason_meta_engineering_rust::signer::resolve_signing_key;
use coreason_meta_engineering_rust::hasher::compute_canonical_hash;
#[derive(Parser)]
#[command(name = "coreason-meta-mcp")]
#[command(about = "CoReason Agentic Forge & AST Manipulation Layer", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
AcceptCla {
#[arg(long)]
operator_id: Option<String>,
},
Run,
}
#[derive(Serialize, Deserialize, Debug)]
struct JsonRpcRequest {
jsonrpc: String,
method: String,
#[serde(default)]
params: Value,
id: Option<Value>,
}
#[derive(Serialize, Debug)]
struct JsonRpcResponse {
jsonrpc: String,
#[serde(skip_serializing_if = "Option::is_none")]
result: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<Value>,
id: Option<Value>,
}
#[tokio::main]
async fn main() {
env_logger::Builder::new()
.filter_level(log::LevelFilter::Info)
.target(env_logger::Target::Stderr)
.init();
let cli = Cli::parse();
match cli.command {
Some(Commands::AcceptCla { operator_id }) => {
let op_id = operator_id.unwrap_or_else(|| {
env::var("USER")
.or_else(|_| env::var("USERNAME"))
.unwrap_or_else(|_| "unknown".to_string())
});
match record_cla_acceptance(&op_id) {
Ok(path) => println!("Success: CLA acceptance recorded at {:?}", path),
Err(e) => eprintln!("Error: Failed to record CLA acceptance: {}", e),
}
}
None | Some(Commands::Run) => {
log::info!("Starting CoReason Agentic Forge MCP Server...");
run_mcp_server().await;
}
}
}
async fn run_mcp_server() {
let stdin = io::stdin();
let reader = stdin.lock();
for line_result in reader.lines() {
let line = match line_result {
Ok(l) => l,
Err(e) => {
log::error!("Error reading from stdin: {}", e);
break;
}
};
if line.trim().is_empty() {
continue;
}
let request: JsonRpcRequest = match serde_json::from_str(&line) {
Ok(req) => req,
Err(e) => {
log::error!("Failed to parse JSON-RPC request: {}", e);
let err_resp = JsonRpcResponse {
jsonrpc: "2.0".to_string(),
result: None,
error: Some(json!({
"code": -32700,
"message": format!("Parse error: {}", e)
})),
id: None,
};
print_response(&err_resp);
continue;
}
};
let response = handle_request(request).await;
print_response(&response);
}
}
fn print_response(response: &JsonRpcResponse) {
if let Ok(serialized) = serde_json::to_string(response) {
println!("{}", serialized);
}
}
async fn handle_request(request: JsonRpcRequest) -> JsonRpcResponse {
let id = request.id.clone();
match request.method.as_str() {
"initialize" => JsonRpcResponse {
jsonrpc: "2.0".to_string(),
result: Some(json!({
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "CoReason Agentic Forge",
"version": "0.1.0"
}
})),
error: None,
id,
},
"notifications/initialized" => JsonRpcResponse {
jsonrpc: "2.0".to_string(),
result: None,
error: None,
id: None,
},
"tools/list" => JsonRpcResponse {
jsonrpc: "2.0".to_string(),
result: Some(json!({
"tools": list_tools()
})),
error: None,
id,
},
"tools/call" => {
let name = request.params["name"].as_str().unwrap_or("");
let arguments = &request.params["arguments"];
match call_tool(name, arguments).await {
Ok(res) => JsonRpcResponse {
jsonrpc: "2.0".to_string(),
result: Some(json!({
"content": [
{
"type": "text",
"text": res
}
]
})),
error: None,
id,
},
Err(e) => JsonRpcResponse {
jsonrpc: "2.0".to_string(),
result: None,
error: Some(json!({
"code": -32000,
"message": e
})),
id,
},
}
}
_ => JsonRpcResponse {
jsonrpc: "2.0".to_string(),
result: None,
error: Some(json!({
"code": -32601,
"message": format!("Method not found: {}", request.method)
})),
id,
},
}
}
fn list_tools() -> Value {
json!([
{
"name": "scaffold_manifest_state",
"description": "Scaffolds a new model by orchestrating LLM agents bounded by the JSON schema.",
"inputSchema": {
"type": "object",
"properties": {
"state_name": { "type": "string" },
"geometric_schema": { "type": "object" },
"target_file_path": { "type": "string" },
"action_space_id": { "type": "string" },
"base_class": { "type": "string", "default": "CoreasonBaseState" }
},
"required": ["state_name", "geometric_schema", "target_file_path", "action_space_id"]
}
},
{
"name": "reconcile_manifest_state",
"description": "Reconciles an existing model by orchestrating LLM agents bounded by the JSON schema.",
"inputSchema": {
"type": "object",
"properties": {
"state_name": { "type": "string" },
"geometric_schema": { "type": "object" },
"target_file_path": { "type": "string" },
"action_space_id": { "type": "string" },
"base_class": { "type": "string", "default": "CoreasonBaseState" }
},
"required": ["state_name", "geometric_schema", "target_file_path", "action_space_id"]
}
},
{
"name": "scaffold_logic_actuator",
"description": "Scaffolds a new logic actuator function by orchestrating LLM agents bounded by the JSON schema.",
"inputSchema": {
"type": "object",
"properties": {
"actuator_name": { "type": "string" },
"geometric_schema": { "type": "object" },
"target_file_path": { "type": "string" },
"action_space_id": { "type": "string" },
"agent_instruction": { "type": "string" },
"causal_affordance": { "type": "string" },
"epistemic_bounds": { "type": "string" },
"return_type": { "type": "string", "default": "None" },
"required_imports": { "type": "array", "items": { "type": "string" } },
"logic_body": { "type": "string" }
},
"required": ["actuator_name", "geometric_schema", "target_file_path", "action_space_id", "agent_instruction", "causal_affordance", "epistemic_bounds"]
}
},
{
"name": "scaffold_epistemic_node",
"description": "Scaffolds a new Swarm Agent structure by orchestrating LLM agents.",
"inputSchema": {
"type": "object",
"properties": {
"node_name": { "type": "string" },
"cognitive_boundary_directive": { "type": "string" },
"target_file_path": { "type": "string" },
"action_space_id": { "type": "string" },
"base_class": { "type": "string", "default": "CoreasonBaseAgent" }
},
"required": ["node_name", "cognitive_boundary_directive", "target_file_path", "action_space_id"]
}
},
{
"name": "scaffold_kubernetes_crd",
"description": "Scaffolds a Kubernetes Custom Resource Definition (CRD) AST mapping by orchestrating LLM agents.",
"inputSchema": {
"type": "object",
"properties": {
"crd_name": { "type": "string" },
"geometric_schema": { "type": "object" },
"target_file_path": { "type": "string" },
"action_space_id": { "type": "string" },
"api_group": { "type": "string", "default": "chaos-mesh.org" },
"api_version": { "type": "string", "default": "v1alpha1" },
"kind": { "type": "string", "default": "NetworkChaos" }
},
"required": ["crd_name", "geometric_schema", "target_file_path", "action_space_id"]
}
},
{
"name": "verify_solver_diff",
"description": "Verify a high-entropy solver diff through the PVV pipeline.",
"inputSchema": {
"type": "object",
"properties": {
"deliberation_trace": { "type": "string" },
"payload": { "type": "string" },
"solver_urn": { "type": "string" },
"tokens_burned": { "type": "integer" }
},
"required": ["deliberation_trace", "payload", "solver_urn", "tokens_burned"]
}
},
{
"name": "scaffold_manifest_yaml",
"description": "Scaffolds a new manifest.yaml for a URN, injecting default CLA properties.",
"inputSchema": {
"type": "object",
"properties": {
"target_dir": { "type": "string" },
"urn": { "type": "string" },
"author_id": { "type": "string" }
},
"required": ["target_dir", "urn", "author_id"]
}
},
{
"name": "scaffold_sensory_urn",
"description": "Scaffolds a new Sensory URN AST module by orchestrating LLM agents.",
"inputSchema": {
"type": "object",
"properties": {
"urn_name": { "type": "string" },
"geometric_schema": { "type": "object" },
"target_file_path": { "type": "string" },
"action_space_id": { "type": "string" },
"has_binary_assets": { "type": "boolean", "default": false }
},
"required": ["urn_name", "geometric_schema", "target_file_path", "action_space_id"]
}
},
{
"name": "scaffold_typescript_bindings",
"description": "Generates TypeScript interface bindings from a JSON schema.",
"inputSchema": {
"type": "object",
"properties": {
"state_name": { "type": "string" },
"geometric_schema": { "type": "object" },
"target_file_path": { "type": "string" }
},
"required": ["state_name", "geometric_schema", "target_file_path"]
}
},
{
"name": "scaffold_wit_bindings",
"description": "Generates WebAssembly WIT record bindings from a JSON schema.",
"inputSchema": {
"type": "object",
"properties": {
"state_name": { "type": "string" },
"geometric_schema": { "type": "object" },
"target_file_path": { "type": "string" }
},
"required": ["state_name", "geometric_schema", "target_file_path"]
}
},
{
"name": "promote_to_urn_authority",
"description": "Promotes a dynamically generated capability source file to the global URN authority ledger.",
"inputSchema": {
"type": "object",
"properties": {
"source_file_path": { "type": "string" },
"target_urn": { "type": "string" },
"urn_authority_dir": { "type": "string" }
},
"required": ["source_file_path", "target_urn", "urn_authority_dir"]
}
}
])
}
async fn call_tool(name: &str, args: &Value) -> Result<String, String> {
match name {
"scaffold_manifest_state" => {
let state_name = args["state_name"].as_str().ok_or("Missing state_name")?;
let geometric_schema = &args["geometric_schema"];
let target_file_path = args["target_file_path"].as_str().ok_or("Missing target_file_path")?;
let action_space_id = args["action_space_id"].as_str().ok_or("Missing action_space_id")?;
let base_class = args["base_class"].as_str().unwrap_or("CoreasonBaseState");
let prompt_template = format!(
"Generate a class named {} inheriting from {}.\n\
Include a docstring with AGENT INSTRUCTION, CAUSAL AFFORDANCE, \
EPISTEMIC BOUNDS, and MCP ROUTING TRIGGERS ({}).\n\
Ensure all fields map correctly to the provided geometric schema.\n",
state_name, base_class, action_space_id
);
scaffold_ast(target_file_path, action_space_id, geometric_schema, 3, &prompt_template).await
}
"reconcile_manifest_state" => {
let state_name = args["state_name"].as_str().ok_or("Missing state_name")?;
let geometric_schema = &args["geometric_schema"];
let target_file_path = args["target_file_path"].as_str().ok_or("Missing target_file_path")?;
let action_space_id = args["action_space_id"].as_str().ok_or("Missing action_space_id")?;
let base_class = args["base_class"].as_str().unwrap_or("CoreasonBaseState");
let prompt_template = format!(
"Reconcile the existing class named {} inheriting from {} with the new schema.\n\
Include a docstring with AGENT INSTRUCTION, CAUSAL AFFORDANCE, \
EPISTEMIC BOUNDS, and MCP ROUTING TRIGGERS ({}).\n\
Ensure all fields map correctly to the provided geometric schema.\n",
state_name, base_class, action_space_id
);
scaffold_ast(target_file_path, action_space_id, geometric_schema, 5, &prompt_template).await
}
"scaffold_logic_actuator" => {
let actuator_name = args["actuator_name"].as_str().ok_or("Missing actuator_name")?;
let geometric_schema = &args["geometric_schema"];
let target_file_path = args["target_file_path"].as_str().ok_or("Missing target_file_path")?;
let action_space_id = args["action_space_id"].as_str().ok_or("Missing action_space_id")?;
let agent_instruction = args["agent_instruction"].as_str().ok_or("Missing agent_instruction")?;
let causal_affordance = args["causal_affordance"].as_str().ok_or("Missing causal_affordance")?;
let epistemic_bounds = args["epistemic_bounds"].as_str().ok_or("Missing epistemic_bounds")?;
let return_type = args["return_type"].as_str().unwrap_or("None");
let required_imports_val = args["required_imports"].as_array();
let mut imports_str = String::new();
if let Some(arr) = required_imports_val {
let imports: Vec<String> = arr.iter().filter_map(|v| v.as_str().map(|s| format!("'{}'", s))).collect();
imports_str = format!("[{}]", imports.join(", "));
}
let logic_body = args["logic_body"].as_str().unwrap_or("pass");
let prompt_template = format!(
"Generate a function named {} bounded by the @mcp.tool() decorator returning {}.\n\
Include a docstring with AGENT INSTRUCTION: {}\n\
CAUSAL AFFORDANCE: {}\n\
EPISTEMIC BOUNDS: {}\n\
MCP ROUTING TRIGGERS: {}.\n\
Required imports to include if possible: {}\n\
Suggested logic body: {}\n",
actuator_name, return_type, agent_instruction, causal_affordance, epistemic_bounds, action_space_id, imports_str, logic_body
);
scaffold_ast(target_file_path, action_space_id, geometric_schema, 8, &prompt_template).await
}
"scaffold_epistemic_node" => {
let node_name = args["node_name"].as_str().ok_or("Missing node_name")?;
let cognitive_boundary_directive = args["cognitive_boundary_directive"].as_str().ok_or("Missing cognitive_boundary_directive")?;
let target_file_path = args["target_file_path"].as_str().ok_or("Missing target_file_path")?;
let action_space_id = args["action_space_id"].as_str().ok_or("Missing action_space_id")?;
let base_class = args["base_class"].as_str().unwrap_or("CoreasonBaseAgent");
let prompt_template = format!(
"Generate an agent class named {} inheriting from {}.\n\
The agent must adhere to the cognitive boundary directive: {}\n\
Include a docstring with AGENT INSTRUCTION, CAUSAL AFFORDANCE, \
EPISTEMIC BOUNDS, and MCP ROUTING TRIGGERS ({}).\n",
node_name, base_class, cognitive_boundary_directive, action_space_id
);
scaffold_ast(target_file_path, action_space_id, &Value::Null, 8, &prompt_template).await
}
"scaffold_kubernetes_crd" => {
let crd_name = args["crd_name"].as_str().ok_or("Missing crd_name")?;
let geometric_schema = &args["geometric_schema"];
let target_file_path = args["target_file_path"].as_str().ok_or("Missing target_file_path")?;
let action_space_id = args["action_space_id"].as_str().ok_or("Missing action_space_id")?;
let api_group = args["api_group"].as_str().unwrap_or("chaos-mesh.org");
let api_version = args["api_version"].as_str().unwrap_or("v1alpha1");
let kind = args["kind"].as_str().unwrap_or("NetworkChaos");
let prompt_template = format!(
"Generate a class for Kubernetes CRD named {}.\n\
API Group: {}, API Version: {}, Kind: {}\n\
Include a docstring with AGENT INSTRUCTION, CAUSAL AFFORDANCE, \
EPISTEMIC BOUNDS, and MCP ROUTING TRIGGERS ({}).\n",
crd_name, api_group, api_version, kind, action_space_id
);
scaffold_ast(target_file_path, action_space_id, geometric_schema, 5, &prompt_template).await
}
"verify_solver_diff" => {
let _deliberation_trace = args["deliberation_trace"].as_str().ok_or("Missing deliberation_trace")?;
let payload = args["payload"].as_str().ok_or("Missing payload")?;
let solver_urn = args["solver_urn"].as_str().ok_or("Missing solver_urn")?;
let tokens_burned = args["tokens_burned"].as_i64().ok_or("Missing tokens_burned")?;
let receipt = coreason_meta_engineering_rust::pvv::execute_pvv_pipeline(
payload,
solver_urn,
tokens_burned,
)?;
Ok(serde_json::to_string(&receipt).unwrap())
}
"scaffold_manifest_yaml" => {
let target_dir = args["target_dir"].as_str().ok_or("Missing target_dir")?;
let urn = args["urn"].as_str().ok_or("Missing urn")?;
let author_id = args["author_id"].as_str().ok_or("Missing author_id")?;
scaffold_manifest_yaml_impl(target_dir, urn, author_id).await
}
"scaffold_sensory_urn" => {
let urn_name = args["urn_name"].as_str().ok_or("Missing urn_name")?;
let geometric_schema = &args["geometric_schema"];
let target_file_path = args["target_file_path"].as_str().ok_or("Missing target_file_path")?;
let action_space_id = args["action_space_id"].as_str().ok_or("Missing action_space_id")?;
let has_binary_assets = args["has_binary_assets"].as_bool().unwrap_or(false);
let mut prompt_template = format!(
"Generate a Sensory UI module for URN {}.\n\
Include a docstring with AGENT INSTRUCTION, CAUSAL AFFORDANCE, \
EPISTEMIC BOUNDS, and MCP ROUTING TRIGGERS ({}).\n\
Ensure all UI visual nodes strictly map to the provided geometric schema.\n",
urn_name, action_space_id
);
if has_binary_assets {
prompt_template += "Allocate passive MCP Resource definitions for binary assets (e.g. logos) associated with this UI URN.\n";
}
scaffold_ast(target_file_path, action_space_id, geometric_schema, 4, &prompt_template).await
}
"scaffold_typescript_bindings" => {
let state_name = args["state_name"].as_str().ok_or("Missing state_name")?;
let geometric_schema = &args["geometric_schema"];
let target_file_path = args["target_file_path"].as_str().ok_or("Missing target_file_path")?;
enforce_cla_gate()?;
let content = generate_typescript_interface(state_name, geometric_schema)?;
write_binding_file(target_file_path, &content)?;
Ok(format!("Scaffolded TypeScript bindings at {}", target_file_path))
}
"scaffold_wit_bindings" => {
let state_name = args["state_name"].as_str().ok_or("Missing state_name")?;
let geometric_schema = &args["geometric_schema"];
let target_file_path = args["target_file_path"].as_str().ok_or("Missing target_file_path")?;
enforce_cla_gate()?;
let content = generate_wit_record(state_name, geometric_schema)?;
write_binding_file(target_file_path, &content)?;
Ok(format!("Scaffolded WIT bindings at {}", target_file_path))
}
"promote_to_urn_authority" => {
let source_file_path = args["source_file_path"].as_str().ok_or("Missing source_file_path")?;
let target_urn = args["target_urn"].as_str().ok_or("Missing target_urn")?;
let urn_authority_dir = args["urn_authority_dir"].as_str().ok_or("Missing urn_authority_dir")?;
promote_to_urn_authority_impl(source_file_path, target_urn, urn_authority_dir).await
}
_ => Err(format!("Unknown tool: {}", name)),
}
}
fn write_binding_file(target_file_path: &str, content: &str) -> Result<(), String> {
let workspace_root = env::var("COREASON_WORKSPACE_ROOT")
.map_err(|_| "COREASON_WORKSPACE_ROOT environment variable is not defined.".to_string())?;
let workspace_dir = Path::new(&workspace_root).canonicalize()
.map_err(|e| format!("Failed to canonicalize workspace root: {}", e))?;
let target_file = Path::new(target_file_path);
let resolved_target = if target_file.is_absolute() {
target_file.to_path_buf()
} else {
workspace_dir.join(target_file)
};
let parent = resolved_target.parent().ok_or_else(|| "Target path must have a parent".to_string())?;
let canonical_parent = parent.canonicalize()
.map_err(|e| format!("Failed to canonicalize target parent directory: {}", e))?;
if !canonical_parent.starts_with(&workspace_dir) {
return Err("Epistemic Quarantine Breach: Unauthorized Path Traversal Detected.".to_string());
}
fs::create_dir_all(resolved_target.parent().unwrap())
.map_err(|e| format!("Failed to create parent directories: {}", e))?;
fs::write(&resolved_target, content)
.map_err(|e| format!("Failed to write bindings to file: {}", e))?;
let file_hash = compute_canonical_hash(content.as_bytes());
let hash_file = PathBuf::from(format!("{}.hash", resolved_target.to_string_lossy()));
fs::write(&hash_file, file_hash)
.map_err(|e| format!("Failed to write sidecar hash file: {}", e))?;
Ok(())
}
async fn scaffold_manifest_yaml_impl(target_dir: &str, urn: &str, author_id: &str) -> Result<String, String> {
enforce_cla_gate()?;
let workspace_root = env::var("COREASON_WORKSPACE_ROOT")
.map_err(|_| "COREASON_WORKSPACE_ROOT environment variable is not defined.".to_string())?;
let workspace_dir = Path::new(&workspace_root).canonicalize()
.map_err(|e| format!("Failed to canonicalize workspace: {}", e))?;
let resolved_target_dir = workspace_dir.join(target_dir);
let canonical_target_dir = resolved_target_dir.canonicalize()
.unwrap_or_else(|_| resolved_target_dir.clone());
if !canonical_target_dir.starts_with(&workspace_dir) {
return Err("Epistemic Quarantine Breach: Unauthorized Path Traversal Detected.".to_string());
}
let mut tenant_cid = "urn:tenant:coreason:global:authority".to_string(); let mut cla_status = "UNSIGNED".to_string();
let mut cla_assignee = "".to_string();
if env::var("AST_GUILLOTINE_ACTIVE").map(|v| v == "True").unwrap_or(false) {
cla_status = "AUTO_ASSIGNED_PPL3".to_string();
cla_assignee = "urn:tenant:coreason:global:authority".to_string();
} else {
let vault_url = env::var("VAULT_ADDR");
let vault_token = env::var("VAULT_TOKEN");
if let (Ok(url), Ok(token)) = (vault_url, vault_token) {
let client = reqwest::blocking::Client::new();
let api_url = format!("{}/v1/secret/data/coreason/identity", url.trim_end_matches('/'));
if let Ok(res) = client.get(&api_url).header("X-Vault-Token", token).send() {
if res.status().is_success() {
if let Ok(json) = res.json::<Value>() {
if let Some(t_cid) = json["data"]["data"]["tenant_cid"].as_str() {
tenant_cid = t_cid.to_string();
cla_assignee = t_cid.to_string();
}
}
}
}
}
}
let urn_lower = urn.to_lowercase();
let mut cognitive_framework_hat = Value::Null;
for hat in &["white", "red", "black", "yellow", "green", "blue"] {
if urn_lower.contains(hat) {
cognitive_framework_hat = Value::String(hat.to_string());
break;
}
}
let manifest_data = json!({
"urn": urn,
"tenant_cid": tenant_cid,
"cognitive_framework_hat": cognitive_framework_hat,
"default_clearance_tiers": [200],
"default_minimum_rigidity_tier": 255,
"epistemic_status": "DRAFT",
"provenance": {
"author_id": author_id,
"created_at": Utc::now().to_rfc3339(),
"oracle_validator": Value::Null,
"certification": "pending",
"prior_event_hash": Value::Null,
"cla_status": cla_status,
"cla_assignee": cla_assignee,
"cla_version": "v1.0",
"developer_tenant_cid": "UNKNOWN_LOCAL_TENANT",
"cla_attestation_signature": "null"
},
"validation": {
"test_coverage_pct": 0.0,
"latency_ms": 0,
"cryptographic_hash": "null"
}
});
let target_file = resolved_target_dir.join("manifest.yaml");
fs::create_dir_all(&resolved_target_dir)
.map_err(|e| format!("Failed to create directory: {}", e))?;
let yaml_str = serde_yaml::to_string(&manifest_data)
.map_err(|e| format!("Failed to serialize YAML: {}", e))?;
fs::write(&target_file, yaml_str)
.map_err(|e| format!("Failed to write manifest: {}", e))?;
Ok(format!("Scaffolded manifest.yaml at {:?}", target_file))
}
async fn promote_to_urn_authority_impl(source_file_path: &str, target_urn: &str, urn_authority_dir: &str) -> Result<String, String> {
enforce_cla_gate()?;
let workspace_root = env::var("COREASON_WORKSPACE_ROOT")
.map_err(|_| "COREASON_WORKSPACE_ROOT environment variable is not defined.".to_string())?;
let workspace_dir = Path::new(&workspace_root).canonicalize()
.map_err(|e| format!("Failed to canonicalize workspace root: {}", e))?;
let resolved_source = workspace_dir.join(source_file_path);
let resolved_urn_dir = workspace_dir.join(urn_authority_dir);
for path in &[&resolved_source, &resolved_urn_dir] {
let parent = path.parent().ok_or_else(|| "Path must have a parent".to_string())?;
let canonical_parent = parent.canonicalize()
.map_err(|e| format!("Failed to canonicalize: {}", e))?;
if !canonical_parent.starts_with(&workspace_dir) {
return Err("Epistemic Quarantine Breach: Unauthorized Path Traversal Detected.".to_string());
}
}
let ledger_path = resolved_urn_dir.join("src").join("coreason_urn_authority").join("ledger").join("index.yaml");
if !ledger_path.exists() {
return Err(format!("URN Authority ledger not found at {:?}", ledger_path));
}
let signing_key = resolve_signing_key()
.ok_or_else(|| "Could not resolve signing key.".to_string())?;
let private_bytes = hex::decode(&signing_key)
.map_err(|e| format!("Invalid signing key hex: {}", e))?;
if private_bytes.len() != 32 {
return Err("Signing key must be exactly 32 bytes.".to_string());
}
let mut key_arr = [0u8; 32];
key_arr.copy_from_slice(&private_bytes);
let ed_private = ed25519_dalek::SigningKey::from_bytes(&key_arr);
let public_bytes = ed_private.verifying_key().to_bytes();
let mut multicodec_bytes = vec![0xed, 0x01];
multicodec_bytes.extend_from_slice(&public_bytes);
let did_str = format!("did:key:z{}", bs58::encode(multicodec_bytes).into_string());
let source_content = fs::read_to_string(&resolved_source)
.map_err(|e| format!("Failed to read source: {}", e))?;
let re = regex::Regex::new(r#"tenant_cid\s*[:=]\s*['"]([^'"]+)['"]"#).unwrap();
let tenant_cid = re.captures(&source_content)
.and_then(|cap| cap.get(1).map(|m| m.as_str().to_string()))
.or_else(|| env::var("COREASON_DEFAULT_TENANT_ID").ok())
.ok_or_else(|| "Could not determine tenant identity. tenant_cid is not defined in source and COREASON_DEFAULT_TENANT_ID is not set.".to_string())?;
let urn_suffix = target_urn.split(':').last().unwrap_or("");
let safe_name = urn_suffix.replace(|c: char| !c.is_ascii_alphanumeric() && c != '_', "_").to_lowercase();
let registry_prefix = env::var("COREASON_OCI_REGISTRY_PREFIX")
.unwrap_or_else(|_| "ghcr.io/coreason-ai".to_string());
let oci_uri = format!("{}/{}:latest", registry_prefix.trim_end_matches('/'), safe_name);
let mut binary_path = None;
if let Ok(val) = env::var("COREASON_URN_AUTHORITY_BINARY") {
binary_path = Some(PathBuf::from(val));
} else {
let sibling_authority_dir = workspace_dir.join("coreason-urn-authority");
if sibling_authority_dir.exists() {
for folder in &["release", "debug"] {
for ext in &["", ".exe"] {
let p = sibling_authority_dir.join("target").join(folder).join(format!("urn-authority{}", ext));
if p.exists() {
binary_path = Some(p);
break;
}
}
if binary_path.is_some() {
break;
}
}
}
}
let binary = binary_path.ok_or_else(|| "URN Authority CLI binary not found in target directories.".to_string())?;
let mut cmd = Command::new(&binary);
cmd.arg("promote")
.arg("--urn").arg(target_urn)
.arg("--oci-uri").arg(&oci_uri)
.arg("--did").arg(&did_str)
.arg("--tenant-cid").arg(&tenant_cid)
.arg("--ledger-path").arg(&ledger_path)
.arg("--skip-git");
if target_urn.contains("sensory") || target_urn.contains("solver") {
cmd.arg("--mcp-namespace").arg(format!("io.github.coreason-ai/{}", safe_name));
}
let res = cmd.output().map_err(|e| format!("Failed to run urn-authority command: {}", e))?;
if !res.status.success() {
return Err(format!(
"Failed to promote URN via URN Authority CLI (exit {}): {}",
res.status.code().unwrap_or(-1),
String::from_utf8_lossy(&res.stderr).trim()
));
}
let receipt = json!({
"success": true,
"receipt": {
"success": true,
"urn": target_urn,
"oci_uri": oci_uri,
"did": did_str,
"tenant_cid": tenant_cid
}
});
Ok(serde_json::to_string(&receipt).unwrap())
}