Complete BZZZ functionality port to CHORUS

🎭 CHORUS now contains full BZZZ functionality adapted for containers

Core systems ported:
- P2P networking (libp2p with DHT and PubSub)
- Task coordination (COOEE protocol)
- HMMM collaborative reasoning
- SHHH encryption and security
- SLURP admin election system
- UCXL content addressing
- UCXI server integration
- Hypercore logging system
- Health monitoring and graceful shutdown
- License validation with KACHING

Container adaptations:
- Environment variable configuration (no YAML files)
- Container-optimized logging to stdout/stderr
- Auto-generated agent IDs for container deployments
- Docker-first architecture

All proven BZZZ P2P protocols, AI integration, and collaboration
features are now available in containerized form.

Next: Build and test container deployment.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-09-02 20:02:37 +10:00
parent 7c6cbd562a
commit 543ab216f9
224 changed files with 86331 additions and 186 deletions

551
pkg/protocol/resolver.go Normal file
View File

@@ -0,0 +1,551 @@
package protocol
import (
"context"
"fmt"
"strings"
"sync"
"time"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
)
// PeerCapability represents the capabilities of a peer
type PeerCapability struct {
PeerID peer.ID `json:"peer_id"`
Agent string `json:"agent"`
Role string `json:"role"`
Capabilities []string `json:"capabilities"`
Models []string `json:"models"`
Specialization string `json:"specialization"`
Project string `json:"project"`
LastSeen time.Time `json:"last_seen"`
Status string `json:"status"` // "online", "busy", "offline"
Metadata map[string]string `json:"metadata"`
}
// PeerAddress represents a resolved peer address
type PeerAddress struct {
PeerID peer.ID `json:"peer_id"`
Addresses []string `json:"addresses"`
Priority int `json:"priority"`
Metadata map[string]interface{} `json:"metadata"`
}
// ResolutionResult represents the result of address resolution
type ResolutionResult struct {
URI *BzzzURI `json:"uri"`
Peers []*PeerAddress `json:"peers"`
ResolvedAt time.Time `json:"resolved_at"`
ResolutionTTL time.Duration `json:"ttl"`
Strategy string `json:"strategy"`
}
// ResolutionStrategy defines how to resolve addresses
type ResolutionStrategy string
const (
StrategyExact ResolutionStrategy = "exact" // Exact match only
StrategyBestMatch ResolutionStrategy = "best_match" // Best available match
StrategyLoadBalance ResolutionStrategy = "load_balance" // Load balance among matches
StrategyPriority ResolutionStrategy = "priority" // Highest priority first
)
// Resolver handles semantic address resolution
type Resolver struct {
// Peer capability registry
capabilities map[peer.ID]*PeerCapability
capMutex sync.RWMutex
// Address resolution cache
cache map[string]*ResolutionResult
cacheMutex sync.RWMutex
cacheTTL time.Duration
// Configuration
defaultStrategy ResolutionStrategy
maxPeersPerResult int
// Peerstore for address information
peerstore peerstore.Peerstore
}
// NewResolver creates a new semantic address resolver
func NewResolver(peerstore peerstore.Peerstore, opts ...ResolverOption) *Resolver {
r := &Resolver{
capabilities: make(map[peer.ID]*PeerCapability),
cache: make(map[string]*ResolutionResult),
cacheTTL: 5 * time.Minute,
defaultStrategy: StrategyBestMatch,
maxPeersPerResult: 5,
peerstore: peerstore,
}
for _, opt := range opts {
opt(r)
}
// Start background cleanup
go r.startCleanup()
return r
}
// ResolverOption configures the resolver
type ResolverOption func(*Resolver)
// WithCacheTTL sets the cache TTL
func WithCacheTTL(ttl time.Duration) ResolverOption {
return func(r *Resolver) {
r.cacheTTL = ttl
}
}
// WithDefaultStrategy sets the default resolution strategy
func WithDefaultStrategy(strategy ResolutionStrategy) ResolverOption {
return func(r *Resolver) {
r.defaultStrategy = strategy
}
}
// WithMaxPeersPerResult sets the maximum peers per result
func WithMaxPeersPerResult(max int) ResolverOption {
return func(r *Resolver) {
r.maxPeersPerResult = max
}
}
// RegisterPeer registers a peer's capabilities
func (r *Resolver) RegisterPeer(peerID peer.ID, capability *PeerCapability) {
r.capMutex.Lock()
defer r.capMutex.Unlock()
capability.PeerID = peerID
capability.LastSeen = time.Now()
r.capabilities[peerID] = capability
// Clear relevant cache entries
r.invalidateCache()
}
// UnregisterPeer removes a peer from the registry
func (r *Resolver) UnregisterPeer(peerID peer.ID) {
r.capMutex.Lock()
defer r.capMutex.Unlock()
delete(r.capabilities, peerID)
// Clear relevant cache entries
r.invalidateCache()
}
// UpdatePeerStatus updates a peer's status
func (r *Resolver) UpdatePeerStatus(peerID peer.ID, status string) {
r.capMutex.Lock()
defer r.capMutex.Unlock()
if cap, exists := r.capabilities[peerID]; exists {
cap.Status = status
cap.LastSeen = time.Now()
}
}
// Resolve resolves a bzzz:// URI to peer addresses
func (r *Resolver) Resolve(ctx context.Context, uri *BzzzURI, strategy ...ResolutionStrategy) (*ResolutionResult, error) {
if uri == nil {
return nil, fmt.Errorf("nil URI")
}
// Determine strategy
resolveStrategy := r.defaultStrategy
if len(strategy) > 0 {
resolveStrategy = strategy[0]
}
// Check cache first
cacheKey := r.getCacheKey(uri, resolveStrategy)
if result := r.getFromCache(cacheKey); result != nil {
return result, nil
}
// Perform resolution
result, err := r.resolveURI(ctx, uri, resolveStrategy)
if err != nil {
return nil, err
}
// Cache result
r.cacheResult(cacheKey, result)
return result, nil
}
// ResolveString resolves a bzzz:// URI string to peer addresses
func (r *Resolver) ResolveString(ctx context.Context, uriStr string, strategy ...ResolutionStrategy) (*ResolutionResult, error) {
uri, err := ParseBzzzURI(uriStr)
if err != nil {
return nil, fmt.Errorf("failed to parse URI: %w", err)
}
return r.Resolve(ctx, uri, strategy...)
}
// resolveURI performs the actual URI resolution
func (r *Resolver) resolveURI(ctx context.Context, uri *BzzzURI, strategy ResolutionStrategy) (*ResolutionResult, error) {
r.capMutex.RLock()
defer r.capMutex.RUnlock()
var matchingPeers []*PeerCapability
// Find matching peers
for _, cap := range r.capabilities {
if r.peerMatches(cap, uri) {
matchingPeers = append(matchingPeers, cap)
}
}
if len(matchingPeers) == 0 {
return &ResolutionResult{
URI: uri,
Peers: []*PeerAddress{},
ResolvedAt: time.Now(),
ResolutionTTL: r.cacheTTL,
Strategy: string(strategy),
}, nil
}
// Apply resolution strategy
selectedPeers := r.applyStrategy(matchingPeers, strategy)
// Convert to peer addresses
var peerAddresses []*PeerAddress
for i, cap := range selectedPeers {
if i >= r.maxPeersPerResult {
break
}
addr := &PeerAddress{
PeerID: cap.PeerID,
Priority: r.calculatePriority(cap, uri),
Metadata: map[string]interface{}{
"agent": cap.Agent,
"role": cap.Role,
"specialization": cap.Specialization,
"status": cap.Status,
"last_seen": cap.LastSeen,
},
}
// Get addresses from peerstore
if r.peerstore != nil {
addrs := r.peerstore.Addrs(cap.PeerID)
for _, ma := range addrs {
addr.Addresses = append(addr.Addresses, ma.String())
}
}
peerAddresses = append(peerAddresses, addr)
}
return &ResolutionResult{
URI: uri,
Peers: peerAddresses,
ResolvedAt: time.Now(),
ResolutionTTL: r.cacheTTL,
Strategy: string(strategy),
}, nil
}
// peerMatches checks if a peer matches the URI criteria
func (r *Resolver) peerMatches(cap *PeerCapability, uri *BzzzURI) bool {
// Check if peer is online
if cap.Status == "offline" {
return false
}
// Check agent match
if !IsWildcard(uri.Agent) && !componentMatches(uri.Agent, cap.Agent) {
return false
}
// Check role match
if !IsWildcard(uri.Role) && !componentMatches(uri.Role, cap.Role) {
return false
}
// Check project match (if specified in metadata)
if !IsWildcard(uri.Project) {
if project, exists := cap.Metadata["project"]; exists {
if !componentMatches(uri.Project, project) {
return false
}
}
}
// Check task capabilities (if peer has relevant capabilities)
if !IsWildcard(uri.Task) {
taskMatches := false
for _, capability := range cap.Capabilities {
if componentMatches(uri.Task, capability) {
taskMatches = true
break
}
}
if !taskMatches {
// Also check specialization
if !componentMatches(uri.Task, cap.Specialization) {
return false
}
}
}
return true
}
// applyStrategy applies the resolution strategy to matching peers
func (r *Resolver) applyStrategy(peers []*PeerCapability, strategy ResolutionStrategy) []*PeerCapability {
switch strategy {
case StrategyExact:
// Return only exact matches (already filtered)
return peers
case StrategyPriority:
// Sort by priority (calculated based on specificity and status)
return r.sortByPriority(peers)
case StrategyLoadBalance:
// Sort by load (prefer less busy peers)
return r.sortByLoad(peers)
case StrategyBestMatch:
fallthrough
default:
// Sort by best match score
return r.sortByMatch(peers)
}
}
// sortByPriority sorts peers by priority score
func (r *Resolver) sortByPriority(peers []*PeerCapability) []*PeerCapability {
// Simple priority: online > working > busy, then by last seen
result := make([]*PeerCapability, len(peers))
copy(result, peers)
// Sort by status priority and recency
for i := 0; i < len(result)-1; i++ {
for j := i + 1; j < len(result); j++ {
iPriority := r.getStatusPriority(result[i].Status)
jPriority := r.getStatusPriority(result[j].Status)
if iPriority < jPriority ||
(iPriority == jPriority && result[i].LastSeen.Before(result[j].LastSeen)) {
result[i], result[j] = result[j], result[i]
}
}
}
return result
}
// sortByLoad sorts peers by current load (prefer less busy)
func (r *Resolver) sortByLoad(peers []*PeerCapability) []*PeerCapability {
result := make([]*PeerCapability, len(peers))
copy(result, peers)
// Sort by status (ready > working > busy)
for i := 0; i < len(result)-1; i++ {
for j := i + 1; j < len(result); j++ {
iLoad := r.getLoadScore(result[i].Status)
jLoad := r.getLoadScore(result[j].Status)
if iLoad > jLoad {
result[i], result[j] = result[j], result[i]
}
}
}
return result
}
// sortByMatch sorts peers by match quality
func (r *Resolver) sortByMatch(peers []*PeerCapability) []*PeerCapability {
result := make([]*PeerCapability, len(peers))
copy(result, peers)
// Simple sorting - prefer online status and recent activity
for i := 0; i < len(result)-1; i++ {
for j := i + 1; j < len(result); j++ {
if r.getMatchScore(result[i]) < r.getMatchScore(result[j]) {
result[i], result[j] = result[j], result[i]
}
}
}
return result
}
// Helper functions for scoring
func (r *Resolver) getStatusPriority(status string) int {
switch status {
case "ready":
return 3
case "working":
return 2
case "busy":
return 1
default:
return 0
}
}
func (r *Resolver) getLoadScore(status string) int {
switch status {
case "ready":
return 0 // Lowest load
case "working":
return 1
case "busy":
return 2 // Highest load
default:
return 3
}
}
func (r *Resolver) getMatchScore(cap *PeerCapability) int {
score := 0
// Status contribution
score += r.getStatusPriority(cap.Status) * 10
// Recency contribution (more recent = higher score)
timeSince := time.Since(cap.LastSeen)
if timeSince < time.Minute {
score += 5
} else if timeSince < time.Hour {
score += 3
} else if timeSince < 24*time.Hour {
score += 1
}
// Capability count contribution
score += len(cap.Capabilities)
return score
}
// calculatePriority calculates priority for a peer address
func (r *Resolver) calculatePriority(cap *PeerCapability, uri *BzzzURI) int {
priority := 0
// Exact matches get higher priority
if cap.Agent == uri.Agent {
priority += 4
}
if cap.Role == uri.Role {
priority += 3
}
if cap.Specialization == uri.Task {
priority += 2
}
// Status-based priority
priority += r.getStatusPriority(cap.Status)
return priority
}
// Cache management
func (r *Resolver) getCacheKey(uri *BzzzURI, strategy ResolutionStrategy) string {
return fmt.Sprintf("%s:%s", uri.String(), strategy)
}
func (r *Resolver) getFromCache(key string) *ResolutionResult {
r.cacheMutex.RLock()
defer r.cacheMutex.RUnlock()
if result, exists := r.cache[key]; exists {
// Check if result is still valid
if time.Since(result.ResolvedAt) < result.ResolutionTTL {
return result
}
// Remove expired entry
delete(r.cache, key)
}
return nil
}
func (r *Resolver) cacheResult(key string, result *ResolutionResult) {
r.cacheMutex.Lock()
defer r.cacheMutex.Unlock()
r.cache[key] = result
}
func (r *Resolver) invalidateCache() {
r.cacheMutex.Lock()
defer r.cacheMutex.Unlock()
// Clear entire cache on capability changes
r.cache = make(map[string]*ResolutionResult)
}
// startCleanup starts background cache cleanup
func (r *Resolver) startCleanup() {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
for range ticker.C {
r.cleanupCache()
}
}
func (r *Resolver) cleanupCache() {
r.cacheMutex.Lock()
defer r.cacheMutex.Unlock()
now := time.Now()
for key, result := range r.cache {
if now.Sub(result.ResolvedAt) > result.ResolutionTTL {
delete(r.cache, key)
}
}
}
// GetPeerCapabilities returns all registered peer capabilities
func (r *Resolver) GetPeerCapabilities() map[peer.ID]*PeerCapability {
r.capMutex.RLock()
defer r.capMutex.RUnlock()
result := make(map[peer.ID]*PeerCapability)
for id, cap := range r.capabilities {
result[id] = cap
}
return result
}
// GetPeerCapability returns a specific peer's capabilities
func (r *Resolver) GetPeerCapability(peerID peer.ID) (*PeerCapability, bool) {
r.capMutex.RLock()
defer r.capMutex.RUnlock()
cap, exists := r.capabilities[peerID]
return cap, exists
}
// Close shuts down the resolver
func (r *Resolver) Close() error {
// Clear all data
r.capMutex.Lock()
r.capabilities = make(map[peer.ID]*PeerCapability)
r.capMutex.Unlock()
r.cacheMutex.Lock()
r.cache = make(map[string]*ResolutionResult)
r.cacheMutex.Unlock()
return nil
}