Files
bzzz/pkg/ucxi/resolver.go
anthonyrawlins d96c931a29 Resolve import cycles and migrate to chorus.services module path
This comprehensive refactoring addresses critical architectural issues:

IMPORT CYCLE RESOLUTION:
• pkg/crypto ↔ pkg/slurp/roles: Created pkg/security/access_levels.go
• pkg/ucxl → pkg/dht: Created pkg/storage/interfaces.go
• pkg/slurp/leader → pkg/election → pkg/slurp/storage: Moved types to pkg/election/interfaces.go

MODULE PATH MIGRATION:
• Changed from github.com/anthonyrawlins/bzzz to chorus.services/bzzz
• Updated all import statements across 115+ files
• Maintains compatibility while removing personal GitHub account dependency

TYPE SYSTEM IMPROVEMENTS:
• Resolved duplicate type declarations in crypto package
• Added missing type definitions (RoleStatus, TimeRestrictions, KeyStatus, KeyRotationResult)
• Proper interface segregation to prevent future cycles

ARCHITECTURAL BENEFITS:
• Build now progresses past structural issues to normal dependency resolution
• Cleaner separation of concerns between packages
• Eliminates circular dependencies that prevented compilation
• Establishes foundation for scalable codebase growth

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-17 10:04:25 +10:00

246 lines
6.2 KiB
Go

