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:
235
pkg/hmmm_adapter/adapter_stub.go
Normal file
235
pkg/hmmm_adapter/adapter_stub.go
Normal file
@@ -0,0 +1,235 @@
|
||||
package hmmm_adapter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Joiner joins a pub/sub topic (ensure availability before publish).
|
||||
type Joiner func(topic string) error
|
||||
|
||||
// Publisher publishes a raw JSON payload to a topic.
|
||||
type Publisher func(topic string, payload []byte) error
|
||||
|
||||
// Adapter bridges BZZZ pub/sub to a RawPublisher-compatible interface.
|
||||
// It does not impose any message envelope so HMMM can publish raw JSON frames.
|
||||
// The adapter provides additional features like topic caching, metrics, and validation.
|
||||
type Adapter struct {
|
||||
join Joiner
|
||||
publish Publisher
|
||||
|
||||
// Topic join cache to avoid redundant joins
|
||||
joinedTopics map[string]bool
|
||||
joinedTopicsMu sync.RWMutex
|
||||
|
||||
// Metrics tracking
|
||||
publishCount int64
|
||||
joinCount int64
|
||||
errorCount int64
|
||||
metricsLock sync.RWMutex
|
||||
|
||||
// Configuration
|
||||
maxPayloadSize int
|
||||
joinTimeout time.Duration
|
||||
publishTimeout time.Duration
|
||||
}
|
||||
|
||||
// AdapterConfig holds configuration options for the Adapter
|
||||
type AdapterConfig struct {
|
||||
MaxPayloadSize int `yaml:"max_payload_size"`
|
||||
JoinTimeout time.Duration `yaml:"join_timeout"`
|
||||
PublishTimeout time.Duration `yaml:"publish_timeout"`
|
||||
}
|
||||
|
||||
// DefaultAdapterConfig returns sensible defaults for the adapter
|
||||
func DefaultAdapterConfig() AdapterConfig {
|
||||
return AdapterConfig{
|
||||
MaxPayloadSize: 1024 * 1024, // 1MB max payload
|
||||
JoinTimeout: 30 * time.Second,
|
||||
PublishTimeout: 10 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdapter constructs a new adapter with explicit join/publish hooks.
|
||||
// Wire these to BZZZ pubsub methods, e.g., JoinDynamicTopic and a thin PublishRaw helper.
|
||||
func NewAdapter(join Joiner, publish Publisher) *Adapter {
|
||||
return NewAdapterWithConfig(join, publish, DefaultAdapterConfig())
|
||||
}
|
||||
|
||||
// NewAdapterWithConfig constructs a new adapter with custom configuration.
|
||||
func NewAdapterWithConfig(join Joiner, publish Publisher, config AdapterConfig) *Adapter {
|
||||
return &Adapter{
|
||||
join: join,
|
||||
publish: publish,
|
||||
joinedTopics: make(map[string]bool),
|
||||
maxPayloadSize: config.MaxPayloadSize,
|
||||
joinTimeout: config.JoinTimeout,
|
||||
publishTimeout: config.PublishTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Publish ensures the topic is joined before sending a raw payload.
|
||||
// Includes validation, caching, metrics, and timeout handling.
|
||||
func (a *Adapter) Publish(ctx context.Context, topic string, payload []byte) error {
|
||||
// Input validation
|
||||
if topic == "" {
|
||||
a.incrementErrorCount()
|
||||
return fmt.Errorf("topic cannot be empty")
|
||||
}
|
||||
if len(payload) == 0 {
|
||||
a.incrementErrorCount()
|
||||
return fmt.Errorf("payload cannot be empty")
|
||||
}
|
||||
if len(payload) > a.maxPayloadSize {
|
||||
a.incrementErrorCount()
|
||||
return fmt.Errorf("payload size %d exceeds maximum %d bytes", len(payload), a.maxPayloadSize)
|
||||
}
|
||||
|
||||
// Check if we need to join the topic (with caching)
|
||||
if !a.isTopicJoined(topic) {
|
||||
joinCtx, cancel := context.WithTimeout(ctx, a.joinTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := a.joinTopic(joinCtx, topic); err != nil {
|
||||
a.incrementErrorCount()
|
||||
return fmt.Errorf("failed to join topic %s: %w", topic, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Publish with timeout
|
||||
publishCtx, cancel := context.WithTimeout(ctx, a.publishTimeout)
|
||||
defer cancel()
|
||||
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- a.publish(topic, payload)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
if err != nil {
|
||||
a.incrementErrorCount()
|
||||
return fmt.Errorf("failed to publish to topic %s: %w", topic, err)
|
||||
}
|
||||
a.incrementPublishCount()
|
||||
return nil
|
||||
case <-publishCtx.Done():
|
||||
a.incrementErrorCount()
|
||||
return fmt.Errorf("publish to topic %s timed out after %v", topic, a.publishTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
// isTopicJoined checks if a topic has already been joined (with caching)
|
||||
func (a *Adapter) isTopicJoined(topic string) bool {
|
||||
a.joinedTopicsMu.RLock()
|
||||
defer a.joinedTopicsMu.RUnlock()
|
||||
return a.joinedTopics[topic]
|
||||
}
|
||||
|
||||
// joinTopic joins a topic and updates the cache
|
||||
func (a *Adapter) joinTopic(ctx context.Context, topic string) error {
|
||||
// Double-check locking pattern to avoid redundant joins
|
||||
if a.isTopicJoined(topic) {
|
||||
return nil
|
||||
}
|
||||
|
||||
a.joinedTopicsMu.Lock()
|
||||
defer a.joinedTopicsMu.Unlock()
|
||||
|
||||
// Check again after acquiring write lock
|
||||
if a.joinedTopics[topic] {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Execute join with context
|
||||
done := make(chan error, 1)
|
||||
go func() {
|
||||
done <- a.join(topic)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-done:
|
||||
if err == nil {
|
||||
a.joinedTopics[topic] = true
|
||||
a.incrementJoinCount()
|
||||
}
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// GetMetrics returns current adapter metrics
|
||||
func (a *Adapter) GetMetrics() AdapterMetrics {
|
||||
a.metricsLock.RLock()
|
||||
defer a.metricsLock.RUnlock()
|
||||
|
||||
return AdapterMetrics{
|
||||
PublishCount: a.publishCount,
|
||||
JoinCount: a.joinCount,
|
||||
ErrorCount: a.errorCount,
|
||||
JoinedTopics: len(a.joinedTopics),
|
||||
}
|
||||
}
|
||||
|
||||
// AdapterMetrics holds metrics data for the adapter
|
||||
type AdapterMetrics struct {
|
||||
PublishCount int64 `json:"publish_count"`
|
||||
JoinCount int64 `json:"join_count"`
|
||||
ErrorCount int64 `json:"error_count"`
|
||||
JoinedTopics int `json:"joined_topics"`
|
||||
}
|
||||
|
||||
// ResetMetrics resets all metrics counters (useful for testing)
|
||||
func (a *Adapter) ResetMetrics() {
|
||||
a.metricsLock.Lock()
|
||||
defer a.metricsLock.Unlock()
|
||||
|
||||
a.publishCount = 0
|
||||
a.joinCount = 0
|
||||
a.errorCount = 0
|
||||
}
|
||||
|
||||
// ClearTopicCache clears the joined topics cache (useful for testing or reconnections)
|
||||
func (a *Adapter) ClearTopicCache() {
|
||||
a.joinedTopicsMu.Lock()
|
||||
defer a.joinedTopicsMu.Unlock()
|
||||
|
||||
a.joinedTopics = make(map[string]bool)
|
||||
}
|
||||
|
||||
// GetJoinedTopics returns a list of currently joined topics
|
||||
func (a *Adapter) GetJoinedTopics() []string {
|
||||
a.joinedTopicsMu.RLock()
|
||||
defer a.joinedTopicsMu.RUnlock()
|
||||
|
||||
topics := make([]string, 0, len(a.joinedTopics))
|
||||
for topic := range a.joinedTopics {
|
||||
topics = append(topics, topic)
|
||||
}
|
||||
return topics
|
||||
}
|
||||
|
||||
// incrementPublishCount safely increments the publish counter
|
||||
func (a *Adapter) incrementPublishCount() {
|
||||
a.metricsLock.Lock()
|
||||
a.publishCount++
|
||||
a.metricsLock.Unlock()
|
||||
}
|
||||
|
||||
// incrementJoinCount safely increments the join counter
|
||||
func (a *Adapter) incrementJoinCount() {
|
||||
a.metricsLock.Lock()
|
||||
a.joinCount++
|
||||
a.metricsLock.Unlock()
|
||||
}
|
||||
|
||||
// incrementErrorCount safely increments the error counter
|
||||
func (a *Adapter) incrementErrorCount() {
|
||||
a.metricsLock.Lock()
|
||||
a.errorCount++
|
||||
a.metricsLock.Unlock()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user