# BZZZ Built-in Web Configuration UI Development Plan

## Clear Separation of Responsibilities

### chorus.services (Installer Host)
- ✅ **Already Complete**: Hosts `install-chorus-enhanced.sh` 
- ✅ **Single Purpose**: One-line installation script download
- ✅ **Working**: `curl -fsSL https://chorus.services/install.sh | sh`

### BZZZ (This Plan)
- 🔄 **To Implement**: Built-in web UI for cluster configuration
- 🎯 **Purpose**: Post-installation setup wizard when no config exists
- 🌐 **Location**: Served by BZZZ itself at `:8080/setup`

## Architecture Overview

The BZZZ binary will serve its own web UI for configuration when:
1. No configuration file exists (first run)
2. User explicitly accesses `:8080/setup`
3. Configuration is incomplete or invalid

## Implementation Strategy

### Phase 1: Integrate Web UI into BZZZ Binary

#### 1.1 Embed Web UI in Go Binary
**File**: `main.go` and new `pkg/webui/`

```go
//go:embed web-ui/dist/*
var webUIFiles embed.FS

func main() {
    // Check if configuration exists
    if !configExists() {
        log.Println("🌐 No configuration found, starting setup wizard...")
        startWebUI()
    }
    
    // Normal BZZZ startup
    startBZZZServices()
}

func startWebUI() {
    // Serve embedded web UI
    // Start configuration wizard
    // Block until configuration is complete
}
```

#### 1.2 Extend Existing HTTP Server
**File**: `api/http_server.go`

Add setup routes to existing server:
```go
func (h *HTTPServer) setupRoutes(router *mux.Router) {
    // Existing API routes
    api := router.PathPrefix("/api").Subrouter()
    // ... existing routes ...
    
    // Setup wizard routes (only if no config exists)
    if !h.hasValidConfig() {
        h.setupWizardRoutes(router)
    }
}

func (h *HTTPServer) setupWizardRoutes(router *mux.Router) {
    // Serve static web UI files
    router.PathPrefix("/setup").Handler(http.StripPrefix("/setup", 
        http.FileServer(http.FS(webUIFiles))))
    
    // Setup API endpoints
    setup := router.PathPrefix("/api/setup").Subrouter()
    setup.HandleFunc("/system", h.handleSystemDetection).Methods("GET")
    setup.HandleFunc("/repository", h.handleRepositoryConfig).Methods("GET", "POST")
    setup.HandleFunc("/cluster", h.handleClusterConfig).Methods("GET", "POST")
    setup.HandleFunc("/validate", h.handleValidateConfig).Methods("POST")
    setup.HandleFunc("/save", h.handleSaveConfig).Methods("POST")
}
```

### Phase 2: Configuration Management Integration

#### 2.1 Leverage Existing Config System
**File**: `pkg/config/setup.go` (new)

```go
type SetupManager struct {
    configPath     string
    systemInfo     *SystemInfo
    repoFactory    *repository.DefaultProviderFactory
    taskCoordinator *coordinator.TaskCoordinator
}

func (s *SetupManager) DetectSystem() (*SystemInfo, error) {
    // Hardware detection (GPU, CPU, memory)
    // Network interface discovery
    // Software prerequisites check
    return systemInfo, nil
}

func (s *SetupManager) ValidateRepositoryConfig(config RepositoryConfig) error {
    // Use existing repository factory
    provider, err := s.repoFactory.CreateProvider(ctx, &repository.Config{
        Provider:    config.Provider,
        BaseURL:     config.BaseURL,
        AccessToken: config.Token,
        // ...
    })
    
    // Test connectivity
    return provider.ListAvailableTasks()
}

func (s *SetupManager) SaveConfiguration(config *SetupConfig) error {
    // Convert to existing config.Config format
    bzzzConfig := &config.Config{
        Node:       config.NodeConfig,
        Repository: config.RepositoryConfig,
        Agent:      config.AgentConfig,
        // ...
    }
    
    // Save to YAML file
    return bzzzConfig.SaveToFile(s.configPath)
}
```

#### 2.2 Repository Integration
**File**: `api/setup_repository.go` (new)

```go
func (h *HTTPServer) handleRepositoryConfig(w http.ResponseWriter, r *http.Request) {
    if r.Method == "GET" {
        // Return current repository configuration
        config := h.setupManager.GetRepositoryConfig()
        json.NewEncoder(w).Encode(config)
        return
    }
    
    // POST: Validate and save repository configuration
    var repoConfig RepositoryConfig
    if err := json.NewDecoder(r.Body).Decode(&repoConfig); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    
    // Validate using existing repository factory
    if err := h.setupManager.ValidateRepositoryConfig(repoConfig); err != nil {
        http.Error(w, fmt.Sprintf("Repository validation failed: %v", err), http.StatusBadRequest)
        return
    }
    
    // Save configuration
    h.setupManager.SaveRepositoryConfig(repoConfig)
    json.NewEncoder(w).Encode(map[string]string{"status": "success"})
}
```

