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:
@@ -1,140 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config represents the complete CHORUS configuration loaded from environment variables
|
||||
type Config struct {
|
||||
Agent AgentConfig `yaml:"agent"`
|
||||
Network NetworkConfig `yaml:"network"`
|
||||
License LicenseConfig `yaml:"license"`
|
||||
AI AIConfig `yaml:"ai"`
|
||||
Logging LoggingConfig `yaml:"logging"`
|
||||
}
|
||||
|
||||
// AgentConfig defines agent-specific settings
|
||||
type AgentConfig struct {
|
||||
ID string `yaml:"id"`
|
||||
Specialization string `yaml:"specialization"`
|
||||
MaxTasks int `yaml:"max_tasks"`
|
||||
Capabilities []string `yaml:"capabilities"`
|
||||
}
|
||||
|
||||
// NetworkConfig defines network and API settings
|
||||
type NetworkConfig struct {
|
||||
P2PPort int `yaml:"p2p_port"`
|
||||
APIPort int `yaml:"api_port"`
|
||||
HealthPort int `yaml:"health_port"`
|
||||
BindAddr string `yaml:"bind_address"`
|
||||
}
|
||||
|
||||
// LicenseConfig defines licensing settings
|
||||
type LicenseConfig struct {
|
||||
Email string `yaml:"email"`
|
||||
LicenseKey string `yaml:"license_key"`
|
||||
ClusterID string `yaml:"cluster_id"`
|
||||
}
|
||||
|
||||
// AIConfig defines AI service settings
|
||||
type AIConfig struct {
|
||||
OllamaEndpoint string `yaml:"ollama_endpoint"`
|
||||
DefaultModel string `yaml:"default_model"`
|
||||
}
|
||||
|
||||
// LoggingConfig defines logging settings
|
||||
type LoggingConfig struct {
|
||||
Level string `yaml:"level"`
|
||||
Format string `yaml:"format"`
|
||||
}
|
||||
|
||||
// LoadFromEnvironment loads configuration from environment variables
|
||||
// This is the primary configuration method for CHORUS (no config files)
|
||||
func LoadFromEnvironment() (*Config, error) {
|
||||
cfg := &Config{
|
||||
Agent: AgentConfig{
|
||||
ID: getEnvOrDefault("CHORUS_AGENT_ID", ""),
|
||||
Specialization: getEnvOrDefault("CHORUS_SPECIALIZATION", "general_developer"),
|
||||
MaxTasks: getEnvIntOrDefault("CHORUS_MAX_TASKS", 3),
|
||||
Capabilities: getEnvArrayOrDefault("CHORUS_CAPABILITIES", []string{"general_development", "task_coordination"}),
|
||||
},
|
||||
Network: NetworkConfig{
|
||||
P2PPort: getEnvIntOrDefault("CHORUS_P2P_PORT", 9000),
|
||||
APIPort: getEnvIntOrDefault("CHORUS_API_PORT", 8080),
|
||||
HealthPort: getEnvIntOrDefault("CHORUS_HEALTH_PORT", 8081),
|
||||
BindAddr: getEnvOrDefault("CHORUS_BIND_ADDRESS", "0.0.0.0"),
|
||||
},
|
||||
License: LicenseConfig{
|
||||
Email: os.Getenv("CHORUS_LICENSE_EMAIL"),
|
||||
LicenseKey: os.Getenv("CHORUS_LICENSE_KEY"),
|
||||
ClusterID: getEnvOrDefault("CHORUS_CLUSTER_ID", "default-cluster"),
|
||||
},
|
||||
AI: AIConfig{
|
||||
OllamaEndpoint: getEnvOrDefault("OLLAMA_ENDPOINT", "http://localhost:11434"),
|
||||
DefaultModel: getEnvOrDefault("CHORUS_DEFAULT_MODEL", "llama3.1:8b"),
|
||||
},
|
||||
Logging: LoggingConfig{
|
||||
Level: getEnvOrDefault("LOG_LEVEL", "info"),
|
||||
Format: getEnvOrDefault("LOG_FORMAT", "structured"),
|
||||
},
|
||||
}
|
||||
|
||||
// Validate required configuration
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("configuration validation failed: %w", err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// Validate ensures all required configuration is present
|
||||
func (c *Config) Validate() error {
|
||||
if c.License.Email == "" {
|
||||
return fmt.Errorf("CHORUS_LICENSE_EMAIL is required")
|
||||
}
|
||||
|
||||
if c.License.LicenseKey == "" {
|
||||
return fmt.Errorf("CHORUS_LICENSE_KEY is required")
|
||||
}
|
||||
|
||||
if c.Agent.ID == "" {
|
||||
// Auto-generate agent ID if not provided
|
||||
hostname, _ := os.Hostname()
|
||||
containerID := os.Getenv("HOSTNAME") // Docker sets this to container ID
|
||||
if containerID != "" && containerID != hostname {
|
||||
c.Agent.ID = fmt.Sprintf("chorus-%s", containerID[:12])
|
||||
} else {
|
||||
c.Agent.ID = fmt.Sprintf("chorus-%s", hostname)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper functions for environment variable parsing
|
||||
|
||||
func getEnvOrDefault(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getEnvIntOrDefault(key string, defaultValue int) int {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
if parsed, err := strconv.Atoi(value); err == nil {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func getEnvArrayOrDefault(key string, defaultValue []string) []string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return strings.Split(value, ",")
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
365
internal/logging/hypercore.go
Normal file
365
internal/logging/hypercore.go
Normal file
@@ -0,0 +1,365 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/peer"
|
||||
)
|
||||
|
||||
// Logger interface for basic logging operations
|
||||
type Logger interface {
|
||||
Info(msg string, args ...interface{})
|
||||
Warn(msg string, args ...interface{})
|
||||
Error(msg string, args ...interface{})
|
||||
}
|
||||
|
||||
// HypercoreLog represents a simplified Hypercore-inspired distributed log
|
||||
type HypercoreLog struct {
|
||||
entries []LogEntry
|
||||
mutex sync.RWMutex
|
||||
peerID peer.ID
|
||||
|
||||
// Verification chain
|
||||
headHash string
|
||||
|
||||
// Replication
|
||||
replicators map[peer.ID]*Replicator
|
||||
}
|
||||
|
||||
// LogEntry represents a single entry in the distributed log
|
||||
type LogEntry struct {
|
||||
Index uint64 `json:"index"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Author string `json:"author"` // Peer ID of the author
|
||||
Type LogType `json:"type"` // Type of log entry
|
||||
Data map[string]interface{} `json:"data"` // Log data
|
||||
Hash string `json:"hash"` // Hash of this entry
|
||||
PrevHash string `json:"prev_hash"` // Hash of previous entry
|
||||
Signature string `json:"signature"` // Digital signature (simplified)
|
||||
}
|
||||
|
||||
// LogType represents different types of log entries
|
||||
type LogType string
|
||||
|
||||
const (
|
||||
// Bzzz coordination logs
|
||||
TaskAnnounced LogType = "task_announced"
|
||||
TaskClaimed LogType = "task_claimed"
|
||||
TaskProgress LogType = "task_progress"
|
||||
TaskCompleted LogType = "task_completed"
|
||||
TaskFailed LogType = "task_failed"
|
||||
|
||||
// HMMM meta-discussion logs
|
||||
PlanProposed LogType = "plan_proposed"
|
||||
ObjectionRaised LogType = "objection_raised"
|
||||
Collaboration LogType = "collaboration"
|
||||
ConsensusReached LogType = "consensus_reached"
|
||||
Escalation LogType = "escalation"
|
||||
TaskHelpRequested LogType = "task_help_requested"
|
||||
TaskHelpOffered LogType = "task_help_offered"
|
||||
TaskHelpReceived LogType = "task_help_received"
|
||||
|
||||
// System logs
|
||||
PeerJoined LogType = "peer_joined"
|
||||
PeerLeft LogType = "peer_left"
|
||||
CapabilityBcast LogType = "capability_broadcast"
|
||||
NetworkEvent LogType = "network_event"
|
||||
)
|
||||
|
||||
// Replicator handles log replication with other peers
|
||||
type Replicator struct {
|
||||
peerID peer.ID
|
||||
lastSyncIndex uint64
|
||||
connected bool
|
||||
}
|
||||
|
||||
// NewHypercoreLog creates a new distributed log for a peer
|
||||
func NewHypercoreLog(peerID peer.ID) *HypercoreLog {
|
||||
return &HypercoreLog{
|
||||
entries: make([]LogEntry, 0),
|
||||
peerID: peerID,
|
||||
headHash: "",
|
||||
replicators: make(map[peer.ID]*Replicator),
|
||||
}
|
||||
}
|
||||
|
||||
// AppendString is a convenience method for string log types (to match interface)
|
||||
func (h *HypercoreLog) AppendString(logType string, data map[string]interface{}) error {
|
||||
_, err := h.Append(LogType(logType), data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Append adds a new entry to the log
|
||||
func (h *HypercoreLog) Append(logType LogType, data map[string]interface{}) (*LogEntry, error) {
|
||||
h.mutex.Lock()
|
||||
defer h.mutex.Unlock()
|
||||
|
||||
index := uint64(len(h.entries))
|
||||
|
||||
entry := LogEntry{
|
||||
Index: index,
|
||||
Timestamp: time.Now(),
|
||||
Author: h.peerID.String(),
|
||||
Type: logType,
|
||||
Data: data,
|
||||
PrevHash: h.headHash,
|
||||
}
|
||||
|
||||
// Calculate hash
|
||||
entryHash, err := h.calculateEntryHash(entry)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to calculate entry hash: %w", err)
|
||||
}
|
||||
entry.Hash = entryHash
|
||||
|
||||
// Add simple signature (in production, use proper cryptographic signatures)
|
||||
entry.Signature = h.createSignature(entry)
|
||||
|
||||
// Append to log
|
||||
h.entries = append(h.entries, entry)
|
||||
h.headHash = entryHash
|
||||
|
||||
fmt.Printf("📝 Log entry appended: %s [%d] by %s\n",
|
||||
logType, index, h.peerID.ShortString())
|
||||
|
||||
// Trigger replication to connected peers
|
||||
go h.replicateEntry(entry)
|
||||
|
||||
return &entry, nil
|
||||
}
|
||||
|
||||
// Get retrieves a log entry by index
|
||||
func (h *HypercoreLog) Get(index uint64) (*LogEntry, error) {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
if index >= uint64(len(h.entries)) {
|
||||
return nil, fmt.Errorf("entry %d not found", index)
|
||||
}
|
||||
|
||||
return &h.entries[index], nil
|
||||
}
|
||||
|
||||
// Length returns the number of entries in the log
|
||||
func (h *HypercoreLog) Length() uint64 {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
return uint64(len(h.entries))
|
||||
}
|
||||
|
||||
// GetRange retrieves a range of log entries
|
||||
func (h *HypercoreLog) GetRange(start, end uint64) ([]LogEntry, error) {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
if start >= uint64(len(h.entries)) {
|
||||
return nil, fmt.Errorf("start index %d out of range", start)
|
||||
}
|
||||
|
||||
if end > uint64(len(h.entries)) {
|
||||
end = uint64(len(h.entries))
|
||||
}
|
||||
|
||||
if start > end {
|
||||
return nil, fmt.Errorf("invalid range: start %d > end %d", start, end)
|
||||
}
|
||||
|
||||
result := make([]LogEntry, end-start)
|
||||
copy(result, h.entries[start:end])
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetEntriesByType retrieves all entries of a specific type
|
||||
func (h *HypercoreLog) GetEntriesByType(logType LogType) ([]LogEntry, error) {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
var result []LogEntry
|
||||
for _, entry := range h.entries {
|
||||
if entry.Type == logType {
|
||||
result = append(result, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetEntriesByAuthor retrieves all entries by a specific author
|
||||
func (h *HypercoreLog) GetEntriesByAuthor(author string) ([]LogEntry, error) {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
var result []LogEntry
|
||||
for _, entry := range h.entries {
|
||||
if entry.Author == author {
|
||||
result = append(result, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetRecentEntries retrieves the most recent N entries from the log
|
||||
func (h *HypercoreLog) GetRecentEntries(count int) ([]LogEntry, error) {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
totalEntries := len(h.entries)
|
||||
if count <= 0 || totalEntries == 0 {
|
||||
return []LogEntry{}, nil
|
||||
}
|
||||
|
||||
start := 0
|
||||
if totalEntries > count {
|
||||
start = totalEntries - count
|
||||
}
|
||||
|
||||
result := make([]LogEntry, totalEntries-start)
|
||||
copy(result, h.entries[start:])
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetEntriesSince retrieves all entries since a given index
|
||||
func (h *HypercoreLog) GetEntriesSince(sinceIndex uint64) ([]LogEntry, error) {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
if sinceIndex >= uint64(len(h.entries)) {
|
||||
return []LogEntry{}, nil
|
||||
}
|
||||
|
||||
result := make([]LogEntry, len(h.entries)-int(sinceIndex))
|
||||
copy(result, h.entries[sinceIndex:])
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// VerifyIntegrity verifies the integrity of the log chain
|
||||
func (h *HypercoreLog) VerifyIntegrity() error {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
var prevHash string
|
||||
for i, entry := range h.entries {
|
||||
// Verify previous hash link
|
||||
if entry.PrevHash != prevHash {
|
||||
return fmt.Errorf("integrity error at entry %d: prev_hash mismatch", i)
|
||||
}
|
||||
|
||||
// Verify entry hash
|
||||
calculatedHash, err := h.calculateEntryHash(entry)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to calculate hash for entry %d: %w", i, err)
|
||||
}
|
||||
|
||||
if entry.Hash != calculatedHash {
|
||||
return fmt.Errorf("integrity error at entry %d: hash mismatch", i)
|
||||
}
|
||||
|
||||
prevHash = entry.Hash
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddReplicator adds a peer for log replication
|
||||
func (h *HypercoreLog) AddReplicator(peerID peer.ID) {
|
||||
h.mutex.Lock()
|
||||
defer h.mutex.Unlock()
|
||||
|
||||
h.replicators[peerID] = &Replicator{
|
||||
peerID: peerID,
|
||||
lastSyncIndex: 0,
|
||||
connected: true,
|
||||
}
|
||||
|
||||
fmt.Printf("🔄 Added replicator: %s\n", peerID.ShortString())
|
||||
}
|
||||
|
||||
// RemoveReplicator removes a peer from replication
|
||||
func (h *HypercoreLog) RemoveReplicator(peerID peer.ID) {
|
||||
h.mutex.Lock()
|
||||
defer h.mutex.Unlock()
|
||||
|
||||
delete(h.replicators, peerID)
|
||||
fmt.Printf("🔄 Removed replicator: %s\n", peerID.ShortString())
|
||||
}
|
||||
|
||||
// replicateEntry sends a new entry to all connected replicators
|
||||
func (h *HypercoreLog) replicateEntry(entry LogEntry) {
|
||||
h.mutex.RLock()
|
||||
replicators := make([]*Replicator, 0, len(h.replicators))
|
||||
for _, replicator := range h.replicators {
|
||||
if replicator.connected {
|
||||
replicators = append(replicators, replicator)
|
||||
}
|
||||
}
|
||||
h.mutex.RUnlock()
|
||||
|
||||
for _, replicator := range replicators {
|
||||
// In a real implementation, this would send the entry over the network
|
||||
fmt.Printf("🔄 Replicating entry %d to %s\n",
|
||||
entry.Index, replicator.peerID.ShortString())
|
||||
}
|
||||
}
|
||||
|
||||
// calculateEntryHash calculates the hash of a log entry
|
||||
func (h *HypercoreLog) calculateEntryHash(entry LogEntry) (string, error) {
|
||||
// Create a copy without the hash and signature for calculation
|
||||
entryForHash := LogEntry{
|
||||
Index: entry.Index,
|
||||
Timestamp: entry.Timestamp,
|
||||
Author: entry.Author,
|
||||
Type: entry.Type,
|
||||
Data: entry.Data,
|
||||
PrevHash: entry.PrevHash,
|
||||
}
|
||||
|
||||
entryBytes, err := json.Marshal(entryForHash)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hash := sha256.Sum256(entryBytes)
|
||||
return hex.EncodeToString(hash[:]), nil
|
||||
}
|
||||
|
||||
// createSignature creates a simplified signature for the entry
|
||||
func (h *HypercoreLog) createSignature(entry LogEntry) string {
|
||||
// In production, this would use proper cryptographic signatures
|
||||
// For now, we use a simple hash-based signature
|
||||
signatureData := fmt.Sprintf("%s:%s:%d", h.peerID.String(), entry.Hash, entry.Index)
|
||||
hash := sha256.Sum256([]byte(signatureData))
|
||||
return hex.EncodeToString(hash[:])[:16] // Shortened for display
|
||||
}
|
||||
|
||||
// GetStats returns statistics about the log
|
||||
func (h *HypercoreLog) GetStats() map[string]interface{} {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
typeCount := make(map[LogType]int)
|
||||
authorCount := make(map[string]int)
|
||||
|
||||
for _, entry := range h.entries {
|
||||
typeCount[entry.Type]++
|
||||
authorCount[entry.Author]++
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"total_entries": len(h.entries),
|
||||
"head_hash": h.headHash,
|
||||
"replicators": len(h.replicators),
|
||||
"entries_by_type": typeCount,
|
||||
"entries_by_author": authorCount,
|
||||
"peer_id": h.peerID.String(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user