package ucxi
import (
"context"
"fmt"
"sync"
"time"
"chorus.services/bzzz/pkg/ucxl"
)
// BasicAddressResolver provides a basic implementation of AddressResolver
type BasicAddressResolver struct {
// In-memory registry for announced content
registry map[string]*ResolvedContent
mutex sync.RWMutex
// P2P integration hooks (to be implemented later)
announceHook func(ctx context.Context, addr *ucxl.Address, content *Content) error
discoverHook func(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error)
// Configuration
defaultTTL time.Duration
nodeID string
}
// NewBasicAddressResolver creates a new basic address resolver
func NewBasicAddressResolver(nodeID string) *BasicAddressResolver {
return &BasicAddressResolver{
registry: make(map[string]*ResolvedContent),
defaultTTL: 5 * time.Minute,
nodeID: nodeID,
}
}
// SetAnnounceHook sets a hook function for content announcements (for P2P integration)
func (r *BasicAddressResolver) SetAnnounceHook(hook func(ctx context.Context, addr *ucxl.Address, content *Content) error) {
r.announceHook = hook
}
// SetDiscoverHook sets a hook function for content discovery (for P2P integration)
func (r *BasicAddressResolver) SetDiscoverHook(hook func(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error)) {
r.discoverHook = hook
}
// Resolve resolves a UCXL address to content
func (r *BasicAddressResolver) Resolve(ctx context.Context, addr *ucxl.Address) (*ResolvedContent, error) {
if addr == nil {
return nil, fmt.Errorf("address cannot be nil")
}
key := r.generateRegistryKey(addr)
r.mutex.RLock()
resolved, exists := r.registry[key]
r.mutex.RUnlock()
if exists {
// Check if content is still valid (TTL)
if time.Now().Before(resolved.Resolved.Add(resolved.TTL)) {
return resolved, nil
}
// Content expired, remove from registry
r.mutex.Lock()
delete(r.registry, key)
r.mutex.Unlock()
}
// Try wildcard matching if exact match not found
if !exists {
if match := r.findWildcardMatch(addr); match != nil {
return match, nil
}
}
// If we have a discover hook, try P2P discovery
if r.discoverHook != nil {
results, err := r.discoverHook(ctx, addr)
if err == nil && len(results) > 0 {
// Cache the first result and return it
result := results[0]
r.cacheResolvedContent(key, result)
return result, nil
}
}
return nil, fmt.Errorf("address not found: %s", addr.String())
}
// Announce announces content at a UCXL address
func (r *BasicAddressResolver) Announce(ctx context.Context, addr *ucxl.Address, content *Content) error {
if addr == nil {
return fmt.Errorf("address cannot be nil")
}
if content == nil {
return fmt.Errorf("content cannot be nil")
}
key := r.generateRegistryKey(addr)
resolved := &ResolvedContent{
Address: addr,
Content: content,
Source: r.nodeID,
Resolved: time.Now(),
TTL: r.defaultTTL,
}
// Store in local registry
r.mutex.Lock()
r.registry[key] = resolved
r.mutex.Unlock()
// Call P2P announce hook if available
if r.announceHook != nil {
if err := r.announceHook(ctx, addr, content); err != nil {
// Log but don't fail - local announcement succeeded
// In a real implementation, this would be logged properly
return nil
}
}
return nil
}
// Discover discovers content matching a pattern
func (r *BasicAddressResolver) Discover(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error) {
if pattern == nil {
return nil, fmt.Errorf("pattern cannot be nil")
}
var results []*ResolvedContent
// Search local registry
r.mutex.RLock()
for _, resolved := range r.registry {
// Check if content is still valid (TTL)
if time.Now().After(resolved.Resolved.Add(resolved.TTL)) {
continue
}
// Check if address matches pattern
if resolved.Address.Matches(pattern) {
results = append(results, resolved)
}
}
r.mutex.RUnlock()
// Try P2P discovery if hook is available
if r.discoverHook != nil {
p2pResults, err := r.discoverHook(ctx, pattern)
if err == nil {
// Merge P2P results with local results
// Cache P2P results for future use
for _, result := range p2pResults {
key := r.generateRegistryKey(result.Address)
r.cacheResolvedContent(key, result)
results = append(results, result)
}
}
}
return results, nil
}
// findWildcardMatch searches for wildcard matches in the registry
func (r *BasicAddressResolver) findWildcardMatch(target *ucxl.Address) *ResolvedContent {
r.mutex.RLock()
defer r.mutex.RUnlock()
for _, resolved := range r.registry {
// Check if content is still valid (TTL)
if time.Now().After(resolved.Resolved.Add(resolved.TTL)) {
continue
}
// Check if target matches the registered address pattern
if target.Matches(resolved.Address) {
return resolved
}
}
return nil
}
// generateRegistryKey generates a unique key for registry storage
func (r *BasicAddressResolver) generateRegistryKey(addr *ucxl.Address) string {
return fmt.Sprintf("%s:%s@%s:%s/%s",
addr.Agent, addr.Role, addr.Project, addr.Task, addr.TemporalSegment.String())
}
// cacheResolvedContent caches resolved content in the local registry
func (r *BasicAddressResolver) cacheResolvedContent(key string, resolved *ResolvedContent) {
r.mutex.Lock()
defer r.mutex.Unlock()
r.registry[key] = resolved
}
// GetRegistryStats returns statistics about the registry
func (r *BasicAddressResolver) GetRegistryStats() map[string]interface{} {
r.mutex.RLock()
defer r.mutex.RUnlock()
active := 0
expired := 0
now := time.Now()
for _, resolved := range r.registry {
if now.Before(resolved.Resolved.Add(resolved.TTL)) {
active++
} else {
expired++
}
}
return map[string]interface{}{
"total_entries": len(r.registry),
"active_entries": active,
"expired_entries": expired,
"node_id": r.nodeID,
}
}
// CleanupExpired removes expired entries from the registry
func (r *BasicAddressResolver) CleanupExpired() int {
r.mutex.Lock()
defer r.mutex.Unlock()
now := time.Now()
removed := 0
for key, resolved := range r.registry {
if now.After(resolved.Resolved.Add(resolved.TTL)) {
delete(r.registry, key)
removed++
}
}
return removed
}
// SetDefaultTTL sets the default TTL for cached content
func (r *BasicAddressResolver) SetDefaultTTL(ttl time.Duration) {
r.defaultTTL = ttl
}