### Phase 3: Enhanced Web UI Components

#### 3.1 Complete Existing Components
The config-ui framework exists but needs implementation:

**SystemDetection.tsx**:
```typescript
const SystemDetection = ({ onComplete, onBack }) => {
    const [systemInfo, setSystemInfo] = useState(null);
    const [loading, setLoading] = useState(true);
    
    useEffect(() => {
        fetch('/api/setup/system')
            .then(res => res.json())
            .then(data => {
                setSystemInfo(data);
                setLoading(false);
            });
    }, []);
    
    return (
        <div>
            <h3>System Detection Results</h3>
            {systemInfo && (
                <div>
                    <div>OS: {systemInfo.os}</div>
                    <div>Architecture: {systemInfo.arch}</div>
                    <div>GPUs: {systemInfo.gpus?.length || 0}</div>
                    <div>Memory: {systemInfo.memory}</div>
                    {systemInfo.gpus?.length > 1 && (
                        <div className="alert alert-info">
                            💡 Multi-GPU detected! Consider Parallama for optimal performance.
                        </div>
                    )}
                </div>
            )}
            <button onClick={() => onComplete(systemInfo)}>Continue</button>
        </div>
    );
};
```

**RepositoryConfiguration.tsx**:
```typescript
const RepositoryConfiguration = ({ onComplete, onBack, configData }) => {
    const [provider, setProvider] = useState('gitea');
    const [config, setConfig] = useState({
        provider: 'gitea',
        baseURL: 'http://ironwood:3000',
        owner: '',
        repository: '',
        token: ''
    });
    const [validating, setValidating] = useState(false);
    
    const validateConfig = async () => {
        setValidating(true);
        try {
            const response = await fetch('/api/setup/repository', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(config)
            });
            
            if (response.ok) {
                onComplete(config);
            } else {
                const error = await response.text();
                alert(`Validation failed: ${error}`);
            }
        } catch (error) {
            alert(`Connection failed: ${error.message}`);
        }
        setValidating(false);
    };
    
    return (
        <div>
            <h3>Repository Configuration</h3>
            <form onSubmit={(e) => { e.preventDefault(); validateConfig(); }}>
                <div>
                    <label>Provider:</label>
                    <select value={provider} onChange={(e) => setProvider(e.target.value)}>
                        <option value="gitea">GITEA (Self-hosted)</option>
                        <option value="github">GitHub</option>
                    </select>
                </div>
                
                {provider === 'gitea' && (
                    <div>
                        <label>GITEA URL:</label>
                        <input 
                            value={config.baseURL} 
                            onChange={(e) => setConfig({...config, baseURL: e.target.value})}
                            placeholder="http://ironwood:3000"
                        />
                    </div>
                )}
                
                <div>
                    <label>Repository Owner:</label>
                    <input 
                        value={config.owner} 
                        onChange={(e) => setConfig({...config, owner: e.target.value})}
                        placeholder="username or organization"
                    />
                </div>
                
                <div>
                    <label>Repository Name:</label>
                    <input 
                        value={config.repository} 
                        onChange={(e) => setConfig({...config, repository: e.target.value})}
                        placeholder="repository name"
                    />
                </div>
                
                <div>
                    <label>Access Token:</label>
                    <input 
                        type="password"
                        value={config.token} 
                        onChange={(e) => setConfig({...config, token: e.target.value})}
                        placeholder="access token"
                    />
                </div>
                
                <div>
                    <button type="button" onClick={onBack}>Back</button>
                    <button type="submit" disabled={validating}>
                        {validating ? 'Validating...' : 'Validate & Continue'}
                    </button>
                </div>
            </form>
        </div>
    );
};
```

### Phase 4: Build Process Integration

#### 4.1 Web UI Build Integration
**File**: `Makefile` or build script

```bash
build-web-ui:
	cd install/config-ui && npm install && npm run build
	mkdir -p web-ui/dist
	cp -r install/config-ui/.next/static web-ui/dist/
	cp -r install/config-ui/out/* web-ui/dist/ 2>/dev/null || true

build-bzzz: build-web-ui
	go build -o bzzz main.go
```

#### 4.2 Embed Files in Binary
**File**: `pkg/webui/embed.go`

```go
package webui

import (
    "embed"
    "io/fs"
    "net/http"
)

//go:embed dist/*
var webUIFiles embed.FS

func GetWebUIHandler() http.Handler {
    // Get the web-ui subdirectory
    webUIFS, err := fs.Sub(webUIFiles, "dist")
    if err != nil {
        panic(err)
    }
    
    return http.FileServer(http.FS(webUIFS))
}
```

### Phase 5: Configuration Flow Integration

#### 5.1 Startup Logic
**File**: `main.go`

