package ucxi import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "strings" "sync" "time" "chorus.services/bzzz/pkg/ucxl" ) // Server represents a UCXI HTTP server for UCXL operations type Server struct { // HTTP server configuration server *http.Server port int basePath string // Address resolution resolver AddressResolver // Content storage storage ContentStorage // Temporal navigation navigators map[string]*ucxl.TemporalNavigator navMutex sync.RWMutex // Server state running bool ctx context.Context cancel context.CancelFunc // Middleware and logging logger Logger } // AddressResolver interface for resolving UCXL addresses to actual content type AddressResolver interface { Resolve(ctx context.Context, addr *ucxl.Address) (*ResolvedContent, error) Announce(ctx context.Context, addr *ucxl.Address, content *Content) error Discover(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error) } // ContentStorage interface for storing and retrieving content type ContentStorage interface { Store(ctx context.Context, key string, content *Content) error Retrieve(ctx context.Context, key string) (*Content, error) Delete(ctx context.Context, key string) error List(ctx context.Context, prefix string) ([]string, error) } // Logger interface for server logging type Logger interface { Info(msg string, fields ...interface{}) Warn(msg string, fields ...interface{}) Error(msg string, fields ...interface{}) Debug(msg string, fields ...interface{}) } // Content represents content stored at a UCXL address type Content struct { Data []byte `json:"data"` ContentType string `json:"content_type"` Metadata map[string]string `json:"metadata"` Version int `json:"version"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` Author string `json:"author,omitempty"` Checksum string `json:"checksum,omitempty"` } // ResolvedContent represents content resolved from a UCXL address type ResolvedContent struct { Address *ucxl.Address `json:"address"` Content *Content `json:"content"` Source string `json:"source"` // Source node/peer ID Resolved time.Time `json:"resolved"` // Resolution timestamp TTL time.Duration `json:"ttl"` // Time to live for caching } // Response represents a standardized UCXI response type Response struct { Success bool `json:"success"` Data interface{} `json:"data,omitempty"` Error string `json:"error,omitempty"` Timestamp time.Time `json:"timestamp"` RequestID string `json:"request_id,omitempty"` Version string `json:"version"` } // ErrorResponse represents an error response type ErrorResponse struct { Code int `json:"code"` Message string `json:"message"` Details string `json:"details,omitempty"` } // ServerConfig holds server configuration type ServerConfig struct { Port int `json:"port"` BasePath string `json:"base_path"` Resolver AddressResolver `json:"-"` Storage ContentStorage `json:"-"` Logger Logger `json:"-"` } // NewServer creates a new UCXI server func NewServer(config ServerConfig) *Server { ctx, cancel := context.WithCancel(context.Background()) return &Server{ port: config.Port, basePath: strings.TrimSuffix(config.BasePath, "/"), resolver: config.Resolver, storage: config.Storage, logger: config.Logger, navigators: make(map[string]*ucxl.TemporalNavigator), ctx: ctx, cancel: cancel, } } // Start starts the UCXI HTTP server func (s *Server) Start() error { if s.running { return fmt.Errorf("server is already running") } mux := http.NewServeMux() // Register routes s.registerRoutes(mux) s.server = &http.Server{ Addr: fmt.Sprintf(":%d", s.port), Handler: s.withMiddleware(mux), ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } s.running = true s.logger.Info("Starting UCXI server", "port", s.port, "base_path", s.basePath) return s.server.ListenAndServe() } // Stop stops the UCXI HTTP server func (s *Server) Stop() error { if !s.running { return nil } s.logger.Info("Stopping UCXI server") s.cancel() s.running = false ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() return s.server.Shutdown(ctx) } // registerRoutes registers all UCXI HTTP routes func (s *Server) registerRoutes(mux *http.ServeMux) { prefix := s.basePath + "/ucxi/v1" // Content operations mux.HandleFunc(prefix+"/get", s.handleGet) mux.HandleFunc(prefix+"/put", s.handlePut) mux.HandleFunc(prefix+"/post", s.handlePost) mux.HandleFunc(prefix+"/delete", s.handleDelete) // Discovery and announcement mux.HandleFunc(prefix+"/announce", s.handleAnnounce) mux.HandleFunc(prefix+"/discover", s.handleDiscover) // Temporal navigation mux.HandleFunc(prefix+"/navigate", s.handleNavigate) // Server status and health mux.HandleFunc(prefix+"/health", s.handleHealth) mux.HandleFunc(prefix+"/status", s.handleStatus) } // handleGet handles GET requests for retrieving content func (s *Server) handleGet(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } addressStr := r.URL.Query().Get("address") if addressStr == "" { s.writeErrorResponse(w, http.StatusBadRequest, "Missing address parameter", "") return } addr, err := ucxl.Parse(addressStr) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid UCXL address", err.Error()) return } // Resolve the address resolved, err := s.resolver.Resolve(r.Context(), addr) if err != nil { s.writeErrorResponse(w, http.StatusNotFound, "Failed to resolve address", err.Error()) return } s.writeSuccessResponse(w, resolved) } // handlePut handles PUT requests for storing content func (s *Server) handlePut(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPut { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } addressStr := r.URL.Query().Get("address") if addressStr == "" { s.writeErrorResponse(w, http.StatusBadRequest, "Missing address parameter", "") return } addr, err := ucxl.Parse(addressStr) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid UCXL address", err.Error()) return } // Read content from request body body, err := io.ReadAll(r.Body) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Failed to read request body", err.Error()) return } content := &Content{ Data: body, ContentType: r.Header.Get("Content-Type"), Metadata: make(map[string]string), CreatedAt: time.Now(), UpdatedAt: time.Now(), Author: r.Header.Get("X-Author"), } // Copy custom metadata from headers for key, values := range r.Header { if strings.HasPrefix(key, "X-Meta-") { metaKey := strings.TrimPrefix(key, "X-Meta-") if len(values) > 0 { content.Metadata[metaKey] = values[0] } } } // Store the content key := s.generateStorageKey(addr) if err := s.storage.Store(r.Context(), key, content); err != nil { s.writeErrorResponse(w, http.StatusInternalServerError, "Failed to store content", err.Error()) return } // Announce the content if err := s.resolver.Announce(r.Context(), addr, content); err != nil { s.logger.Warn("Failed to announce content", "error", err.Error(), "address", addr.String()) // Don't fail the request if announcement fails } response := map[string]interface{}{ "address": addr.String(), "key": key, "stored": true, } s.writeSuccessResponse(w, response) } // handlePost handles POST requests for updating content func (s *Server) handlePost(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } // POST is similar to PUT but may have different semantics // For now, delegate to PUT handler s.handlePut(w, r) } // handleDelete handles DELETE requests for removing content func (s *Server) handleDelete(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodDelete { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } addressStr := r.URL.Query().Get("address") if addressStr == "" { s.writeErrorResponse(w, http.StatusBadRequest, "Missing address parameter", "") return } addr, err := ucxl.Parse(addressStr) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid UCXL address", err.Error()) return } key := s.generateStorageKey(addr) if err := s.storage.Delete(r.Context(), key); err != nil { s.writeErrorResponse(w, http.StatusInternalServerError, "Failed to delete content", err.Error()) return } response := map[string]interface{}{ "address": addr.String(), "key": key, "deleted": true, } s.writeSuccessResponse(w, response) } // handleAnnounce handles content announcement requests func (s *Server) handleAnnounce(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } var request struct { Address string `json:"address"` Content Content `json:"content"` } if err := json.NewDecoder(r.Body).Decode(&request); err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid JSON request", err.Error()) return } addr, err := ucxl.Parse(request.Address) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid UCXL address", err.Error()) return } if err := s.resolver.Announce(r.Context(), addr, &request.Content); err != nil { s.writeErrorResponse(w, http.StatusInternalServerError, "Failed to announce content", err.Error()) return } response := map[string]interface{}{ "address": addr.String(), "announced": true, } s.writeSuccessResponse(w, response) } // handleDiscover handles content discovery requests func (s *Server) handleDiscover(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } pattern := r.URL.Query().Get("pattern") if pattern == "" { s.writeErrorResponse(w, http.StatusBadRequest, "Missing pattern parameter", "") return } addr, err := ucxl.Parse(pattern) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid UCXL pattern", err.Error()) return } results, err := s.resolver.Discover(r.Context(), addr) if err != nil { s.writeErrorResponse(w, http.StatusInternalServerError, "Discovery failed", err.Error()) return } s.writeSuccessResponse(w, results) } // handleNavigate handles temporal navigation requests func (s *Server) handleNavigate(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } var request struct { Address string `json:"address"` TemporalSegment string `json:"temporal_segment"` } if err := json.NewDecoder(r.Body).Decode(&request); err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid JSON request", err.Error()) return } addr, err := ucxl.Parse(request.Address) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid UCXL address", err.Error()) return } // Get or create navigator for this address context navKey := s.generateNavigatorKey(addr) navigator := s.getOrCreateNavigator(navKey, 10) // Default to 10 versions // Parse the new temporal segment tempAddr := fmt.Sprintf("ucxl://temp:temp@temp:temp/%s", request.TemporalSegment) tempParsed, err := ucxl.Parse(tempAddr) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Invalid temporal segment", err.Error()) return } // Perform navigation result, err := navigator.Navigate(tempParsed.TemporalSegment) if err != nil { s.writeErrorResponse(w, http.StatusBadRequest, "Navigation failed", err.Error()) return } s.writeSuccessResponse(w, result) } // handleHealth handles health check requests func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } health := map[string]interface{}{ "status": "healthy", "running": s.running, "uptime": time.Now().UTC(), } s.writeSuccessResponse(w, health) } // handleStatus handles server status requests func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { s.writeErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed", "") return } s.navMutex.RLock() navigatorCount := len(s.navigators) s.navMutex.RUnlock() status := map[string]interface{}{ "server": map[string]interface{}{ "port": s.port, "base_path": s.basePath, "running": s.running, }, "navigators": map[string]interface{}{ "active_count": navigatorCount, }, "version": "1.0.0", } s.writeSuccessResponse(w, status) } // Utility methods // generateStorageKey generates a storage key from a UCXL address func (s *Server) generateStorageKey(addr *ucxl.Address) string { return fmt.Sprintf("%s:%s@%s:%s/%s", addr.Agent, addr.Role, addr.Project, addr.Task, addr.TemporalSegment.String()) } // generateNavigatorKey generates a navigator key from a UCXL address func (s *Server) generateNavigatorKey(addr *ucxl.Address) string { return fmt.Sprintf("%s:%s@%s:%s", addr.Agent, addr.Role, addr.Project, addr.Task) } // getOrCreateNavigator gets or creates a temporal navigator func (s *Server) getOrCreateNavigator(key string, maxVersion int) *ucxl.TemporalNavigator { s.navMutex.Lock() defer s.navMutex.Unlock() if navigator, exists := s.navigators[key]; exists { return navigator } navigator := ucxl.NewTemporalNavigator(maxVersion) s.navigators[key] = navigator return navigator } // withMiddleware wraps the handler with common middleware func (s *Server) withMiddleware(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Add CORS headers w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Author, X-Meta-*") // Handle preflight requests if r.Method == http.MethodOptions { w.WriteHeader(http.StatusOK) return } // Set content type to JSON by default w.Header().Set("Content-Type", "application/json") // Log request start := time.Now() s.logger.Debug("Request", "method", r.Method, "url", r.URL.String(), "remote", r.RemoteAddr) // Call the handler handler.ServeHTTP(w, r) // Log response duration := time.Since(start) s.logger.Debug("Response", "duration", duration.String()) }) } // writeSuccessResponse writes a successful JSON response func (s *Server) writeSuccessResponse(w http.ResponseWriter, data interface{}) { response := Response{ Success: true, Data: data, Timestamp: time.Now().UTC(), Version: "1.0.0", } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(response) } // writeErrorResponse writes an error JSON response func (s *Server) writeErrorResponse(w http.ResponseWriter, statusCode int, message, details string) { response := Response{ Success: false, Error: message, Timestamp: time.Now().UTC(), Version: "1.0.0", } if details != "" { response.Data = map[string]string{"details": details} } w.WriteHeader(statusCode) json.NewEncoder(w).Encode(response) } // Simple logger implementation type SimpleLogger struct{} func (l SimpleLogger) Info(msg string, fields ...interface{}) { log.Printf("INFO: %s %v", msg, fields) } func (l SimpleLogger) Warn(msg string, fields ...interface{}) { log.Printf("WARN: %s %v", msg, fields) } func (l SimpleLogger) Error(msg string, fields ...interface{}) { log.Printf("ERROR: %s %v", msg, fields) } func (l SimpleLogger) Debug(msg string, fields ...interface{}) { log.Printf("DEBUG: %s %v", msg, fields) }