Agent Identity Implementation Specification
Complete technical specification for implementing the Agent Identity Crate (sindhan-identity) with detailed APIs, data structures, and implementation guidance.
Version Information
- Specification Version: 1.0.0
- Target Crate:
sindhan-identityv0.1.0 - Rust Version: 1.70+
- Last Updated: 2024-01-15
API Specification
Core Data Structures
AgentIdentity
Complete agent identity structure with all fields and validation rules:
use uuid::Uuid;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentIdentity {
// Immutable Core Identity
pub id: AgentId, // Cryptographically secure unique identifier
pub creation_timestamp: DateTime<Utc>,
pub creator_identity: String, // Who/what created this agent
pub agent_type: AgentType, // Type classification
// Mutable Identity Characteristics
pub name: String, // Human-readable name
pub description: String, // Agent purpose and description
pub version: SemanticVersion, // Agent version
pub capabilities: Vec<Capability>, // Available capabilities
// Lifecycle Information
pub lifecycle_state: LifecycleState,
pub state_history: Vec<StateTransition>,
pub last_activity: DateTime<Utc>,
// External System Integration
pub external_identities: HashMap<String, ExternalIdentity>,
pub idm_bindings: Vec<IdmBinding>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct AgentId {
pub uuid: Uuid, // Primary unique identifier
pub namespace: String, // Identity namespace
pub checksum: String, // Integrity verification
pub creation_context: CreationContext,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LifecycleState {
Requested,
Provisioning,
Configuring,
Active,
Suspended,
Maintaining,
Retiring,
Decommissioned,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StateTransition {
pub from_state: LifecycleState,
pub to_state: LifecycleState,
pub timestamp: DateTime<Utc>,
pub trigger: TransitionTrigger,
pub actor: String, // Who initiated the transition
pub reason: String, // Why the transition occurred
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TransitionTrigger {
Administrative, // Manual administrative action
Automatic, // System-triggered transition
External, // External system triggered
Scheduled, // Scheduled transition
Emergency, // Emergency transition
}External Identity Integration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExternalIdentity {
pub system: String, // IDM system name (e.g., "Active Directory")
pub identity: String, // External identity reference
pub binding_type: BindingType, // How the binding was established
pub created_at: DateTime<Utc>,
pub last_verified: DateTime<Utc>,
pub status: BindingStatus,
pub attributes: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BindingType {
Manual, // Manually configured binding
Automatic, // Automatically discovered binding
Delegated, // Delegated by another agent
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BindingStatus {
Active,
Suspended,
Expired,
Invalid,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IdmBinding {
pub system_name: String,
pub binding_id: String,
pub external_identity: ExternalIdentity,
pub permissions: Vec<String>,
pub created_at: DateTime<Utc>,
pub expires_at: Option<DateTime<Utc>>,
}Core Interfaces
Identity Registry Interface
use async_trait::async_trait;
use anyhow::Result;
#[async_trait]
pub trait IdentityRegistry {
/// Create a new agent identity
async fn create_identity(&mut self, request: CreateIdentityRequest) -> Result<AgentIdentity>;
/// Retrieve an agent identity by ID
async fn get_identity(&self, agent_id: &AgentId) -> Result<Option<AgentIdentity>>;
/// Update an existing agent identity
async fn update_identity(&mut self, identity: AgentIdentity) -> Result<()>;
/// Delete an agent identity (soft delete)
async fn delete_identity(&mut self, agent_id: &AgentId) -> Result<()>;
/// List identities by criteria
async fn list_identities(&self, criteria: ListCriteria) -> Result<Vec<AgentIdentity>>;
/// Search identities by name or attributes
async fn search_identities(&self, query: SearchQuery) -> Result<Vec<AgentIdentity>>;
}
#[derive(Debug, Clone)]
pub struct CreateIdentityRequest {
pub name: String,
pub description: String,
pub agent_type: AgentType,
pub namespace: String,
pub capabilities: Vec<Capability>,
pub creator_identity: String,
pub initial_state: Option<LifecycleState>,
}Lifecycle State Manager Interface
#[async_trait]
pub trait LifecycleStateManager {
/// Transition agent to new state
async fn transition_state(
&mut self,
agent_id: &AgentId,
new_state: LifecycleState,
trigger: TransitionTrigger,
actor: &str,
reason: &str
) -> Result<()>;
/// Get current state of agent
async fn get_current_state(&self, agent_id: &AgentId) -> Result<LifecycleState>;
/// Get state history for agent
async fn get_state_history(&self, agent_id: &AgentId) -> Result<Vec<StateTransition>>;
/// Validate if state transition is allowed
fn validate_transition(&self, from: &LifecycleState, to: &LifecycleState) -> Result<bool>;
/// Get agents in specific state
async fn get_agents_in_state(&self, state: &LifecycleState) -> Result<Vec<AgentId>>;
}External IDM Connector Interface
#[async_trait]
pub trait ExternalIdmConnector {
/// Establish binding to external IDM system
async fn establish_binding(
&mut self,
agent_id: &AgentId,
idm_system: &str,
external_identity: &str,
binding_type: BindingType
) -> Result<ExternalIdentity>;
/// Authenticate agent via external IDM
async fn authenticate_via_external_idm(
&self,
agent_id: &AgentId,
idm_system: &str
) -> Result<AuthenticationResult>;
/// Verify external identity binding
async fn verify_binding(
&self,
agent_id: &AgentId,
idm_system: &str
) -> Result<bool>;
/// Remove external identity binding
async fn remove_binding(
&mut self,
agent_id: &AgentId,
idm_system: &str
) -> Result<()>;
/// List all bindings for agent
async fn list_bindings(&self, agent_id: &AgentId) -> Result<Vec<IdmBinding>>;
}
#[derive(Debug, Clone)]
pub struct AuthenticationResult {
pub success: bool,
pub token: Option<String>,
pub expires_at: Option<DateTime<Utc>>,
pub permissions: Vec<String>,
pub error: Option<String>,
}Implementation Details
Identity Generation Algorithm
use sha2::{Sha256, Digest};
use rand::Rng;
impl AgentId {
pub fn new(namespace: &str, creation_context: CreationContext) -> Self {
let uuid = Uuid::new_v4();
let checksum = Self::compute_checksum(&uuid, namespace, &creation_context);
Self {
uuid,
namespace: namespace.to_string(),
checksum,
creation_context,
}
}
fn compute_checksum(
uuid: &Uuid,
namespace: &str,
context: &CreationContext
) -> String {
let mut hasher = Sha256::new();
hasher.update(uuid.as_bytes());
hasher.update(namespace.as_bytes());
hasher.update(context.to_bytes());
let result = hasher.finalize();
hex::encode(result)
}
pub fn validate_integrity(&self) -> bool {
let expected_checksum = Self::compute_checksum(
&self.uuid,
&self.namespace,
&self.creation_context
);
self.checksum == expected_checksum
}
}State Transition Validation
impl LifecycleStateManager for DefaultStateManager {
fn validate_transition(&self, from: &LifecycleState, to: &LifecycleState) -> Result<bool> {
use LifecycleState::*;
let allowed = match (from, to) {
(Requested, Provisioning) => true,
(Provisioning, Configuring) => true,
(Configuring, Active) => true,
(Active, Suspended) => true,
(Suspended, Active) => true,
(Active, Maintaining) => true,
(Maintaining, Active) => true,
(Active | Suspended | Maintaining, Retiring) => true,
(Retiring, Decommissioned) => true,
_ => false,
};
if !allowed {
return Err(anyhow::anyhow!(
"Invalid state transition from {:?} to {:?}",
from, to
));
}
Ok(true)
}
async fn transition_state(
&mut self,
agent_id: &AgentId,
new_state: LifecycleState,
trigger: TransitionTrigger,
actor: &str,
reason: &str
) -> Result<()> {
let current_identity = self.registry.get_identity(agent_id).await?
.ok_or_else(|| anyhow::anyhow!("Agent not found: {:?}", agent_id))?;
// Validate transition is allowed
self.validate_transition(¤t_identity.lifecycle_state, &new_state)?;
// Create transition record
let transition = StateTransition {
from_state: current_identity.lifecycle_state.clone(),
to_state: new_state.clone(),
timestamp: Utc::now(),
trigger,
actor: actor.to_string(),
reason: reason.to_string(),
metadata: HashMap::new(),
};
// Update identity
let mut updated_identity = current_identity;
updated_identity.lifecycle_state = new_state;
updated_identity.state_history.push(transition.clone());
updated_identity.last_activity = Utc::now();
// Persist changes
self.registry.update_identity(updated_identity).await?;
// Notify observers
self.event_bus.publish(IdentityEvent::StateChanged {
agent_id: agent_id.clone(),
transition,
}).await?;
Ok(())
}
}Caching Implementation
use lru::LruCache;
use tokio::sync::RwLock;
use std::sync::Arc;
use std::time::Duration;
#[derive(Debug)]
pub struct IdentityCache {
identity_cache: Arc<RwLock<LruCache<AgentId, AgentIdentity>>>,
binding_cache: Arc<RwLock<LruCache<String, Vec<ExternalIdentity>>>>,
ttl_manager: TtlManager,
}
impl IdentityCache {
pub fn new(capacity: usize) -> Self {
Self {
identity_cache: Arc::new(RwLock::new(LruCache::new(capacity))),
binding_cache: Arc::new(RwLock::new(LruCache::new(capacity / 2))),
ttl_manager: TtlManager::new(),
}
}
pub async fn get_identity(&self, agent_id: &AgentId) -> Option<AgentIdentity> {
if self.ttl_manager.is_expired(agent_id).await {
self.invalidate_identity(agent_id).await;
return None;
}
let cache = self.identity_cache.read().await;
cache.get(agent_id).cloned()
}
pub async fn cache_identity(&self, identity: AgentIdentity) {
let mut cache = self.identity_cache.write().await;
cache.put(identity.id.clone(), identity.clone());
// Set TTL for cache entry
self.ttl_manager.set_ttl(&identity.id, Duration::from_secs(3600)).await;
}
pub async fn invalidate_identity(&self, agent_id: &AgentId) {
let mut cache = self.identity_cache.write().await;
cache.pop(agent_id);
self.ttl_manager.remove_ttl(agent_id).await;
}
}Performance Requirements
Latency Targets
| Operation | Target Latency | Maximum Latency | Percentile |
|---|---|---|---|
| Identity Creation | < 50ms | < 200ms | 95th |
| Identity Lookup (cached) | < 5ms | < 20ms | 99th |
| Identity Lookup (uncached) | < 50ms | < 200ms | 95th |
| State Transition | < 10ms | < 50ms | 95th |
| External IDM Auth | < 100ms | < 500ms | 95th |
| Binding Verification | < 25ms | < 100ms | 95th |
Throughput Targets
| Operation | Target Throughput | Sustained Load | Peak Load |
|---|---|---|---|
| Identity Creation | 1,000/sec | 500/sec | 2,000/sec |
| Identity Lookup | 10,000/sec | 5,000/sec | 20,000/sec |
| State Transitions | 5,000/sec | 2,500/sec | 10,000/sec |
| External Auth | 500/sec | 250/sec | 1,000/sec |
Resource Requirements
| Resource | Baseline | Per 1K Agents | Maximum |
|---|---|---|---|
| Memory | 100MB | +50MB | 4GB |
| CPU | 0.5 cores | +0.1 cores | 8 cores |
| Storage | 1GB | +100MB | 500GB |
| Network | 10Mbps | +1Mbps | 1Gbps |
Testing Specifications
Unit Testing Requirements
#[cfg(test)]
mod tests {
use super::*;
use tokio_test;
#[tokio::test]
async fn test_identity_creation() {
let mut registry = MockIdentityRegistry::new();
let request = CreateIdentityRequest {
name: "test-agent".to_string(),
description: "Test agent for unit testing".to_string(),
agent_type: AgentType::Discovery,
namespace: "test".to_string(),
capabilities: vec![],
creator_identity: "system".to_string(),
initial_state: None,
};
let identity = registry.create_identity(request).await.unwrap();
assert_eq!(identity.name, "test-agent");
assert_eq!(identity.namespace, "test");
assert_eq!(identity.lifecycle_state, LifecycleState::Requested);
}
#[tokio::test]
async fn test_state_transition_validation() {
let manager = DefaultStateManager::new();
// Valid transitions
assert!(manager.validate_transition(
&LifecycleState::Requested,
&LifecycleState::Provisioning
).is_ok());
// Invalid transitions
assert!(manager.validate_transition(
&LifecycleState::Requested,
&LifecycleState::Active
).is_err());
}
#[tokio::test]
async fn test_identity_integrity() {
let context = CreationContext::new("test-system", "1.0.0");
let agent_id = AgentId::new("test-namespace", context);
assert!(agent_id.validate_integrity());
// Test tampered checksum
let mut tampered_id = agent_id.clone();
tampered_id.checksum = "invalid_checksum".to_string();
assert!(!tampered_id.validate_integrity());
}
}Integration Testing Requirements
#[tokio::test]
async fn test_full_identity_lifecycle() {
let mut system = IdentitySystem::new().await;
// Create identity
let request = CreateIdentityRequest {
name: "integration-test-agent".to_string(),
description: "Agent for integration testing".to_string(),
agent_type: AgentType::Operator,
namespace: "integration".to_string(),
capabilities: vec![Capability::DataProcessing],
creator_identity: "test-system".to_string(),
initial_state: None,
};
let identity = system.create_identity(request).await.unwrap();
let agent_id = &identity.id;
// Test state transitions
system.transition_state(
agent_id,
LifecycleState::Provisioning,
TransitionTrigger::Administrative,
"test-operator",
"Starting provisioning"
).await.unwrap();
// Verify state
let current_state = system.get_current_state(agent_id).await.unwrap();
assert_eq!(current_state, LifecycleState::Provisioning);
// Test external IDM binding
let binding = system.establish_binding(
agent_id,
"test-ldap",
"cn=test-agent,ou=agents,dc=test,dc=com",
BindingType::Manual
).await.unwrap();
assert_eq!(binding.system, "test-ldap");
assert_eq!(binding.status, BindingStatus::Active);
// Cleanup
system.delete_identity(agent_id).await.unwrap();
}Performance Testing Requirements
#[tokio::test]
async fn test_identity_lookup_performance() {
let system = IdentitySystem::new().await;
let agent_id = create_test_identity(&system).await;
let start = Instant::now();
// Perform 1000 lookups
for _ in 0..1000 {
let _identity = system.get_identity(&agent_id).await.unwrap();
}
let duration = start.elapsed();
let avg_latency = duration / 1000;
// Assert average latency is under 5ms
assert!(avg_latency < Duration::from_millis(5));
}
#[tokio::test]
async fn test_concurrent_state_transitions() {
let system = Arc::new(IdentitySystem::new().await);
let agent_ids: Vec<_> = (0..100)
.map(|_| create_test_identity(&system))
.collect::<FuturesUnordered<_>>()
.collect().await;
let start = Instant::now();
// Perform concurrent state transitions
let futures: Vec<_> = agent_ids.iter().map(|agent_id| {
let system = Arc::clone(&system);
let agent_id = agent_id.clone();
tokio::spawn(async move {
system.transition_state(
&agent_id,
LifecycleState::Provisioning,
TransitionTrigger::Automatic,
"test-system",
"Concurrent test"
).await
})
}).collect();
let results = futures::future::join_all(futures).await;
let duration = start.elapsed();
// All transitions should succeed
for result in results {
assert!(result.unwrap().is_ok());
}
// Total time should be reasonable for 100 concurrent operations
assert!(duration < Duration::from_secs(5));
}Configuration Specification
Environment Configuration
# config/identity.toml
[identity]
namespace = "production"
enable_caching = true
cache_size = 10000
cache_ttl_seconds = 3600
[lifecycle]
enable_state_validation = true
audit_all_transitions = true
max_history_entries = 1000
[external_idm]
connection_timeout_ms = 5000
authentication_timeout_ms = 10000
max_concurrent_connections = 100
retry_attempts = 3
retry_backoff_ms = 1000
[storage]
backend = "postgresql"
connection_pool_size = 20
max_connections = 100
connection_timeout_ms = 30000
[security]
enable_encryption_at_rest = true
encryption_algorithm = "AES-256-GCM"
key_rotation_interval_hours = 24
audit_log_retention_days = 365
[performance]
enable_metrics = true
metrics_export_interval_seconds = 60
enable_tracing = true
trace_sample_rate = 0.1Runtime Configuration
#[derive(Debug, Clone, Deserialize)]
pub struct IdentityConfig {
pub identity: IdentitySettings,
pub lifecycle: LifecycleSettings,
pub external_idm: ExternalIdmSettings,
pub storage: StorageSettings,
pub security: SecuritySettings,
pub performance: PerformanceSettings,
}
#[derive(Debug, Clone, Deserialize)]
pub struct IdentitySettings {
pub namespace: String,
pub enable_caching: bool,
pub cache_size: usize,
pub cache_ttl_seconds: u64,
}
// ... other settings structuresDeployment Guidelines
Prerequisites
- Rust 1.70+ toolchain
- PostgreSQL 13+ or compatible database
- Redis 6+ for caching (optional)
- Access to external IDM systems (LDAP, AD, etc.)
Installation Steps
-
Database Setup:
CREATE DATABASE sindhan_identity; CREATE USER sindhan_identity WITH PASSWORD 'secure_password'; GRANT ALL PRIVILEGES ON DATABASE sindhan_identity TO sindhan_identity; -
Configuration:
cp config/identity.toml.example config/identity.toml # Edit configuration as needed -
Database Migration:
cargo run --bin migrate -- --config config/identity.toml -
Service Startup:
cargo run --bin sindhan-identity -- --config config/identity.toml
Monitoring Setup
Required monitoring endpoints:
- Health Check:
GET /health - Metrics:
GET /metrics(Prometheus format) - Ready Check:
GET /ready
Key metrics to monitor:
identity_operations_total{operation, status}identity_operation_duration_seconds{operation}identity_cache_hit_rateidentity_state_transitions_total{from_state, to_state}external_idm_operations_total{system, operation, status}
This specification provides complete implementation guidance for the Agent Identity Crate, ensuring consistent and high-quality implementations across all development teams.