```go
func main() {
    configPath := getConfigPath()
    
    // Check if configuration exists and is valid
    config, err := loadConfig(configPath)
    if err != nil || !isValidConfig(config) {
        fmt.Println("🌐 Starting BZZZ setup wizard...")
        fmt.Printf("📱 Open your browser to: http://localhost:8080/setup\n")
        
        // Start HTTP server in setup mode
        startSetupMode(configPath)
        
        // Wait for configuration to be saved
        waitForConfiguration(configPath)
        
        // Reload configuration
        config, err = loadConfig(configPath)
        if err != nil {
            log.Fatal("Failed to load configuration after setup:", err)
        }
    }
    
    // Normal BZZZ startup with configuration
    fmt.Println("🚀 Starting BZZZ with configuration...")
    startNormalMode(config)
}

func startSetupMode(configPath string) {
    // Start HTTP server in setup mode
    setupManager := NewSetupManager(configPath)
    httpServer := api.NewHTTPServer(8080, nil, nil)
    httpServer.SetSetupManager(setupManager)
    
    go func() {
        if err := httpServer.Start(); err != nil {
            log.Fatal("Failed to start setup server:", err)
        }
    }()
}

func waitForConfiguration(configPath string) {
    ticker := time.NewTicker(1 * time.Second)
    defer ticker.Stop()
    
    for {
        select {
        case <-ticker.C:
            if fileExists(configPath) {
                if config, err := loadConfig(configPath); err == nil && isValidConfig(config) {
                    fmt.Println("✅ Configuration saved successfully!")
                    return
                }
            }
        case <-time.After(30 * time.Minute):
            log.Fatal("Setup timeout - no configuration provided within 30 minutes")
        }
    }
}
```

#### 5.2 Configuration Validation
**File**: `pkg/config/validation.go`

```go
func isValidConfig(config *Config) bool {
    // Check required fields
    if config.Node.ID == "" || config.Node.Role == "" {
        return false
    }
    
    // Check repository configuration
    if config.Repository.Provider == "" || config.Repository.Config.Owner == "" {
        return false
    }
    
    // Check agent configuration
    if config.Agent.ID == "" || len(config.Agent.Expertise) == 0 {
        return false
    }
    
    return true
}
```

## Integration with Existing Systems

### ✅ **Leverage Without Changes**
1. **HTTP Server** (`api/http_server.go`) - Extend with setup routes
2. **Configuration System** (`pkg/config/`) - Use existing YAML structure
3. **Repository Factory** (`repository/factory.go`) - Validate credentials
4. **Task Coordinator** (`coordinator/task_coordinator.go`) - Agent management

### 🔧 **Minimal Extensions**
1. **Setup Manager** - New component using existing systems
2. **Web UI Routes** - Add to existing HTTP server
3. **Embedded Files** - Add web UI to binary
4. **Startup Logic** - Add configuration check to main.go

## File Structure

```
/home/tony/chorus/project-queues/active/BZZZ/
├── main.go                              # Enhanced startup logic
├── api/
│   ├── http_server.go                   # Extended with setup routes
│   ├── setup_handlers.go                # New setup API handlers
│   └── setup_repository.go              # Repository configuration API
├── pkg/
│   ├── config/
│   │   ├── config.go                    # Existing
│   │   ├── setup.go                     # New setup manager
│   │   └── validation.go                # New validation logic
│   └── webui/
│       └── embed.go                     # New embedded web UI
├── install/config-ui/                   # Existing React app
│   ├── app/setup/page.tsx               # Existing wizard framework
│   ├── app/setup/components/            # Complete component implementations
│   └── package.json                     # Existing
└── web-ui/                              # Build output (embedded)
    └── dist/                            # Generated by build process
```

## Implementation Timeline

### Week 1: Core Integration
- **Day 1**: Setup manager and configuration validation
- **Day 2**: Extend HTTP server with setup routes
- **Day 3**: Repository configuration API integration
- **Day 4**: System detection and hardware profiling
- **Day 5**: Build process and file embedding

### Week 2: Web UI Implementation
- **Day 1-2**: Complete SystemDetection and RepositoryConfiguration components
- **Day 3-4**: Cluster formation and validation components
- **Day 5**: UI polish and error handling

### Week 3: Integration and Testing
- **Day 1-2**: End-to-end configuration flow testing
- **Day 3-4**: Multi-node cluster testing
- **Day 5**: Documentation and deployment

## Success Criteria

### Technical
- [ ] BZZZ binary serves web UI when no config exists
- [ ] Complete 8-step setup wizard functional
- [ ] Repository integration with GITEA/GitHub validation
- [ ] Configuration saves to existing YAML format
- [ ] Seamless transition from setup to normal operation

### User Experience
- [ ] Zero-config startup launches web wizard
- [ ] Intuitive setup flow with validation
- [ ] Professional UI matching BZZZ branding
- [ ] Clear error messages and recovery options
- [ ] Mobile-responsive design

This plan keeps the clear separation between the chorus.services installer (already working) and BZZZ's built-in configuration system (to be implemented).