rdev/cmd/claudebox-sidecar/main.go
jordan 3b35900a2d feat: enterprise worker pool with HTTP sidecar pattern
Implements horizontally-scalable worker pool architecture:
- claudebox-sidecar: HTTP server for Claude Code, git, and SDLC ops
- rdev-worker: standalone worker binary polling rdev-api for tasks
- HTTP client adapter for sidecar communication
- HPA with custom Prometheus metrics for autoscaling
- ServiceMonitor for metrics scraping

Code review fixes applied:
- URL-encode query parameters in GitStatus (Critical #1)
- Remove unused shellQuote function (Critical #2)
- Use stdlib strings.Split/TrimSpace (Critical #3)
- Add version injection via ldflags (Warning #4)
- Add debug logging for swallowed git/sdlc errors (Warning #5, #6)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 16:21:11 -07:00

112 lines
2.8 KiB
Go

// Package main provides the claudebox-sidecar HTTP server.
// This sidecar runs alongside Claude Code in worker pods, exposing HTTP endpoints
// for execute, git, and SDLC operations - replacing kubectl exec calls.
package main
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/orchard9/rdev/internal/claudebox"
"github.com/orchard9/rdev/internal/envutil"
"github.com/orchard9/rdev/internal/logging"
)
func main() {
// Configure logging
logLevel := logging.LevelInfo
if envutil.GetEnvBool("DEBUG", false) {
logLevel = logging.LevelDebug
}
log := logging.New(logging.Config{
Level: logLevel,
Format: logging.FormatJSON,
})
// Configuration from environment
port := envutil.GetEnv("PORT", "8080")
workDir := envutil.GetEnv("WORKSPACE_DIR", "/workspace")
giteaToken := os.Getenv("GITEA_TOKEN") // Required for git push auth
gitUser := envutil.GetEnv("GIT_USER", "rdev-worker")
gitEmail := envutil.GetEnv("GIT_EMAIL", "worker@threesix.ai")
// Create server components
executor := claudebox.NewExecutor(workDir)
gitOps := claudebox.NewGitOperations(claudebox.GitOperationsConfig{
WorkDir: workDir,
GiteaToken: giteaToken,
GitUser: gitUser,
GitEmail: gitEmail,
Logger: log.Slog(),
})
sdlcRunner := claudebox.NewSDLCRunner(claudebox.SDLCRunnerConfig{
WorkDir: workDir,
Logger: log.Slog(),
})
// Create the server
server := claudebox.NewServer(claudebox.ServerConfig{
Executor: executor,
GitOps: gitOps,
SDLCRunner: sdlcRunner,
Logger: log.Slog(),
})
// Create router
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(logging.Middleware(logging.MiddlewareConfig{
Logger: log,
}))
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(10 * time.Minute))
// Mount server routes
server.Mount(r)
// Create HTTP server
addr := fmt.Sprintf(":%s", port)
httpServer := &http.Server{
Addr: addr,
Handler: r,
ReadTimeout: 30 * time.Second,
WriteTimeout: 15 * time.Minute, // Long timeout for streaming responses
IdleTimeout: 60 * time.Second,
}
// Start server in goroutine
go func() {
log.Info("starting claudebox-sidecar", "addr", addr, "workDir", workDir)
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Error("server error", logging.FieldError, err)
os.Exit(1)
}
}()
// Wait for shutdown signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Info("shutting down server")
// Graceful shutdown with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := httpServer.Shutdown(ctx); err != nil {
log.Error("server shutdown error", logging.FieldError, err)
os.Exit(1)
}
log.Info("server stopped")
}