// Command llmsproxy is a standalone gateway that exposes multiple upstream LLM // sources behind a unified OpenAI-compatible HTTP API. Protocol differences are // handled by Lua adapters (per source), optionally signing outgoing requests. // It supports explicit model selection or AUTO routing by priority, image // generation, multimodal payloads, a web UI for adapter/source management, and // concurrent/queued scheduling with backoff and failover. package main import ( "flag" "log" "net/http" "os" "os/signal" "strings" "syscall" "time" "llmsproxy/internal/config" "llmsproxy/internal/core" "llmsproxy/internal/gateway" ) func main() { cfgPath := flag.String("config", "config.yaml", "path to gateway config file") checkOnly := flag.Bool("check", false, "validate the config file and exit (0 = valid, 1 = invalid); nothing is started and no file is written") flag.Parse() // -check is the deploy-time preflight: parse and validate the config // without starting the Lua VM, touching runtime.json, or binding a port. // It deliberately does NOT call EnsureDefault, so a missing file is an // error here instead of being silently created. if *checkOnly { if _, err := os.Stat(*cfgPath); err != nil { log.Fatalf("[llmsproxy] check: %v", err) } if _, err := config.Load(*cfgPath); err != nil { log.Fatalf("[llmsproxy] check: %v", err) } log.Printf("[llmsproxy] check: %s is valid", *cfgPath) return } created, err := config.EnsureDefault(*cfgPath) if err != nil { log.Fatalf("[llmsproxy] config: %v", err) } c, err := core.New(*cfgPath) if err != nil { log.Fatalf("[llmsproxy] core: %v", err) } defer c.Close() if created { for _, k := range c.GatewayKeys() { log.Printf("[llmsproxy] generated default config at %s — admin key: %s (rotate it in the WebUI after first login)", *cfgPath, k) } } gw, err := gateway.New(c, c.GatewayKeys()) if err != nil { log.Fatalf("[llmsproxy] gateway: %v", err) } // Ops hygiene: surface the two most common footguns instead of silently // running with them. if keys := c.GatewayKeys(); len(keys) == 0 { log.Printf("[llmsproxy] WARNING: gateway_keys is EMPTY — without a key every request is rejected") } else { for _, k := range keys { if k == "sk-gw-local-0001" || k == "sk-local-0001" { log.Printf("[llmsproxy] WARNING: gateway key %q looks like the starter/example key — rotate it before exposing the gateway", k) } } } if l := c.Listen(); strings.HasPrefix(l, "0.0.0.0:") || strings.HasPrefix(l, "::") { log.Printf("[llmsproxy] WARNING: listen=%s binds ALL interfaces — bind an internal address in production", l) } srv := &http.Server{ Addr: c.Listen(), Handler: gw.Handler(), ReadHeaderTimeout: 10 * time.Second, } go func() { cert, key := c.TLS() if cert != "" && key != "" { log.Printf("[llmsproxy] listening HTTPS on %s (cert=%q) (default_model=%s, models=%v, adapters=%d)", c.Listen(), cert, c.DefaultModel(), c.Registry().ModelList(), len(c.ListAdapters())) if err := srv.ListenAndServeTLS(cert, key); err != nil && err != http.ErrServerClosed { log.Fatalf("[llmsproxy] server: %v", err) } return } log.Printf("[llmsproxy] listening HTTP on %s (default_model=%s, models=%v, adapters=%d)", c.Listen(), c.DefaultModel(), c.Registry().ModelList(), len(c.ListAdapters())) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("[llmsproxy] server: %v", err) } }() stop := make(chan os.Signal, 1) signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) <-stop log.Printf("[llmsproxy] shutting down") }