use crate::blockchain::serialization::UuidWrapper;
use crate::types::{EntityType, GlobalIdentity};
use crate::error::{IdentityError, Result};
use crate::synapse::blockchain::serialization::DateTimeWrapper;
use chrono::Utc;
use dashmap::DashMap;
use uuid::Uuid;
#[derive(Debug)]
pub struct IdentityRegistry {
identities: DashMap<String, GlobalIdentity>,
local_names: DashMap<String, String>,
}
impl IdentityRegistry {
pub fn new() -> Self {
Self {
identities: DashMap::new(),
local_names: DashMap::new(),
}
}
pub fn register_identity(&self, identity: GlobalIdentity) -> Result<()> {
if self.local_names.contains_key(&identity.local_name) {
return Err(IdentityError::AlreadyExists(format!(
"Local name '{}' already registered",
identity.local_name
))
.into());
}
if self.identities.contains_key(&identity.global_id) {
return Err(IdentityError::AlreadyExists(format!(
"Global ID '{}' already registered",
identity.global_id
))
.into());
}
self.local_names
.insert(identity.local_name.clone(), identity.global_id.clone());
self.identities.insert(identity.global_id.clone(), identity);
tracing::info!("📝 Registered new identity");
Ok(())
}
pub fn resolve_local_name(&self, local_name: &str) -> Option<GlobalIdentity> {
let global_id = self.local_names.get(local_name)?;
self.identities.get(global_id.as_str()).map(|entry| entry.value().clone())
}
pub fn get_identity(&self, global_id: &str) -> Option<GlobalIdentity> {
self.identities.get(global_id).map(|entry| entry.value().clone())
}
pub fn get_identity_mut(&self, global_id: &str) -> Option<GlobalIdentity> {
self.identities.get(global_id).map(|entry| entry.value().clone())
}
pub fn update_identity(&self, global_id: &str, identity: GlobalIdentity) -> Result<()> {
self.identities.insert(global_id.to_string(), identity);
Ok(())
}
pub fn get_public_key(&self, global_id: &str) -> Option<String> {
self.identities.get(global_id).map(|entry| entry.public_key.clone())
}
pub fn update_trust_level(&self, global_id: &str, trust_delta: i32) -> Result<()> {
self.identities.entry(global_id.to_string())
.and_modify(|identity| {
let current_trust = identity.trust_level as i32;
let new_trust = (current_trust + trust_delta).clamp(0, 100) as u8;
identity.trust_level = new_trust;
tracing::debug!(
"Updated trust level for {} from {} to {}",
global_id,
current_trust,
new_trust
);
})
.or_insert_with(|| {
tracing::warn!("Attempted to update trust for non-existent identity: {}", global_id);
GlobalIdentity::default()
});
Ok(())
}
pub fn update_last_seen(&self, global_id: &str) -> Result<()> {
self.identities.entry(global_id.to_string())
.and_modify(|identity| {
identity.update_last_seen();
})
.or_insert_with(|| {
tracing::warn!("Attempted to update last seen for non-existent identity: {}", global_id);
GlobalIdentity::default()
});
Ok(())
}
pub fn remove_identity(&self, global_id: &str) -> Result<GlobalIdentity> {
let identity = self.identities.remove(global_id).ok_or_else(|| {
IdentityError::NotFound(format!("Identity not found: {}", global_id))
})?;
self.local_names.remove(&identity.1.local_name);
tracing::info!("🗑️ Removed identity: {}", global_id);
Ok(identity.1)
}
pub fn list_by_type(&self, entity_type: EntityType) -> Vec<GlobalIdentity> {
self.identities
.iter()
.filter(|entry| entry.value().entity_type == entity_type)
.map(|entry| entry.value().clone())
.collect()
}
pub fn list_by_capability(&self, capability: &str) -> Vec<GlobalIdentity> {
self.identities
.iter()
.filter(|entry| entry.value().has_capability(capability))
.map(|entry| entry.value().clone())
.collect()
}
pub fn all_global_ids(&self) -> Vec<String> {
self.identities.iter().map(|entry| entry.key().clone()).collect()
}
pub fn all_local_names(&self) -> Vec<String> {
self.local_names.iter().map(|entry| entry.key().clone()).collect()
}
pub fn has_local_name(&self, local_name: &str) -> bool {
self.local_names.contains_key(local_name)
}
pub fn has_global_id(&self, global_id: &str) -> bool {
self.identities.contains_key(global_id)
}
pub fn count(&self) -> usize {
self.identities.len()
}
pub fn clear(&self) {
self.identities.clear();
self.local_names.clear();
tracing::info!("🧹 Cleared all identities");
}
pub fn resolve_entity(&self, identifier: &str) -> Result<GlobalIdentity> {
if let Some(global_id) = self.local_names.get(identifier) {
return self.identities.get(global_id.as_str())
.ok_or_else(|| IdentityError::NotFound(format!("Identity for global_id '{}' not found", global_id.as_str())).into())
.map(|entry| entry.value().clone());
}
self.identities.get(identifier)
.ok_or_else(|| IdentityError::NotFound(format!("Entity '{}' not found", identifier)).into())
.map(|entry| entry.value().clone())
}
pub fn register_entity(&self, global_id: &str, _email: &str, display_name: Option<String>) -> Result<()> {
let identity = GlobalIdentity {
global_id: global_id.to_string(),
local_name: display_name.clone().unwrap_or_else(|| global_id.to_string()),
entity_type: EntityType::AiModel, public_key: "".to_string(), trust_level: 0,
capabilities: Vec::new(),
last_seen: DateTimeWrapper::new(Utc::now()),
routing_preferences: std::collections::HashMap::new(),
};
self.register_identity(identity)
}
pub fn list_entities(&self) -> Vec<GlobalIdentity> {
self.identities.iter().map(|entry| entry.value().clone()).collect()
}
}
impl Default for IdentityRegistry {
fn default() -> Self {
Self::new()
}
}
impl IdentityRegistry {
pub fn create_ai_model(
local_name: impl Into<String>,
domain: impl Into<String>,
public_key: impl Into<String>,
capabilities: Vec<String>,
) -> GlobalIdentity {
let local_name = local_name.into();
let domain = domain.into();
let global_id = format!("{}@ai.{}", local_name.to_lowercase(), domain);
let mut identity = GlobalIdentity::new(local_name, global_id, EntityType::AiModel, public_key);
identity.capabilities = capabilities;
identity
}
pub fn create_human(
local_name: impl Into<String>,
domain: impl Into<String>,
public_key: impl Into<String>,
) -> GlobalIdentity {
let local_name = local_name.into();
let domain = domain.into();
let global_id = format!("{}@humans.{}", local_name.to_lowercase(), domain);
GlobalIdentity::new(local_name, global_id, EntityType::Human, public_key)
}
pub fn create_tool(
local_name: impl Into<String>,
domain: impl Into<String>,
public_key: impl Into<String>,
capabilities: Vec<String>,
) -> GlobalIdentity {
let local_name = local_name.into();
let domain = domain.into();
let global_id = format!("{}@tools.{}", local_name.to_lowercase(), domain);
let mut identity = GlobalIdentity::new(local_name, global_id, EntityType::Tool, public_key);
identity.capabilities = capabilities;
identity
}
pub fn generate_test_global_id(local_name: &str, entity_type: EntityType) -> String {
let subdomain = match entity_type {
EntityType::Human => "humans",
EntityType::AiModel => "ai",
EntityType::Tool => "tools",
EntityType::Service => "services",
EntityType::Router => "routers",
};
let uuid = UuidWrapper::new(Uuid::new_v4()).to_string()[..8].to_string();
format!("{}.{}@{}.test.local", local_name.to_lowercase(), uuid, subdomain)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_identity_registration() {
let registry = IdentityRegistry::new();
let identity = IdentityRegistry::create_ai_model(
"Claude",
"anthropic.ai",
"test-public-key",
vec!["conversation".to_string(), "analysis".to_string()],
);
assert!(registry.register_identity(identity).is_ok());
assert!(registry.has_local_name("Claude"));
assert!(registry.resolve_local_name("Claude").is_some());
}
#[test]
fn test_duplicate_registration() {
let registry = IdentityRegistry::new();
let identity1 = IdentityRegistry::create_human("Eric", "company.com", "key1");
let identity2 = IdentityRegistry::create_human("Eric", "company.com", "key2");
assert!(registry.register_identity(identity1).is_ok());
assert!(registry.register_identity(identity2).is_err());
}
#[test]
fn test_trust_level_update() {
let registry = IdentityRegistry::new();
let identity = IdentityRegistry::create_tool(
"FileSystem",
"tools.local",
"fs-key",
vec!["file_ops".to_string()],
);
let global_id = identity.global_id.clone();
registry.register_identity(identity).unwrap();
registry.update_trust_level(&global_id, 20).unwrap();
assert_eq!(registry.get_identity(&global_id).unwrap().trust_level, 70);
registry.update_trust_level(&global_id, -30).unwrap();
assert_eq!(registry.get_identity(&global_id).unwrap().trust_level, 40);
registry.update_trust_level(&global_id, -100).unwrap();
assert_eq!(registry.get_identity(&global_id).unwrap().trust_level, 0);
registry.update_trust_level(&global_id, 200).unwrap();
assert_eq!(registry.get_identity(&global_id).unwrap().trust_level, 100);
}
#[test]
fn test_capability_filtering() {
let registry = IdentityRegistry::new();
let ai1 = IdentityRegistry::create_ai_model(
"Claude",
"anthropic.ai",
"key1",
vec!["conversation".to_string(), "analysis".to_string()],
);
let ai2 = IdentityRegistry::create_ai_model(
"GPT",
"openai.com",
"key2",
vec!["conversation".to_string(), "generation".to_string()],
);
let tool = IdentityRegistry::create_tool(
"FileSystem",
"tools.local",
"key3",
vec!["file_ops".to_string()],
);
registry.register_identity(ai1).unwrap();
registry.register_identity(ai2).unwrap();
registry.register_identity(tool).unwrap();
let conversation_entities = registry.list_by_capability("conversation");
assert_eq!(conversation_entities.len(), 2);
let analysis_entities = registry.list_by_capability("analysis");
assert_eq!(analysis_entities.len(), 1);
let ai_entities = registry.list_by_type(EntityType::AiModel);
assert_eq!(ai_entities.len(), 2);
}
}