coreason-urn-authority 0.45.1

Epistemic Ledger & OCI Trust Anchor for CoReason URNs.
Documentation
// Copyright (c) 2026 CoReason, Inc.
// All rights reserved.

use async_nats::jetstream::kv::Config;
use futures_util::StreamExt;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::OnceLock;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CapabilityEntry {
    pub clearance: String,
    pub epistemic_status: String,
    pub capability_metadata: serde_json::Value,
    pub content_hash: String,
}

pub fn urn_to_key(urn: &str) -> String {
    urn.replace(':', ".")
}

pub fn key_to_urn(key: &str) -> String {
    // Replace first 5 dots back to colons
    let mut urn = String::new();
    let mut dots_count = 0;
    for c in key.chars() {
        if c == '.' && dots_count < 5 {
            urn.push(':');
            dots_count += 1;
        } else {
            urn.push(c);
        }
    }
    urn
}

pub fn validate_urn(urn: &str) -> Result<(), String> {
    static RE: OnceLock<Regex> = OnceLock::new();
    let re = RE.get_or_init(|| {
        Regex::new(r"^urn:[a-z0-9_]+:actionspace:(oracle|solver|effector|substrate|sensory|node):[a-z0-9_]+:v[0-9]+$").unwrap()
    });
    if !re.is_match(urn) {
        return Err(format!(
            "URN Topology Breach: URN {} does not conform to the CoReason manifest modern actionspace taxonomy. Rejecting capability.",
            urn
        ));
    }
    Ok(())
}

pub async fn connect_and_get_kv(
    nats_url: &str,
    bucket: &str,
) -> Result<async_nats::jetstream::kv::Store, String> {
    let client = async_nats::connect(nats_url)
        .await
        .map_err(|e| format!("Failed to connect to NATS: {}", e))?;
    let js = async_nats::jetstream::new(client);

    // Try to bind first
    match js.get_key_value(bucket).await {
        Ok(kv) => Ok(kv),
        Err(_) => {
            // Create it
            js.create_key_value(Config {
                bucket: bucket.to_string(),
                description: "CoReason URN Capability Registry".to_string(),
                history: 5,
                ..Default::default()
            })
            .await
            .map_err(|e| format!("Failed to create KV bucket: {}", e))
        }
    }
}

pub async fn register_capability(
    nats_url: &str,
    bucket: &str,
    urn: &str,
    clearance: &str,
    epistemic_status: &str,
    metadata: serde_json::Value,
    content_hash: &str,
) -> Result<(), String> {
    // Validate URN
    validate_urn(urn)?;

    let kv = connect_and_get_kv(nats_url, bucket).await?;
    let key = urn_to_key(urn);
    let entry = CapabilityEntry {
        clearance: clearance.to_string(),
        epistemic_status: epistemic_status.to_string(),
        capability_metadata: metadata,
        content_hash: content_hash.to_string(),
    };
    let val_bytes = serde_json::to_vec(&entry)
        .map_err(|e| format!("Failed to serialize capability entry: {}", e))?;
    kv.put(key, val_bytes.into())
        .await
        .map_err(|e| format!("Failed to put capability: {}", e))?;
    Ok(())
}

pub async fn resolve_urn(
    nats_url: &str,
    bucket: &str,
    urn: &str,
) -> Result<CapabilityEntry, String> {
    let kv = connect_and_get_kv(nats_url, bucket).await?;
    let key = urn_to_key(urn);
    let entry_val = kv
        .get(key)
        .await
        .map_err(|e| format!("Failed to get capability: {}", e))?
        .ok_or_else(|| format!("Geometrical topology fault: unregistered URN {}", urn))?;
    let entry: CapabilityEntry = serde_json::from_slice(&entry_val)
        .map_err(|e| format!("Failed to deserialize capability: {}", e))?;
    Ok(entry)
}

pub async fn list_all_capabilities(
    nats_url: &str,
    bucket: &str,
) -> Result<HashMap<String, CapabilityEntry>, String> {
    let kv = connect_and_get_kv(nats_url, bucket).await?;
    let mut keys = kv
        .keys()
        .await
        .map_err(|e| format!("Failed to list KV keys: {}", e))?;

    let mut result = HashMap::new();
    while let Some(key_res) = keys.next().await {
        if let Ok(key) = key_res {
            if let Ok(Some(entry_bytes)) = kv.get(&key).await {
                if let Ok(entry) = serde_json::from_slice(&entry_bytes) {
                    let urn = key_to_urn(&key);
                    result.insert(urn, entry);
                }
            }
        }
    }
    Ok(result)
}