// 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(50 * 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: 55 * 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") }