Deploy API — Dockerfile, Compose, and Health Endpoints
import "github.com/lookatitude/beluga-ai/deploy"Package deploy provides utilities for packaging and running Beluga AI agents as container workloads. It generates production-ready Dockerfiles and Docker Compose manifests from typed configuration structs, and exposes HTTP handlers for Kubernetes liveness and readiness probes.
GenerateDockerfile
Section titled “GenerateDockerfile”GenerateDockerfile produces a two-stage Dockerfile from DockerfileConfig. The builder stage compiles the agent binary with CGO disabled. The final stage uses a minimal runtime image (distroless by default) and runs as nonroot.
DockerfileConfig
Section titled “DockerfileConfig”type DockerfileConfig struct { BaseImage string // default: "gcr.io/distroless/static-debian12" GoVersion string // default: "1.23"; must match ^[0-9]+\.[0-9]+(\.[0-9]+)?$ AgentConfig string // path to agent config file; required; no path traversal Port int // required; must be in [1, 65535]}All fields are validated before the Dockerfile is rendered. GoVersion and BaseImage are checked against allowlists to prevent Dockerfile instruction injection. AgentConfig is cleaned with filepath.Clean and rejected if it resolves outside the build context or is absolute.
import ( "fmt"
"github.com/lookatitude/beluga-ai/deploy")
dockerfile, err := deploy.GenerateDockerfile(deploy.DockerfileConfig{ GoVersion: "1.23", BaseImage: "gcr.io/distroless/static-debian12", AgentConfig: "config/agent.yaml", Port: 8080,})if err != nil { fmt.Println("error:", err)} else { fmt.Print(dockerfile)}The generated Dockerfile:
# syntax=docker/dockerfile:1FROM golang:1.23 AS builderWORKDIR /srcCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags='-s -w' -o /agent ./cmd/agent
FROM gcr.io/distroless/static-debian12WORKDIR /appCOPY --from=builder /agent /app/agentCOPY config/agent.yaml /config/USER nonroot:nonrootEXPOSE 8080ENTRYPOINT ["/app/agent"]GenerateCompose
Section titled “GenerateCompose”GenerateCompose produces a Docker Compose v3.9 YAML manifest from ComposeConfig. Each AgentDeployment becomes a service with a read-only config volume, a port mapping to container port 8080, optional environment variables, and optional depends_on declarations.
ComposeConfig and AgentDeployment
Section titled “ComposeConfig and AgentDeployment”type ComposeConfig struct { Agents []AgentDeployment // at least one required}
type AgentDeployment struct { Name string // required; must match ^[a-zA-Z0-9_-]+$ ConfigPath string // required; no path traversal; bind-mounted at /config/ Port int // required; host port mapped to container 8080 DependsOn []string // must reference names declared in Agents Environment map[string]string // keys must be valid POSIX variable names}All service names, config paths, environment keys, and DependsOn references are validated before rendering. Newlines are rejected in all string fields to prevent YAML injection.
import ( "fmt"
"github.com/lookatitude/beluga-ai/deploy")
yaml, err := deploy.GenerateCompose(deploy.ComposeConfig{ Agents: []deploy.AgentDeployment{ { Name: "planner", ConfigPath: "config/planner.yaml", Port: 8080, Environment: map[string]string{ "OPENAI_API_KEY": "${OPENAI_API_KEY}", }, }, { Name: "executor", ConfigPath: "config/executor.yaml", Port: 8081, DependsOn: []string{"planner"}, }, },})if err != nil { fmt.Println("error:", err)} else { fmt.Print(yaml)}DependsOn references are validated against the set of declared service names; referencing an undeclared service returns an error.
HealthEndpoint
Section titled “HealthEndpoint”HealthEndpoint exposes HTTP handlers for Kubernetes liveness and readiness probes. Health endpoints are intentionally unauthenticated — restrict network access via infrastructure controls such as NetworkPolicy rather than application-layer authentication.
import ( "context" "fmt" "net/http"
"github.com/lookatitude/beluga-ai/deploy")
h := deploy.NewHealthEndpoint()
// Register a readiness check (e.g. database reachability).h.AddCheck("db", func(ctx context.Context) error { return pingDatabase(ctx)})
mux := http.NewServeMux()mux.HandleFunc("/healthz", h.Healthz())mux.HandleFunc("/readyz", h.Readyz())
if err := http.ListenAndServe(":8081", mux); err != nil { fmt.Println("server error:", err)}Healthz
Section titled “Healthz”Healthz returns an http.HandlerFunc that always responds 200 OK with
{"status":"ok"}. It confirms the process is alive. Only GET and HEAD are
accepted; all other methods receive 405.
Readyz
Section titled “Readyz”Readyz returns an http.HandlerFunc that runs all registered checks with a 5-second per-check deadline derived from the request context. The response is:
- 200 OK — all checks passed; body includes per-check status.
- 503 Service Unavailable — one or more checks failed; failing check names are listed but error details are suppressed to prevent information disclosure to unauthenticated callers.
Only GET and HEAD are accepted; all other methods receive 405.
// 200 response when all checks pass:{"status":"ok","checks":{"db":{"status":"ok"}}}
// 503 response when a check fails:{"status":"fail","checks":{"db":{"status":"unhealthy"}}}AddCheck is safe to call after the server has started. Checks are executed
in registration order. Log check errors internally via your observability
stack; do not surface them in the HTTP response.
Related Packages
Section titled “Related Packages”k8s— Kubernetes operator that generates Deployment, Service, and HPA manifests from Agent and Team CRDs.server— HTTP adapters (gin, fiber, echo, chi, gRPC) that mount the health handlers on a shared mux.o11y— OpenTelemetry integration; instrument health check latency with OTel metrics.config— Load and validate agent configuration files referenced by AgentConfig and ConfigPath.